diff --git a/backend/internal/pkg/antigravity/request_transformer.go b/backend/internal/pkg/antigravity/request_transformer.go index a75bf6b3..972771a8 100644 --- a/backend/internal/pkg/antigravity/request_transformer.go +++ b/backend/internal/pkg/antigravity/request_transformer.go @@ -314,7 +314,7 @@ func buildContents(messages []ClaudeMessage, toolIDToName map[string]string, isT parts = append([]GeminiPart{{ Text: "Thinking...", Thought: true, - ThoughtSignature: dummyThoughtSignature, + ThoughtSignature: DummyThoughtSignature, }}, parts...) } } @@ -332,9 +332,10 @@ func buildContents(messages []ClaudeMessage, toolIDToName map[string]string, isT return contents, strippedThinking, nil } -// dummyThoughtSignature 用于跳过 Gemini 3 thought_signature 验证 +// DummyThoughtSignature 用于跳过 Gemini 3 thought_signature 验证 // 参考: https://ai.google.dev/gemini-api/docs/thought-signatures -const dummyThoughtSignature = "skip_thought_signature_validator" +// 导出供跨包使用(如 gemini_native_signature_cleaner 跨账号修复) +const DummyThoughtSignature = "skip_thought_signature_validator" // buildParts 构建消息的 parts // allowDummyThought: 只有 Gemini 模型支持 dummy thought signature @@ -372,7 +373,7 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu // signature 处理: // - Claude 模型(allowDummyThought=false):必须是上游返回的真实 signature(dummy 视为缺失) // - Gemini 模型(allowDummyThought=true):优先透传真实 signature,缺失时使用 dummy signature - if block.Signature != "" && (allowDummyThought || block.Signature != dummyThoughtSignature) { + if block.Signature != "" && (allowDummyThought || block.Signature != DummyThoughtSignature) { part.ThoughtSignature = block.Signature } else if !allowDummyThought { // Claude 模型需要有效 signature;在缺失时降级为普通文本,并在上层禁用 thinking mode。 @@ -383,7 +384,7 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu continue } else { // Gemini 模型使用 dummy signature - part.ThoughtSignature = dummyThoughtSignature + part.ThoughtSignature = DummyThoughtSignature } parts = append(parts, part) @@ -413,10 +414,10 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu // tool_use 的 signature 处理: // - Claude 模型(allowDummyThought=false):必须是上游返回的真实 signature(dummy 视为缺失) // - Gemini 模型(allowDummyThought=true):优先透传真实 signature,缺失时使用 dummy signature - if block.Signature != "" && (allowDummyThought || block.Signature != dummyThoughtSignature) { + if block.Signature != "" && (allowDummyThought || block.Signature != DummyThoughtSignature) { part.ThoughtSignature = block.Signature } else if allowDummyThought { - part.ThoughtSignature = dummyThoughtSignature + part.ThoughtSignature = DummyThoughtSignature } parts = append(parts, part) diff --git a/backend/internal/pkg/antigravity/request_transformer_test.go b/backend/internal/pkg/antigravity/request_transformer_test.go index 9d62a4a1..f938b47f 100644 --- a/backend/internal/pkg/antigravity/request_transformer_test.go +++ b/backend/internal/pkg/antigravity/request_transformer_test.go @@ -86,7 +86,7 @@ func TestBuildParts_ThinkingBlockWithoutSignature(t *testing.T) { if len(parts) != 3 { t.Fatalf("expected 3 parts, got %d", len(parts)) } - if !parts[1].Thought || parts[1].ThoughtSignature != dummyThoughtSignature { + if !parts[1].Thought || parts[1].ThoughtSignature != DummyThoughtSignature { t.Fatalf("expected dummy thought signature, got thought=%v signature=%q", parts[1].Thought, parts[1].ThoughtSignature) } @@ -126,8 +126,8 @@ func TestBuildParts_ToolUseSignatureHandling(t *testing.T) { if len(parts) != 1 || parts[0].FunctionCall == nil { t.Fatalf("expected 1 functionCall part, got %+v", parts) } - if parts[0].ThoughtSignature != dummyThoughtSignature { - t.Fatalf("expected dummy tool signature %q, got %q", dummyThoughtSignature, parts[0].ThoughtSignature) + if parts[0].ThoughtSignature != DummyThoughtSignature { + t.Fatalf("expected dummy tool signature %q, got %q", DummyThoughtSignature, parts[0].ThoughtSignature) } }) diff --git a/backend/internal/service/gemini_native_signature_cleaner.go b/backend/internal/service/gemini_native_signature_cleaner.go index b3352fb0..d43fb445 100644 --- a/backend/internal/service/gemini_native_signature_cleaner.go +++ b/backend/internal/service/gemini_native_signature_cleaner.go @@ -2,20 +2,22 @@ package service import ( "encoding/json" + + "github.com/Wei-Shaw/sub2api/internal/pkg/antigravity" ) -// CleanGeminiNativeThoughtSignatures 从 Gemini 原生 API 请求中移除 thoughtSignature 字段, +// CleanGeminiNativeThoughtSignatures 从 Gemini 原生 API 请求中替换 thoughtSignature 字段为 dummy 签名, // 以避免跨账号签名验证错误。 // // 当粘性会话切换账号时(例如原账号异常、不可调度等),旧账号返回的 thoughtSignature -// 会导致新账号的签名验证失败。通过移除这些签名,让新账号重新生成有效的签名。 +// 会导致新账号的签名验证失败。通过替换为 dummy 签名,跳过签名验证。 // -// CleanGeminiNativeThoughtSignatures removes thoughtSignature fields from Gemini native API requests -// to avoid cross-account signature validation errors. +// CleanGeminiNativeThoughtSignatures replaces thoughtSignature fields with dummy signature +// in Gemini native API requests to avoid cross-account signature validation errors. // // When sticky session switches accounts (e.g., original account becomes unavailable), // thoughtSignatures from the old account will cause validation failures on the new account. -// By removing these signatures, we allow the new account to generate valid signatures. +// By replacing with dummy signature, we skip signature validation. func CleanGeminiNativeThoughtSignatures(body []byte) []byte { if len(body) == 0 { return body @@ -28,11 +30,11 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte { return body } - // 递归清理 thoughtSignature - cleaned := cleanThoughtSignaturesRecursive(data) + // 递归替换 thoughtSignature 为 dummy 签名 + replaced := replaceThoughtSignaturesRecursive(data) // 重新序列化 - result, err := json.Marshal(cleaned) + result, err := json.Marshal(replaced) if err != nil { // 如果序列化失败,返回原始 body return body @@ -41,19 +43,20 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte { return result } -// cleanThoughtSignaturesRecursive 递归遍历数据结构,移除所有 thoughtSignature 字段 -func cleanThoughtSignaturesRecursive(data any) any { +// replaceThoughtSignaturesRecursive 递归遍历数据结构,将所有 thoughtSignature 字段替换为 dummy 签名 +func replaceThoughtSignaturesRecursive(data any) any { switch v := data.(type) { case map[string]any: - // 创建新的 map,移除 thoughtSignature + // 创建新的 map,替换 thoughtSignature 为 dummy 签名 result := make(map[string]any, len(v)) for key, value := range v { - // 跳过 thoughtSignature 字段 + // 替换 thoughtSignature 字段为 dummy 签名 if key == "thoughtSignature" { + result[key] = antigravity.DummyThoughtSignature continue } // 递归处理嵌套结构 - result[key] = cleanThoughtSignaturesRecursive(value) + result[key] = replaceThoughtSignaturesRecursive(value) } return result @@ -61,7 +64,7 @@ func cleanThoughtSignaturesRecursive(data any) any { // 递归处理数组中的每个元素 result := make([]any, len(v)) for i, item := range v { - result[i] = cleanThoughtSignaturesRecursive(item) + result[i] = replaceThoughtSignaturesRecursive(item) } return result