fix: 防止流式响应中重复发送结束标志并添加新模型支持
修复 handleStreamRequest 中可能重复发送流结束标志的问题,新增 hasMessageStop 状态跟踪。 在 provider-pool-manager 中修复临时配置缺少全局配置的问题。 为 Gemini 提供者添加新的模型支持:gemini-claude-opus-4-6-thinking。
This commit is contained in:
parent
60b4296097
commit
4d588f6c7d
4 changed files with 41 additions and 8 deletions
|
|
@ -66,7 +66,8 @@ const MODEL_ALIAS_MAP = {
|
||||||
'gemini-2.5-flash-preview': 'gemini-2.5-flash',
|
'gemini-2.5-flash-preview': 'gemini-2.5-flash',
|
||||||
'gemini-claude-sonnet-4-5': 'claude-sonnet-4-5',
|
'gemini-claude-sonnet-4-5': 'claude-sonnet-4-5',
|
||||||
'gemini-claude-sonnet-4-5-thinking': 'claude-sonnet-4-5-thinking',
|
'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',
|
'gemini-2.5-flash': 'gemini-2.5-flash-preview',
|
||||||
'claude-sonnet-4-5': 'gemini-claude-sonnet-4-5',
|
'claude-sonnet-4-5': 'gemini-claude-sonnet-4-5',
|
||||||
'claude-sonnet-4-5-thinking': 'gemini-claude-sonnet-4-5-thinking',
|
'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'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ export const PROVIDER_MODELS = {
|
||||||
'gemini-2.5-flash-preview',
|
'gemini-2.5-flash-preview',
|
||||||
'gemini-claude-sonnet-4-5',
|
'gemini-claude-sonnet-4-5',
|
||||||
'gemini-claude-sonnet-4-5-thinking',
|
'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-custom': [],
|
||||||
'claude-kiro-oauth': [
|
'claude-kiro-oauth': [
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,7 @@ export class ProviderPoolManager {
|
||||||
|
|
||||||
// 使用适配器进行刷新
|
// 使用适配器进行刷新
|
||||||
const tempConfig = {
|
const tempConfig = {
|
||||||
|
...this.globalConfig,
|
||||||
...config,
|
...config,
|
||||||
MODEL_PROVIDER: providerType
|
MODEL_PROVIDER: providerType
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
const addEvent = getProtocolPrefix(fromProvider) === MODEL_PROTOCOL_PREFIX.CLAUDE || getProtocolPrefix(fromProvider) === MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES;
|
||||||
|
|
||||||
let hasToolCall = false;
|
let hasToolCall = false;
|
||||||
|
let hasMessageStop = false; // 跟踪是否已经发送过结束标志(message_stop / done)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for await (const nativeChunk of nativeStream) {
|
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) {
|
if (addEvent) {
|
||||||
// fullOldResponseJson += chunk.type+"\n";
|
// fullOldResponseJson += chunk.type+"\n";
|
||||||
// fullResponseJson += chunk.type+"\n";
|
// fullResponseJson += chunk.type+"\n";
|
||||||
|
|
@ -574,13 +589,27 @@ export async function handleStreamRequest(res, service, model, requestBody, from
|
||||||
if (!res.writableEnded) {
|
if (!res.writableEnded) {
|
||||||
try {
|
try {
|
||||||
if (clientProtocol === MODEL_PROTOCOL_PREFIX.OPENAI) {
|
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) {
|
} else if (clientProtocol === MODEL_PROTOCOL_PREFIX.OPENAI_RESPONSES) {
|
||||||
res.write('event: done\n');
|
if (!hasMessageStop) {
|
||||||
res.write('data: {}\n\n');
|
res.write('event: done\n');
|
||||||
|
res.write('data: {}\n\n');
|
||||||
|
hasMessageStop = true;
|
||||||
|
}
|
||||||
} else if (clientProtocol === MODEL_PROTOCOL_PREFIX.CLAUDE) {
|
} else if (clientProtocol === MODEL_PROTOCOL_PREFIX.CLAUDE) {
|
||||||
res.write('event: message_stop\n');
|
if (!hasMessageStop) {
|
||||||
res.write('data: {"type":"message_stop"}\n\n');
|
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();
|
res.end();
|
||||||
} catch (writeErr) {
|
} catch (writeErr) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue