fix: 防止流式响应中重复发送结束标志并添加新模型支持

修复 handleStreamRequest 中可能重复发送流结束标志的问题,新增 hasMessageStop 状态跟踪。
在 provider-pool-manager 中修复临时配置缺少全局配置的问题。
为 Gemini 提供者添加新的模型支持:gemini-claude-opus-4-6-thinking。
This commit is contained in:
hex2077 2026-02-08 15:42:45 +08:00
parent 60b4296097
commit 4d588f6c7d
4 changed files with 41 additions and 8 deletions

View file

@ -66,7 +66,8 @@ const MODEL_ALIAS_MAP = {
'gemini-2.5-flash-preview': 'gemini-2.5-flash',
'gemini-claude-sonnet-4-5': 'claude-sonnet-4-5',
'gemini-claude-sonnet-4-5-thinking': 'claude-sonnet-4-5-thinking',
'gemini-claude-opus-4-5-thinking': 'claude-opus-4-5-thinking'
'gemini-claude-opus-4-5-thinking': 'claude-opus-4-5-thinking',
'gemini-claude-opus-4-6-thinking': 'claude-opus-4-6-thinking'
};
// 真实模型名 -> 别名
@ -78,7 +79,8 @@ const MODEL_NAME_MAP = {
'gemini-2.5-flash': 'gemini-2.5-flash-preview',
'claude-sonnet-4-5': 'gemini-claude-sonnet-4-5',
'claude-sonnet-4-5-thinking': 'gemini-claude-sonnet-4-5-thinking',
'claude-opus-4-5-thinking': 'gemini-claude-opus-4-5-thinking'
'claude-opus-4-5-thinking': 'gemini-claude-opus-4-5-thinking',
'claude-opus-4-6-thinking': 'gemini-claude-opus-4-6-thinking'
};
/**

View file

@ -21,7 +21,8 @@ export const PROVIDER_MODELS = {
'gemini-2.5-flash-preview',
'gemini-claude-sonnet-4-5',
'gemini-claude-sonnet-4-5-thinking',
'gemini-claude-opus-4-5-thinking'
'gemini-claude-opus-4-5-thinking',
'gemini-claude-opus-4-6-thinking'
],
'claude-custom': [],
'claude-kiro-oauth': [

View file

@ -379,6 +379,7 @@ export class ProviderPoolManager {
// 使用适配器进行刷新
const tempConfig = {
...this.globalConfig,
...config,
MODEL_PROVIDER: providerType
};

View file

@ -322,6 +322,7 @@ export async function handleStreamRequest(res, service, model, requestBody, from
const addEvent = getProtocolPrefix(fromProvider) === MODEL_PROTOCOL_PREFIX.CLAUDE || getProtocolPrefix(fromProvider) === MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES;
let hasToolCall = false;
let hasMessageStop = false; // 跟踪是否已经发送过结束标志message_stop / done
try {
for await (const nativeChunk of nativeStream) {
@ -399,6 +400,20 @@ export async function handleStreamRequest(res, service, model, requestBody, from
}
}
// 防止重复发送结束标志
// OpenAI: choices[].finish_reason
// Claude: message_stop
// OpenAI Responses: done
// Gemini: candidates[].finishReason如 STOP / MAX_TOKENS / SAFETY 等)
if (
chunk?.choices?.some(choice => choice?.finish_reason) ||
chunk?.type === 'message_stop' ||
chunk?.type === 'done' ||
chunk?.candidates?.some(candidate => candidate?.finishReason)
) {
hasMessageStop = true;
}
if (addEvent) {
// fullOldResponseJson += chunk.type+"\n";
// fullResponseJson += chunk.type+"\n";
@ -574,13 +589,27 @@ export async function handleStreamRequest(res, service, model, requestBody, from
if (!res.writableEnded) {
try {
if (clientProtocol === MODEL_PROTOCOL_PREFIX.OPENAI) {
res.write('data: [DONE]\n\n');
if (!hasMessageStop) {
res.write('data: [DONE]\n\n');
hasMessageStop = true;
}
} else if (clientProtocol === MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES) {
res.write('event: done\n');
res.write('data: {}\n\n');
if (!hasMessageStop) {
res.write('event: done\n');
res.write('data: {}\n\n');
hasMessageStop = true;
}
} else if (clientProtocol === MODEL_PROTOCOL_PREFIX.CLAUDE) {
res.write('event: message_stop\n');
res.write('data: {"type":"message_stop"}\n\n');
if (!hasMessageStop) {
res.write('event: message_stop\n');
res.write('data: {"type":"message_stop"}\n\n');
hasMessageStop = true;
}
} else if (clientProtocol === MODEL_PROTOCOL_PREFIX.GEMINI) {
if (!hasMessageStop) {
res.write('data: {"candidates":[{"finishReason":"STOP"}]}\n\n');
hasMessageStop = true;
}
}
res.end();
} catch (writeErr) {