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
Show 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
parts
=
append
([]
GeminiPart
{{
Text
:
"Thinking..."
,
Thought
:
true
,
ThoughtSignature
:
d
ummyThoughtSignature
,
ThoughtSignature
:
D
ummyThoughtSignature
,
}},
parts
...
)
}
}
...
...
@@ -332,9 +332,10 @@ func buildContents(messages []ClaudeMessage, toolIDToName map[string]string, isT
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
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
!=
d
ummyThoughtSignature
)
{
if
block
.
Signature
!=
""
&&
(
allowDummyThought
||
block
.
Signature
!=
D
ummyThoughtSignature
)
{
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
=
d
ummyThoughtSignature
part
.
ThoughtSignature
=
D
ummyThoughtSignature
}
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
!=
d
ummyThoughtSignature
)
{
if
block
.
Signature
!=
""
&&
(
allowDummyThought
||
block
.
Signature
!=
D
ummyThoughtSignature
)
{
part
.
ThoughtSignature
=
block
.
Signature
}
else
if
allowDummyThought
{
part
.
ThoughtSignature
=
d
ummyThoughtSignature
part
.
ThoughtSignature
=
D
ummyThoughtSignature
}
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) {
if
len
(
parts
)
!=
3
{
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"
,
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
!=
d
ummyThoughtSignature
{
t
.
Fatalf
(
"expected dummy tool signature %q, got %q"
,
d
ummyThoughtSignature
,
parts
[
0
]
.
ThoughtSignature
)
if
parts
[
0
]
.
ThoughtSignature
!=
D
ummyThoughtSignature
{
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
import
(
"encoding/json"
"github.com/Wei-Shaw/sub2api/internal/pkg/antigravity"
)
// CleanGeminiNativeThoughtSignatures 从 Gemini 原生 API 请求中
移除
thoughtSignature 字段,
// CleanGeminiNativeThoughtSignatures 从 Gemini 原生 API 请求中
替换
thoughtSignature 字段
为 dummy 签名
,
// 以避免跨账号签名验证错误。
//
// 当粘性会话切换账号时(例如原账号异常、不可调度等),旧账号返回的 thoughtSignature
// 会导致新账号的签名验证失败。通过
移除这些签名,让新账号重新生成有效的签名
。
// 会导致新账号的签名验证失败。通过
替换为 dummy 签名,跳过签名验证
。
//
// CleanGeminiNativeThoughtSignatures re
mov
es thoughtSignature fields
from Gemini native API requests
// to avoid cross-account signature validation errors.
// CleanGeminiNativeThoughtSignatures re
plac
es 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 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
{
if
len
(
body
)
==
0
{
return
body
...
...
@@ -28,11 +30,11 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte {
return
body
}
// 递归
清理
thoughtSignature
cleaned
:=
clean
ThoughtSignaturesRecursive
(
data
)
// 递归
替换
thoughtSignature
为 dummy 签名
replaced
:=
replace
ThoughtSignaturesRecursive
(
data
)
// 重新序列化
result
,
err
:=
json
.
Marshal
(
clean
ed
)
result
,
err
:=
json
.
Marshal
(
replac
ed
)
if
err
!=
nil
{
// 如果序列化失败,返回原始 body
return
body
...
...
@@ -41,19 +43,20 @@ func CleanGeminiNativeThoughtSignatures(body []byte) []byte {
return
result
}
//
clean
ThoughtSignaturesRecursive 递归遍历数据结构,
移除
所有 thoughtSignature 字段
func
clean
ThoughtSignaturesRecursive
(
data
any
)
any
{
//
replace
ThoughtSignaturesRecursive 递归遍历数据结构,
将
所有 thoughtSignature 字段
替换为 dummy 签名
func
replace
ThoughtSignaturesRecursive
(
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
]
=
clean
ThoughtSignaturesRecursive
(
value
)
result
[
key
]
=
replace
ThoughtSignaturesRecursive
(
value
)
}
return
result
...
...
@@ -61,7 +64,7 @@ func cleanThoughtSignaturesRecursive(data any) any {
// 递归处理数组中的每个元素
result
:=
make
([]
any
,
len
(
v
))
for
i
,
item
:=
range
v
{
result
[
i
]
=
clean
ThoughtSignaturesRecursive
(
item
)
result
[
i
]
=
replace
ThoughtSignaturesRecursive
(
item
)
}
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