Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
cb58daf3
Unverified
Commit
cb58daf3
authored
Feb 03, 2026
by
Wesley Liddick
Committed by
GitHub
Feb 03, 2026
Browse files
Merge pull request #476 from touwaeriol/fix/gemini-thoughtsignature-v2
fix(gemini): 导出 DummyThoughtSignature 常量并修复跨账号签名验证
parents
a9398d21
6baf8108
Changes
3
Hide whitespace changes
Inline
Side-by-side
backend/internal/pkg/antigravity/request_transformer.go
View file @
cb58daf3
...
@@ -314,7 +314,7 @@ func buildContents(messages []ClaudeMessage, toolIDToName map[string]string, isT
...
@@ -314,7 +314,7 @@ func buildContents(messages []ClaudeMessage, toolIDToName map[string]string, isT
parts
=
append
([]
GeminiPart
{{
parts
=
append
([]
GeminiPart
{{
Text
:
"Thinking..."
,
Text
:
"Thinking..."
,
Thought
:
true
,
Thought
:
true
,
ThoughtSignature
:
d
ummyThoughtSignature
,
ThoughtSignature
:
D
ummyThoughtSignature
,
}},
parts
...
)
}},
parts
...
)
}
}
}
}
...
@@ -332,9 +332,10 @@ func buildContents(messages []ClaudeMessage, toolIDToName map[string]string, isT
...
@@ -332,9 +332,10 @@ func buildContents(messages []ClaudeMessage, toolIDToName map[string]string, isT
return
contents
,
strippedThinking
,
nil
return
contents
,
strippedThinking
,
nil
}
}
//
d
ummyThoughtSignature 用于跳过 Gemini 3 thought_signature 验证
//
D
ummyThoughtSignature 用于跳过 Gemini 3 thought_signature 验证
// 参考: https://ai.google.dev/gemini-api/docs/thought-signatures
// 参考: 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
// buildParts 构建消息的 parts
// allowDummyThought: 只有 Gemini 模型支持 dummy thought signature
// allowDummyThought: 只有 Gemini 模型支持 dummy thought signature
...
@@ -372,7 +373,7 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu
...
@@ -372,7 +373,7 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu
// signature 处理:
// signature 处理:
// - Claude 模型(allowDummyThought=false):必须是上游返回的真实 signature(dummy 视为缺失)
// - Claude 模型(allowDummyThought=false):必须是上游返回的真实 signature(dummy 视为缺失)
// - Gemini 模型(allowDummyThought=true):优先透传真实 signature,缺失时使用 dummy signature
// - Gemini 模型(allowDummyThought=true):优先透传真实 signature,缺失时使用 dummy signature
if
block
.
Signature
!=
""
&&
(
allowDummyThought
||
block
.
Signature
!=
d
ummyThoughtSignature
)
{
if
block
.
Signature
!=
""
&&
(
allowDummyThought
||
block
.
Signature
!=
D
ummyThoughtSignature
)
{
part
.
ThoughtSignature
=
block
.
Signature
part
.
ThoughtSignature
=
block
.
Signature
}
else
if
!
allowDummyThought
{
}
else
if
!
allowDummyThought
{
// Claude 模型需要有效 signature;在缺失时降级为普通文本,并在上层禁用 thinking mode。
// Claude 模型需要有效 signature;在缺失时降级为普通文本,并在上层禁用 thinking mode。
...
@@ -383,7 +384,7 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu
...
@@ -383,7 +384,7 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu
continue
continue
}
else
{
}
else
{
// Gemini 模型使用 dummy signature
// Gemini 模型使用 dummy signature
part
.
ThoughtSignature
=
d
ummyThoughtSignature
part
.
ThoughtSignature
=
D
ummyThoughtSignature
}
}
parts
=
append
(
parts
,
part
)
parts
=
append
(
parts
,
part
)
...
@@ -413,10 +414,10 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu
...
@@ -413,10 +414,10 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu
// tool_use 的 signature 处理:
// tool_use 的 signature 处理:
// - Claude 模型(allowDummyThought=false):必须是上游返回的真实 signature(dummy 视为缺失)
// - Claude 模型(allowDummyThought=false):必须是上游返回的真实 signature(dummy 视为缺失)
// - Gemini 模型(allowDummyThought=true):优先透传真实 signature,缺失时使用 dummy signature
// - Gemini 模型(allowDummyThought=true):优先透传真实 signature,缺失时使用 dummy signature
if
block
.
Signature
!=
""
&&
(
allowDummyThought
||
block
.
Signature
!=
d
ummyThoughtSignature
)
{
if
block
.
Signature
!=
""
&&
(
allowDummyThought
||
block
.
Signature
!=
D
ummyThoughtSignature
)
{
part
.
ThoughtSignature
=
block
.
Signature
part
.
ThoughtSignature
=
block
.
Signature
}
else
if
allowDummyThought
{
}
else
if
allowDummyThought
{
part
.
ThoughtSignature
=
d
ummyThoughtSignature
part
.
ThoughtSignature
=
D
ummyThoughtSignature
}
}
parts
=
append
(
parts
,
part
)
parts
=
append
(
parts
,
part
)
...
...
backend/internal/pkg/antigravity/request_transformer_test.go
View file @
cb58daf3
...
@@ -86,7 +86,7 @@ func TestBuildParts_ThinkingBlockWithoutSignature(t *testing.T) {
...
@@ -86,7 +86,7 @@ func TestBuildParts_ThinkingBlockWithoutSignature(t *testing.T) {
if
len
(
parts
)
!=
3
{
if
len
(
parts
)
!=
3
{
t
.
Fatalf
(
"expected 3 parts, got %d"
,
len
(
parts
))
t
.
Fatalf
(
"expected 3 parts, got %d"
,
len
(
parts
))
}
}
if
!
parts
[
1
]
.
Thought
||
parts
[
1
]
.
ThoughtSignature
!=
d
ummyThoughtSignature
{
if
!
parts
[
1
]
.
Thought
||
parts
[
1
]
.
ThoughtSignature
!=
D
ummyThoughtSignature
{
t
.
Fatalf
(
"expected dummy thought signature, got thought=%v signature=%q"
,
t
.
Fatalf
(
"expected dummy thought signature, got thought=%v signature=%q"
,
parts
[
1
]
.
Thought
,
parts
[
1
]
.
ThoughtSignature
)
parts
[
1
]
.
Thought
,
parts
[
1
]
.
ThoughtSignature
)
}
}
...
@@ -126,8 +126,8 @@ func TestBuildParts_ToolUseSignatureHandling(t *testing.T) {
...
@@ -126,8 +126,8 @@ func TestBuildParts_ToolUseSignatureHandling(t *testing.T) {
if
len
(
parts
)
!=
1
||
parts
[
0
]
.
FunctionCall
==
nil
{
if
len
(
parts
)
!=
1
||
parts
[
0
]
.
FunctionCall
==
nil
{
t
.
Fatalf
(
"expected 1 functionCall part, got %+v"
,
parts
)
t
.
Fatalf
(
"expected 1 functionCall part, got %+v"
,
parts
)
}
}
if
parts
[
0
]
.
ThoughtSignature
!=
d
ummyThoughtSignature
{
if
parts
[
0
]
.
ThoughtSignature
!=
D
ummyThoughtSignature
{
t
.
Fatalf
(
"expected dummy tool signature %q, got %q"
,
d
ummyThoughtSignature
,
parts
[
0
]
.
ThoughtSignature
)
t
.
Fatalf
(
"expected dummy tool signature %q, got %q"
,
D
ummyThoughtSignature
,
parts
[
0
]
.
ThoughtSignature
)
}
}
})
})
...
...
backend/internal/service/gemini_native_signature_cleaner.go
View file @
cb58daf3
...
@@ -2,20 +2,22 @@ package service
...
@@ -2,20 +2,22 @@ package service
import
(
import
(
"encoding/json"
"encoding/json"
"github.com/Wei-Shaw/sub2api/internal/pkg/antigravity"
)
)
// CleanGeminiNativeThoughtSignatures 从 Gemini 原生 API 请求中
移除
thoughtSignature 字段,
// CleanGeminiNativeThoughtSignatures 从 Gemini 原生 API 请求中
替换
thoughtSignature 字段
为 dummy 签名
,
// 以避免跨账号签名验证错误。
// 以避免跨账号签名验证错误。
//
//
// 当粘性会话切换账号时(例如原账号异常、不可调度等),旧账号返回的 thoughtSignature
// 当粘性会话切换账号时(例如原账号异常、不可调度等),旧账号返回的 thoughtSignature
// 会导致新账号的签名验证失败。通过
移除这些签名,让新账号重新生成有效的签名
。
// 会导致新账号的签名验证失败。通过
替换为 dummy 签名,跳过签名验证
。
//
//
// CleanGeminiNativeThoughtSignatures re
mov
es thoughtSignature fields
from Gemini native API requests
// CleanGeminiNativeThoughtSignatures re
plac
es thoughtSignature fields
with dummy signature
// to avoid cross-account signature validation errors.
//
in Gemini native API requests
to avoid cross-account signature validation errors.
//
//
// When sticky session switches accounts (e.g., original account becomes unavailable),
// When sticky session switches accounts (e.g., original account becomes unavailable),
// thoughtSignatures from the old account will cause validation failures on the new account.
// thoughtSignatures from the old account will cause validation failures on the new account.
// By re
moving these
signature
s
, we
allow the new account to generate valid signatures
.
// By re
placing with dummy
signature, we
skip signature validation
.
func
CleanGeminiNativeThoughtSignatures
(
body
[]
byte
)
[]
byte
{
func
CleanGeminiNativeThoughtSignatures
(
body
[]
byte
)
[]
byte
{
if
len
(
body
)
==
0
{
if
len
(
body
)
==
0
{
return
body
return
body
...
@@ -28,11 +30,11 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte {
...
@@ -28,11 +30,11 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte {
return
body
return
body
}
}
// 递归
清理
thoughtSignature
// 递归
替换
thoughtSignature
为 dummy 签名
cleaned
:=
clean
ThoughtSignaturesRecursive
(
data
)
replaced
:=
replace
ThoughtSignaturesRecursive
(
data
)
// 重新序列化
// 重新序列化
result
,
err
:=
json
.
Marshal
(
clean
ed
)
result
,
err
:=
json
.
Marshal
(
replac
ed
)
if
err
!=
nil
{
if
err
!=
nil
{
// 如果序列化失败,返回原始 body
// 如果序列化失败,返回原始 body
return
body
return
body
...
@@ -41,19 +43,20 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte {
...
@@ -41,19 +43,20 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte {
return
result
return
result
}
}
//
clean
ThoughtSignaturesRecursive 递归遍历数据结构,
移除
所有 thoughtSignature 字段
//
replace
ThoughtSignaturesRecursive 递归遍历数据结构,
将
所有 thoughtSignature 字段
替换为 dummy 签名
func
clean
ThoughtSignaturesRecursive
(
data
any
)
any
{
func
replace
ThoughtSignaturesRecursive
(
data
any
)
any
{
switch
v
:=
data
.
(
type
)
{
switch
v
:=
data
.
(
type
)
{
case
map
[
string
]
any
:
case
map
[
string
]
any
:
// 创建新的 map,
移除
thoughtSignature
// 创建新的 map,
替换
thoughtSignature
为 dummy 签名
result
:=
make
(
map
[
string
]
any
,
len
(
v
))
result
:=
make
(
map
[
string
]
any
,
len
(
v
))
for
key
,
value
:=
range
v
{
for
key
,
value
:=
range
v
{
//
跳过
thoughtSignature 字段
//
替换
thoughtSignature 字段
为 dummy 签名
if
key
==
"thoughtSignature"
{
if
key
==
"thoughtSignature"
{
result
[
key
]
=
antigravity
.
DummyThoughtSignature
continue
continue
}
}
// 递归处理嵌套结构
// 递归处理嵌套结构
result
[
key
]
=
clean
ThoughtSignaturesRecursive
(
value
)
result
[
key
]
=
replace
ThoughtSignaturesRecursive
(
value
)
}
}
return
result
return
result
...
@@ -61,7 +64,7 @@ func cleanThoughtSignaturesRecursive(data any) any {
...
@@ -61,7 +64,7 @@ func cleanThoughtSignaturesRecursive(data any) any {
// 递归处理数组中的每个元素
// 递归处理数组中的每个元素
result
:=
make
([]
any
,
len
(
v
))
result
:=
make
([]
any
,
len
(
v
))
for
i
,
item
:=
range
v
{
for
i
,
item
:=
range
v
{
result
[
i
]
=
clean
ThoughtSignaturesRecursive
(
item
)
result
[
i
]
=
replace
ThoughtSignaturesRecursive
(
item
)
}
}
return
result
return
result
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment