gateway: send terminal SSE chunk for OpenAI chat completions
This commit is contained in:
parent
4583f88626
commit
3a4e791c77
@ -40,6 +40,22 @@ function writeSse(res: ServerResponse, data: unknown) {
|
|||||||
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildFinalChunk(params: { runId: string; model: string }) {
|
||||||
|
return {
|
||||||
|
id: params.runId,
|
||||||
|
object: "chat.completion.chunk",
|
||||||
|
created: Math.floor(Date.now() / 1000),
|
||||||
|
model: params.model,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
delta: {},
|
||||||
|
finish_reason: "stop",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function asMessages(val: unknown): OpenAiChatMessage[] {
|
function asMessages(val: unknown): OpenAiChatMessage[] {
|
||||||
return Array.isArray(val) ? (val as OpenAiChatMessage[]) : [];
|
return Array.isArray(val) ? (val as OpenAiChatMessage[]) : [];
|
||||||
}
|
}
|
||||||
@ -54,7 +70,10 @@ function extractTextContent(content: unknown): string {
|
|||||||
const text = (part as { text?: unknown }).text;
|
const text = (part as { text?: unknown }).text;
|
||||||
const inputText = (part as { input_text?: unknown }).input_text;
|
const inputText = (part as { input_text?: unknown }).input_text;
|
||||||
if (type === "text" && typeof text === "string") return text;
|
if (type === "text" && typeof text === "string") return text;
|
||||||
if (type === "input_text" && typeof text === "string") return text;
|
if (type === "input_text") {
|
||||||
|
if (typeof inputText === "string") return inputText;
|
||||||
|
if (typeof text === "string") return text;
|
||||||
|
}
|
||||||
if (typeof inputText === "string") return inputText;
|
if (typeof inputText === "string") return inputText;
|
||||||
return "";
|
return "";
|
||||||
})
|
})
|
||||||
@ -251,8 +270,22 @@ export async function handleOpenAiHttpRequest(
|
|||||||
|
|
||||||
let wroteRole = false;
|
let wroteRole = false;
|
||||||
let sawAssistantDelta = false;
|
let sawAssistantDelta = false;
|
||||||
|
let sentTerminalChunk = false;
|
||||||
let closed = false;
|
let closed = false;
|
||||||
|
|
||||||
|
const endStream = () => {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
if (!sentTerminalChunk) {
|
||||||
|
sentTerminalChunk = true;
|
||||||
|
// Some OpenAI-compatible clients expect a terminal chunk before [DONE].
|
||||||
|
writeSse(res, buildFinalChunk({ runId, model }));
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
writeDone(res);
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
|
||||||
const unsubscribe = onAgentEvent((evt) => {
|
const unsubscribe = onAgentEvent((evt) => {
|
||||||
if (evt.runId !== runId) return;
|
if (evt.runId !== runId) return;
|
||||||
if (closed) return;
|
if (closed) return;
|
||||||
@ -294,10 +327,7 @@ export async function handleOpenAiHttpRequest(
|
|||||||
if (evt.stream === "lifecycle") {
|
if (evt.stream === "lifecycle") {
|
||||||
const phase = evt.data?.phase;
|
const phase = evt.data?.phase;
|
||||||
if (phase === "end" || phase === "error") {
|
if (phase === "end" || phase === "error") {
|
||||||
closed = true;
|
endStream();
|
||||||
unsubscribe();
|
|
||||||
writeDone(res);
|
|
||||||
res.end();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -383,10 +413,7 @@ export async function handleOpenAiHttpRequest(
|
|||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
closed = true;
|
endStream();
|
||||||
unsubscribe();
|
|
||||||
writeDone(res);
|
|
||||||
res.end();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user