From 5c5283979bc00fa02022cde92fe9f2da7a5f72a3 Mon Sep 17 00:00:00 2001 From: alfadb Date: Fri, 12 Jun 2026 12:11:52 +0800 Subject: [PATCH] doc(thinking-protocol): clarify mappedModel vs originalModel semantics per call path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 回应 PR #3247 Copilot review: 1. NormalizeChineseLLMThinking 文档写 'MiniMax M3 / M3.x' 但实现覆盖 minimax-m* (M2.x 也命中)。改成 'MiniMax M-series (M2.x/M3.x)'。 2. Copilot 质疑 gemini_messages_compat 传 originalModel 不是 mappedModel 是误用。实际是有意为之的跨协议场景: - 上游是 Gemini,但被剥离的 body 是 Anthropic 格式 - 剥离逻辑要按客户端请求的 Anthropic 子协议族判定 - 传 mappedModel (gemini-3.1-pro) 会被判为 Unknown→不剥离→retry 死循环 未改代码逻辑,改为更新文档: - thinking_protocol.go ResolveThinkingProtocol 添加「调用路径语义」 说明 Anthropic gateway 与 Gemini compat 两条路径的参数语义差异。 - gemini_messages_compat_service.go 调用点加 inline 注释解释 路径语义为什么该传 originalModel。 --- backend/internal/service/gateway_request.go | 8 ++++---- .../service/gemini_messages_compat_service.go | 3 +++ backend/internal/service/thinking_protocol.go | 12 +++++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/backend/internal/service/gateway_request.go b/backend/internal/service/gateway_request.go index 1cf5728e0c..ff9786431d 100644 --- a/backend/internal/service/gateway_request.go +++ b/backend/internal/service/gateway_request.go @@ -1294,10 +1294,10 @@ func RectifyThinkingBudget(body []byte) ([]byte, bool) { // NormalizeChineseLLMThinking rewrites the top-level `thinking` object for Chinese // LLM providers that use Anthropic-compatible endpoints but have different accepted // values for `thinking.type`. Currently scoped to: -// - MiniMax M3 / M3.x (`MiniMax-m*`): official docs accept only `thinking.type` -// of "adaptive" or "disabled"; "enabled" is not a valid value and may be -// rejected/ignored. Pi-ai and other Anthropic-SDK clients default to "enabled" -// (Anthropic-original) and never auto-rewrite for non-Anthropic models. +// - MiniMax M-series (`MiniMax-m*`, covering M2.x / M3 / M3.x): official docs accept +// only `thinking.type` of "adaptive" or "disabled"; "enabled" is not a valid value +// and may be rejected/ignored. Pi-ai and other Anthropic-SDK clients default to +// "enabled" (Anthropic-original) and never auto-rewrite for non-Anthropic models. // // Non-MiniMax models (Kimi/GLM/DeepSeek) currently accept "enabled" as-is, so this // function is intentionally a no-op for them. New Chinese LLM quirks should be diff --git a/backend/internal/service/gemini_messages_compat_service.go b/backend/internal/service/gemini_messages_compat_service.go index f041292e2f..d56df59e4d 100644 --- a/backend/internal/service/gemini_messages_compat_service.go +++ b/backend/internal/service/gemini_messages_compat_service.go @@ -832,6 +832,9 @@ func (s *GeminiMessagesCompatService) Forward(ctx context.Context, c *gin.Contex var strippedClaudeBody []byte stageName := "" + // 路径说明:本处上游是 Gemini,但被剥离的 body 是 Anthropic 格式。传 originalModel + // (客户端原 Anthropic model)而非 mappedModel(上游 Gemini model),让剥离逻辑按 + // 客户端请求的 Anthropic 子协议族判定(详见 ResolveThinkingProtocol 文档)。 switch signatureRetryStage { case 0: // Stage 1: disable thinking + thinking->text diff --git a/backend/internal/service/thinking_protocol.go b/backend/internal/service/thinking_protocol.go index b7c0f2bc01..686baf467d 100644 --- a/backend/internal/service/thinking_protocol.go +++ b/backend/internal/service/thinking_protocol.go @@ -26,9 +26,15 @@ const ( ThinkingProtocolPassbackRequired ) -// ResolveThinkingProtocol 根据「实际发给上游的模型 ID」推断 thinking 协议族。 -// 必须传入映射后的模型 ID(mappedModel),不要传客户端原始请求模型 ID(reqModel), -// 否则用户配置「claude-sonnet-4-6 → deepseek-v4-pro」时会判错。 +// ResolveThinkingProtocol 根据「作为 thinking block 处理参考的模型 ID」推断 thinking 协议族。 +// +// 传入参数的语义随调用路径不同: +// - **Anthropic gateway**(转发原始 Anthropic 请求):传 mappedModel(账号级 model mapping +// 后的上游 model ID)。例:用户配置「claude-sonnet-4-6 → deepseek-v4-pro」后, +// 传 deepseek-v4-pro 才能被正确判为 passback-required。 +// - **Gemini messages compat**(Anthropic body → Gemini upstream):传 originalModel +// (客户端 Anthropic 请求的 model ID)。原因:此场景下上游是 Gemini,但被剥 +// 离的 body 是 Anthropic 格式,需按客户端请求的 Anthropic 子协议族判定剥离行为。 // // 匹配规则按厂商前缀硬编码: // - anthropic-strict: claude-* / opus-* / sonnet-* / haiku-*