Solved error with not tracked response.completed event where does't get all the deltas
This commit is contained in:
parent
6b1a6bed4d
commit
52321f0f3a
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) {
|
if (data.response_format || data.text?.verbosity) {
|
||||||
const textObj = {};
|
const textObj = {};
|
||||||
if (data.response_format) {
|
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) {
|
if (data.text?.verbosity) {
|
||||||
textObj.verbosity = data.text.verbosity;
|
textObj.verbosity = data.text.verbosity;
|
||||||
}
|
}
|
||||||
codexRequest.text = textObj;
|
if (Object.keys(textObj).length > 0) {
|
||||||
|
codexRequest.text = textObj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在 input 开头注入特殊指令(如果配置允许)
|
// 在 input 开头注入特殊指令(如果配置允许)
|
||||||
|
|
@ -531,9 +536,7 @@ export class CodexConverter extends BaseConverter {
|
||||||
schema: responseFormat.json_schema?.schema || {}
|
schema: responseFormat.json_schema?.schema || {}
|
||||||
};
|
};
|
||||||
} else if (responseFormat.type === 'json_object') {
|
} else if (responseFormat.type === 'json_object') {
|
||||||
return {
|
return null;
|
||||||
type: 'json_object'
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return responseFormat;
|
return responseFormat;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -596,9 +596,13 @@ export class CodexApiService {
|
||||||
parseNonStreamResponse(data) {
|
parseNonStreamResponse(data) {
|
||||||
// 确保 data 是字符串
|
// 确保 data 是字符串
|
||||||
const responseText = typeof data === 'string' ? data : String(data);
|
const responseText = typeof data === 'string' ? data : String(data);
|
||||||
|
|
||||||
// 从 SSE 流中提取 response.completed 事件
|
// 从 SSE 流中提取所有事件,累积 output
|
||||||
const lines = responseText.split('\n');
|
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) {
|
for (const line of lines) {
|
||||||
if (line.startsWith('data: ')) {
|
if (line.startsWith('data: ')) {
|
||||||
const jsonData = line.slice(6).trim();
|
const jsonData = line.slice(6).trim();
|
||||||
|
|
@ -607,8 +611,26 @@ export class CodexApiService {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(jsonData);
|
const parsed = JSON.parse(jsonData);
|
||||||
if (parsed.type === 'response.completed') {
|
switch (parsed.type) {
|
||||||
return parsed;
|
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) {
|
} catch (e) {
|
||||||
// 继续解析下一行
|
// 继续解析下一行
|
||||||
|
|
@ -616,10 +638,47 @@ export class CodexApiService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有找到 response.completed,抛出错误
|
if (!completedEvent) {
|
||||||
logger.error('[Codex] No completed response found in Codex response');
|
logger.error('[Codex] No completed response found in Codex response');
|
||||||
throw new Error('stream error: stream disconnected before completion: stream closed before response.completed');
|
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