Merge pull request #460 from nesitor/fix/track-completed-event
Solve error parsing JSON response
This commit is contained in:
commit
d9a672909f
2 changed files with 75 additions and 13 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue