fix(CodexConverter): 修复流式响应中事件与状态不匹配的问题

当 Codex 增量事件缺少 response.id 时,现在会智能选择最近活跃的流状态进行归并,避免使用固定 "default" key 导致状态污染。同时为流状态添加最后更新时间戳,并维护最近响应ID的引用,确保流式转换的准确性和稳定性。
This commit is contained in:
hex2077 2026-03-04 15:26:53 +08:00
parent eec5cb8d81
commit 8456f64615
2 changed files with 31 additions and 4 deletions

View file

@ -1 +1 @@
2.10.5.2
2.10.5.3

View file

@ -1126,8 +1126,29 @@ export class CodexConverter extends BaseConverter {
*/
toClaudeStreamChunk(chunk, model) {
const type = chunk.type;
const resId = chunk.response?.id || 'default';
// Codex 的多数增量事件不带 response.id需要将其归并到最近活跃的流状态。
let resId = chunk.response?.id;
if (!resId) {
if (this.lastClaudeStreamResponseId && this.streamParams.has(this.lastClaudeStreamResponseId)) {
resId = this.lastClaudeStreamResponseId;
} else if (this.streamParams.size === 1) {
resId = this.streamParams.keys().next().value;
} else {
// 兜底:选择最近更新的流,避免落到固定 "default" key 导致串流状态污染。
let latestKey = null;
let latestUpdatedAt = -1;
for (const [key, streamState] of this.streamParams.entries()) {
const updatedAt = streamState?.lastUpdatedAt || 0;
if (updatedAt > latestUpdatedAt) {
latestUpdatedAt = updatedAt;
latestKey = key;
}
}
resId = latestKey || 'default';
}
}
if (!this.streamParams.has(resId)) {
this.streamParams.set(resId, {
model: model,
@ -1135,13 +1156,16 @@ export class CodexConverter extends BaseConverter {
responseID: resId,
blockIndex: 0,
blockStarted: false, // track whether content_block_start has been sent for current block
currentBlockType: null // 'thinking' or 'text'
currentBlockType: null, // 'thinking' or 'text'
lastUpdatedAt: Date.now()
});
}
const state = this.streamParams.get(resId);
state.lastUpdatedAt = Date.now();
if (type === 'response.created') {
state.responseID = chunk.response.id;
this.lastClaudeStreamResponseId = state.responseID;
return {
type: "message_start",
message: {
@ -1262,6 +1286,9 @@ export class CodexConverter extends BaseConverter {
{ type: "message_stop" }
);
this.streamParams.delete(resId);
if (this.lastClaudeStreamResponseId === resId) {
this.lastClaudeStreamResponseId = null;
}
return events;
}