forked from boos/BoosAPI
fix responses lifecycle parity (#272)
* fix responses lifecycle parity * remove websocket test debug logging * fix responses proxy stream typecheck * update compact upstream response expectations * add community and deepwiki badges * update linuxdo banner link * fix responses review follow-ups * preserve whitespace in responses terminal deltas * fix responses stream reconciliation edges --------- Co-authored-by: apple <apple@appledeMacBook-Pro.local>
This commit is contained in:
@@ -56,6 +56,12 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === 'object';
|
||||
}
|
||||
|
||||
function isResponsesWebsocketTransportRequest(headers: Record<string, unknown>): boolean {
|
||||
return Object.entries(headers)
|
||||
.some(([rawKey, rawValue]) => rawKey.trim().toLowerCase() === 'x-metapi-responses-websocket-transport'
|
||||
&& String(rawValue).trim() === '1');
|
||||
}
|
||||
|
||||
function normalizeIncludeList(value: unknown): string[] {
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim();
|
||||
@@ -485,12 +491,10 @@ export async function handleOpenAiResponsesSurfaceRequest(
|
||||
const writeLines = (lines: string[]) => {
|
||||
for (const line of lines) reply.raw.write(line);
|
||||
};
|
||||
const websocketTransportRequest = isResponsesWebsocketTransportRequest(request.headers as Record<string, unknown>);
|
||||
const streamSession = openAiResponsesTransformer.proxyStream.createSession({
|
||||
modelName,
|
||||
successfulUpstreamPath,
|
||||
strictTerminalEvents: Object.entries(request.headers as Record<string, unknown>)
|
||||
.some(([rawKey, rawValue]) => rawKey.trim().toLowerCase() === 'x-metapi-responses-websocket-transport'
|
||||
&& String(rawValue).trim() === '1'),
|
||||
getUsage: () => parsedUsage,
|
||||
onParsedPayload: (payload) => {
|
||||
if (payload && typeof payload === 'object') {
|
||||
@@ -608,7 +612,65 @@ export async function handleOpenAiResponsesSurfaceRequest(
|
||||
|
||||
startSseResponse();
|
||||
|
||||
const upstreamReader = upstream.body?.getReader();
|
||||
let replayReader: ReturnType<typeof createSingleChunkStreamReader> | null = null;
|
||||
if (websocketTransportRequest) {
|
||||
const rawText = await upstream.text();
|
||||
if (looksLikeResponsesSseText(rawText)) {
|
||||
try {
|
||||
const collectedPayload = collectResponsesFinalPayloadFromSseText(rawText, modelName).payload;
|
||||
parsedUsage = mergeProxyUsage(parsedUsage, parseProxyUsage(collectedPayload));
|
||||
const createdPayload = {
|
||||
...collectedPayload,
|
||||
status: 'in_progress',
|
||||
output: [],
|
||||
output_text: '',
|
||||
};
|
||||
const terminalEventType = String(collectedPayload.status || '').trim().toLowerCase() === 'incomplete'
|
||||
? 'response.incomplete'
|
||||
: 'response.completed';
|
||||
writeLines([
|
||||
`event: response.created\ndata: ${JSON.stringify({ type: 'response.created', response: createdPayload })}\n\n`,
|
||||
`event: ${terminalEventType}\ndata: ${JSON.stringify({ type: terminalEventType, response: collectedPayload })}\n\n`,
|
||||
'data: [DONE]\n\n',
|
||||
]);
|
||||
reply.raw.end();
|
||||
const latency = Date.now() - startTime;
|
||||
await finalizeStreamSuccess(parsedUsage, latency);
|
||||
return;
|
||||
} catch {
|
||||
// Fall through to the generic stream session for response.failed/error terminals.
|
||||
}
|
||||
|
||||
const streamResult = await streamSession.run(
|
||||
createSingleChunkStreamReader(rawText),
|
||||
reply.raw,
|
||||
);
|
||||
const latency = Date.now() - startTime;
|
||||
if (streamResult.status === 'failed') {
|
||||
await failureToolkit.recordStreamFailure({
|
||||
selected,
|
||||
requestedModel,
|
||||
modelName,
|
||||
errorMessage: streamResult.errorMessage,
|
||||
latencyMs: latency,
|
||||
retryCount,
|
||||
promptTokens: parsedUsage.promptTokens,
|
||||
completionTokens: parsedUsage.completionTokens,
|
||||
totalTokens: parsedUsage.totalTokens,
|
||||
upstreamPath: successfulUpstreamPath,
|
||||
runtimeFailureStatus: 502,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await finalizeStreamSuccess(parsedUsage, latency);
|
||||
return;
|
||||
}
|
||||
|
||||
replayReader = createSingleChunkStreamReader(rawText);
|
||||
}
|
||||
|
||||
const upstreamReader = replayReader ?? upstream.body?.getReader();
|
||||
const baseReader = String(selected.site.platform || '').trim().toLowerCase() === 'gemini-cli' && upstreamReader
|
||||
? createGeminiCliStreamReader(upstreamReader)
|
||||
: upstreamReader;
|
||||
|
||||
Reference in New Issue
Block a user