Merge pull request #460 from nesitor/fix/track-completed-event

Solve error parsing JSON response
This commit is contained in:
何夕2077 2026-04-08 23:44:38 +08:00 committed by GitHub
commit d9a672909f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 13 deletions

View file

@ -226,12 +226,17 @@ export class CodexConverter extends BaseConverter {
if (data.response_format || data.text?.verbosity) {
const textObj = {};
if (data.response_format) {
textObj.format = this.convertResponseFormat(data.response_format);
const converted = this.convertResponseFormat(data.response_format);
if (converted) {
textObj.format = converted;
}
}
if (data.text?.verbosity) {
textObj.verbosity = data.text.verbosity;
}
codexRequest.text = textObj;
if (Object.keys(textObj).length > 0) {
codexRequest.text = textObj;
}
}
// 在 input 开头注入特殊指令(如果配置允许)
@ -531,9 +536,7 @@ export class CodexConverter extends BaseConverter {
schema: responseFormat.json_schema?.schema || {}
};
} else if (responseFormat.type === 'json_object') {
return {
type: 'json_object'
};
return null;
}
return responseFormat;
}

View file

@ -596,9 +596,13 @@ export class CodexApiService {
parseNonStreamResponse(data) {
// 确保 data 是字符串
const responseText = typeof data === 'string' ? data : String(data);
// 从 SSE 流中提取 response.completed 事件
// 从 SSE 流中提取所有事件,累积 output
const lines = responseText.split('\n');
const outputItems = new Map(); // id -> output item
const textDeltas = new Map(); // item_id -> accumulated text
let completedEvent = null;
for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonData = line.slice(6).trim();
@ -607,8 +611,26 @@ export class CodexApiService {
}
try {
const parsed = JSON.parse(jsonData);
if (parsed.type === 'response.completed') {
return parsed;
switch (parsed.type) {
case 'response.output_item.added':
if (parsed.item) {
outputItems.set(parsed.item.id, parsed.item);
}
break;
case 'response.output_text.delta':
if (parsed.item_id && parsed.delta) {
const existing = textDeltas.get(parsed.item_id) || '';
textDeltas.set(parsed.item_id, existing + parsed.delta);
}
break;
case 'response.output_text.done':
if (parsed.item_id && parsed.text) {
textDeltas.set(parsed.item_id, parsed.text);
}
break;
case 'response.completed':
completedEvent = parsed;
break;
}
} catch (e) {
// 继续解析下一行
@ -616,10 +638,47 @@ export class CodexApiService {
}
}
}
// 如果没有找到 response.completed抛出错误
logger.error('[Codex] No completed response found in Codex response');
throw new Error('stream error: stream disconnected before completion: stream closed before response.completed');
if (!completedEvent) {
logger.error('[Codex] No completed response found in Codex response');
throw new Error('stream error: stream disconnected before completion: stream closed before response.completed');
}
// 用累积的 delta 文本填充 output items 中缺失的内容
if (completedEvent.response && textDeltas.size > 0) {
const output = completedEvent.response.output || [];
for (const item of output) {
if (item.type === 'message' && item.role === 'assistant') {
const accumulatedText = textDeltas.get(item.id);
if (accumulatedText !== undefined) {
// content 为空或不含 output_text直接注入
if (!item.content || item.content.length === 0) {
item.content = [{ type: 'output_text', text: accumulatedText }];
} else {
item.content = item.content.map(c => {
if (c.type === 'output_text' && !c.text) {
return { ...c, text: accumulatedText };
}
return c;
});
}
}
}
}
// 如果 output 完全为空,从累积事件重建
if (output.length === 0 && outputItems.size > 0) {
for (const [id, item] of outputItems) {
const accumulatedText = textDeltas.get(id);
if (accumulatedText !== undefined && item.type === 'message') {
item.content = [{ type: 'output_text', text: accumulatedText }];
}
output.push(item);
}
completedEvent.response.output = output;
}
}
return completedEvent;
}
/**