fix responses stream failure logging

This commit is contained in:
cita
2026-03-18 18:17:06 +08:00
parent 38e46e165b
commit 332007d4fb
8 changed files with 248 additions and 15 deletions
@@ -13,6 +13,11 @@ type ResponseSink = {
end(): void;
};
type ResponsesProxyStreamResult = {
status: 'completed' | 'failed';
errorMessage: string | null;
};
type ResponsesProxyStreamSessionInput = {
modelName: string;
successfulUpstreamPath: string;
@@ -33,20 +38,58 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === 'object';
}
function getResponsesStreamFailureMessage(payload: unknown, fallback = 'upstream stream failed'): string {
if (isRecord(payload)) {
if (isRecord(payload.error) && typeof payload.error.message === 'string' && payload.error.message.trim()) {
return payload.error.message.trim();
}
if (typeof payload.message === 'string' && payload.message.trim()) {
return payload.message.trim();
}
if (isRecord(payload.response) && isRecord(payload.response.error) && typeof payload.response.error.message === 'string' && payload.response.error.message.trim()) {
return payload.response.error.message.trim();
}
}
return fallback;
}
export function createResponsesProxyStreamSession(input: ResponsesProxyStreamSessionInput) {
const streamContext = openAiResponsesStream.createContext(input.modelName);
const responsesState = createOpenAiResponsesAggregateState(input.modelName);
let finalized = false;
let terminalResult: ResponsesProxyStreamResult = {
status: 'completed',
errorMessage: null,
};
const finalize = () => {
if (finalized) return;
finalized = true;
terminalResult = {
status: 'completed',
errorMessage: null,
};
input.writeLines(completeResponsesStream(responsesState, streamContext, input.getUsage()));
};
const fail = (payload: unknown, fallbackMessage?: string) => {
if (finalized) return;
finalized = true;
terminalResult = {
status: 'failed',
errorMessage: getResponsesStreamFailureMessage(payload, fallbackMessage),
};
input.writeLines(failResponsesStream(responsesState, streamContext, input.getUsage(), payload));
};
const closeOut = () => {
if (finalized) return;
finalize();
};
const handleEventBlock = (eventBlock: ParsedSseEvent): boolean => {
if (eventBlock.data === '[DONE]') {
finalize();
closeOut();
return true;
}
@@ -71,8 +114,7 @@ export function createResponsesProxyStreamSession(input: ResponsesProxyStreamSes
|| payloadType === 'response.failed'
);
if (isFailureEvent) {
input.writeLines(failResponsesStream(responsesState, streamContext, input.getUsage(), parsedPayload));
finalized = true;
fail(parsedPayload);
return true;
}
@@ -97,15 +139,16 @@ export function createResponsesProxyStreamSession(input: ResponsesProxyStreamSes
};
return {
async run(reader: StreamReader | null | undefined, response: ResponseSink) {
async run(reader: StreamReader | null | undefined, response: ResponseSink): Promise<ResponsesProxyStreamResult> {
const lifecycle = createProxyStreamLifecycle<ParsedSseEvent>({
reader,
response,
pullEvents: (buffer) => openAiResponsesStream.pullSseEvents(buffer),
handleEvent: handleEventBlock,
onEof: finalize,
onEof: closeOut,
});
await lifecycle.run();
return terminalResult;
},
};
}