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
226df1c2
Unverified
Commit
226df1c2
authored
Jan 22, 2026
by
Wesley Liddick
Committed by
GitHub
Jan 22, 2026
Browse files
Merge pull request #358 from 0xff26b9a8/main
fix(antigravity): 修复非流式 Claude To Antigravity 响应内容为空的问题
parents
2665230a
4f0c2b79
Changes
2
Hide whitespace changes
Inline
Side-by-side
backend/internal/pkg/antigravity/response_transformer.go
View file @
226df1c2
...
@@ -20,6 +20,15 @@ func TransformGeminiToClaude(geminiResp []byte, originalModel string) ([]byte, *
...
@@ -20,6 +20,15 @@ func TransformGeminiToClaude(geminiResp []byte, originalModel string) ([]byte, *
v1Resp
.
Response
=
directResp
v1Resp
.
Response
=
directResp
v1Resp
.
ResponseID
=
directResp
.
ResponseID
v1Resp
.
ResponseID
=
directResp
.
ResponseID
v1Resp
.
ModelVersion
=
directResp
.
ModelVersion
v1Resp
.
ModelVersion
=
directResp
.
ModelVersion
}
else
if
len
(
v1Resp
.
Response
.
Candidates
)
==
0
{
// 第一次解析成功但 candidates 为空,说明是直接的 GeminiResponse 格式
var
directResp
GeminiResponse
if
err2
:=
json
.
Unmarshal
(
geminiResp
,
&
directResp
);
err2
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"parse gemini response as direct: %w"
,
err2
)
}
v1Resp
.
Response
=
directResp
v1Resp
.
ResponseID
=
directResp
.
ResponseID
v1Resp
.
ModelVersion
=
directResp
.
ModelVersion
}
}
// 使用处理器转换
// 使用处理器转换
...
@@ -174,16 +183,20 @@ func (p *NonStreamingProcessor) processPart(part *GeminiPart) {
...
@@ -174,16 +183,20 @@ func (p *NonStreamingProcessor) processPart(part *GeminiPart) {
p
.
trailingSignature
=
""
p
.
trailingSignature
=
""
}
}
p
.
textBuilder
+=
part
.
Text
// 非空 text 带签名 - 特殊处理:先输出 text,再输出空 thinking 块
// 非空 text 带签名 - 立即刷新并输出空 thinking 块
if
signature
!=
""
{
if
signature
!=
""
{
p
.
flushText
()
p
.
contentBlocks
=
append
(
p
.
contentBlocks
,
ClaudeContentItem
{
Type
:
"text"
,
Text
:
part
.
Text
,
})
p
.
contentBlocks
=
append
(
p
.
contentBlocks
,
ClaudeContentItem
{
p
.
contentBlocks
=
append
(
p
.
contentBlocks
,
ClaudeContentItem
{
Type
:
"thinking"
,
Type
:
"thinking"
,
Thinking
:
""
,
Thinking
:
""
,
Signature
:
signature
,
Signature
:
signature
,
})
})
}
else
{
// 普通 text (无签名) - 累积到 builder
p
.
textBuilder
+=
part
.
Text
}
}
}
}
}
}
...
...
backend/internal/service/antigravity_gateway_service.go
View file @
226df1c2
...
@@ -1985,6 +1985,58 @@ func getOrCreateGeminiParts(response map[string]any) (result map[string]any, exi
...
@@ -1985,6 +1985,58 @@ func getOrCreateGeminiParts(response map[string]any) (result map[string]any, exi
return
result
,
existingParts
,
setParts
return
result
,
existingParts
,
setParts
}
}
// mergeCollectedPartsToResponse 将收集的所有 parts 合并到 Gemini 响应中
// 这个函数会合并所有类型的 parts:text、thinking、functionCall、inlineData 等
// 保持原始顺序,只合并连续的普通 text parts
func
mergeCollectedPartsToResponse
(
response
map
[
string
]
any
,
collectedParts
[]
map
[
string
]
any
)
map
[
string
]
any
{
if
len
(
collectedParts
)
==
0
{
return
response
}
result
,
_
,
setParts
:=
getOrCreateGeminiParts
(
response
)
// 合并策略:
// 1. 保持原始顺序
// 2. 连续的普通 text parts 合并为一个
// 3. thinking、functionCall、inlineData 等保持原样
var
mergedParts
[]
any
var
textBuffer
strings
.
Builder
flushTextBuffer
:=
func
()
{
if
textBuffer
.
Len
()
>
0
{
mergedParts
=
append
(
mergedParts
,
map
[
string
]
any
{
"text"
:
textBuffer
.
String
(),
})
textBuffer
.
Reset
()
}
}
for
_
,
part
:=
range
collectedParts
{
// 检查是否是普通 text part
if
text
,
ok
:=
part
[
"text"
]
.
(
string
);
ok
{
// 检查是否有 thought 标记
if
thought
,
_
:=
part
[
"thought"
]
.
(
bool
);
thought
{
// thinking part,先刷新 text buffer,然后保留原样
flushTextBuffer
()
mergedParts
=
append
(
mergedParts
,
part
)
}
else
{
// 普通 text,累积到 buffer
_
,
_
=
textBuffer
.
WriteString
(
text
)
}
}
else
{
// 非 text part(functionCall、inlineData 等),先刷新 text buffer,然后保留原样
flushTextBuffer
()
mergedParts
=
append
(
mergedParts
,
part
)
}
}
// 刷新剩余的 text
flushTextBuffer
()
setParts
(
mergedParts
)
return
result
}
// mergeImagePartsToResponse 将收集到的图片 parts 合并到 Gemini 响应中
// mergeImagePartsToResponse 将收集到的图片 parts 合并到 Gemini 响应中
func
mergeImagePartsToResponse
(
response
map
[
string
]
any
,
imageParts
[]
map
[
string
]
any
)
map
[
string
]
any
{
func
mergeImagePartsToResponse
(
response
map
[
string
]
any
,
imageParts
[]
map
[
string
]
any
)
map
[
string
]
any
{
if
len
(
imageParts
)
==
0
{
if
len
(
imageParts
)
==
0
{
...
@@ -2168,6 +2220,7 @@ func (s *AntigravityGatewayService) handleClaudeStreamToNonStreaming(c *gin.Cont
...
@@ -2168,6 +2220,7 @@ func (s *AntigravityGatewayService) handleClaudeStreamToNonStreaming(c *gin.Cont
var
firstTokenMs
*
int
var
firstTokenMs
*
int
var
last
map
[
string
]
any
var
last
map
[
string
]
any
var
lastWithParts
map
[
string
]
any
var
lastWithParts
map
[
string
]
any
var
collectedParts
[]
map
[
string
]
any
// 收集所有 parts(包括 text、thinking、functionCall、inlineData 等)
type
scanEvent
struct
{
type
scanEvent
struct
{
line
string
line
string
...
@@ -2262,9 +2315,12 @@ func (s *AntigravityGatewayService) handleClaudeStreamToNonStreaming(c *gin.Cont
...
@@ -2262,9 +2315,12 @@ func (s *AntigravityGatewayService) handleClaudeStreamToNonStreaming(c *gin.Cont
last
=
parsed
last
=
parsed
// 保留最后一个有 parts 的响应
// 保留最后一个有 parts 的响应
,并收集所有 parts
if
parts
:=
extractGeminiParts
(
parsed
);
len
(
parts
)
>
0
{
if
parts
:=
extractGeminiParts
(
parsed
);
len
(
parts
)
>
0
{
lastWithParts
=
parsed
lastWithParts
=
parsed
// 收集所有 parts(text、thinking、functionCall、inlineData 等)
collectedParts
=
append
(
collectedParts
,
parts
...
)
}
}
case
<-
intervalCh
:
case
<-
intervalCh
:
...
@@ -2287,6 +2343,11 @@ returnResponse:
...
@@ -2287,6 +2343,11 @@ returnResponse:
return
nil
,
s
.
writeClaudeError
(
c
,
http
.
StatusBadGateway
,
"upstream_error"
,
"Empty response from upstream"
)
return
nil
,
s
.
writeClaudeError
(
c
,
http
.
StatusBadGateway
,
"upstream_error"
,
"Empty response from upstream"
)
}
}
// 将收集的所有 parts 合并到最终响应中
if
len
(
collectedParts
)
>
0
{
finalResponse
=
mergeCollectedPartsToResponse
(
finalResponse
,
collectedParts
)
}
// 序列化为 JSON(Gemini 格式)
// 序列化为 JSON(Gemini 格式)
geminiBody
,
err
:=
json
.
Marshal
(
finalResponse
)
geminiBody
,
err
:=
json
.
Marshal
(
finalResponse
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
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