mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
refactor: Use helper to extract wrapped "thinking" text
Improve robustness when handling "thinking" content by using a dedicated helper to extract the thinking text. This ensures wrapped or nested thinking objects are handled correctly instead of relying on a direct string extraction, reducing parsing errors for complex payloads.
This commit is contained in:
@@ -118,7 +118,8 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
||||
contentResult := contentResults[j]
|
||||
contentTypeResult := contentResult.Get("type")
|
||||
if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "thinking" {
|
||||
thinkingText := contentResult.Get("thinking").String()
|
||||
// Use GetThinkingText to handle wrapped thinking objects
|
||||
thinkingText := util.GetThinkingText(contentResult)
|
||||
signatureResult := contentResult.Get("signature")
|
||||
signature := ""
|
||||
if signatureResult.Exists() && signatureResult.String() != "" {
|
||||
|
||||
87
internal/util/thinking_text.go
Normal file
87
internal/util/thinking_text.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
// GetThinkingText extracts the thinking text from a content part.
|
||||
// Handles various formats:
|
||||
// - Simple string: { "thinking": "text" } or { "text": "text" }
|
||||
// - Wrapped object: { "thinking": { "text": "text", "cache_control": {...} } }
|
||||
// - Gemini-style: { "thought": true, "text": "text" }
|
||||
// Returns the extracted text string.
|
||||
func GetThinkingText(part gjson.Result) string {
|
||||
// Try direct text field first (Gemini-style)
|
||||
if text := part.Get("text"); text.Exists() && text.Type == gjson.String {
|
||||
return text.String()
|
||||
}
|
||||
|
||||
// Try thinking field
|
||||
thinkingField := part.Get("thinking")
|
||||
if !thinkingField.Exists() {
|
||||
return ""
|
||||
}
|
||||
|
||||
// thinking is a string
|
||||
if thinkingField.Type == gjson.String {
|
||||
return thinkingField.String()
|
||||
}
|
||||
|
||||
// thinking is an object with inner text/thinking
|
||||
if thinkingField.IsObject() {
|
||||
if inner := thinkingField.Get("text"); inner.Exists() && inner.Type == gjson.String {
|
||||
return inner.String()
|
||||
}
|
||||
if inner := thinkingField.Get("thinking"); inner.Exists() && inner.Type == gjson.String {
|
||||
return inner.String()
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetThinkingTextFromJSON extracts thinking text from a raw JSON string.
|
||||
func GetThinkingTextFromJSON(jsonStr string) string {
|
||||
return GetThinkingText(gjson.Parse(jsonStr))
|
||||
}
|
||||
|
||||
// SanitizeThinkingPart normalizes a thinking part to a canonical form.
|
||||
// Strips cache_control and other non-essential fields.
|
||||
// Returns the sanitized part as JSON string.
|
||||
func SanitizeThinkingPart(part gjson.Result) string {
|
||||
// Gemini-style: { thought: true, text, thoughtSignature }
|
||||
if part.Get("thought").Bool() {
|
||||
result := `{"thought":true}`
|
||||
if text := GetThinkingText(part); text != "" {
|
||||
result, _ = sjson.Set(result, "text", text)
|
||||
}
|
||||
if sig := part.Get("thoughtSignature"); sig.Exists() && sig.Type == gjson.String {
|
||||
result, _ = sjson.Set(result, "thoughtSignature", sig.String())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Anthropic-style: { type: "thinking", thinking, signature }
|
||||
if part.Get("type").String() == "thinking" || part.Get("thinking").Exists() {
|
||||
result := `{"type":"thinking"}`
|
||||
if text := GetThinkingText(part); text != "" {
|
||||
result, _ = sjson.Set(result, "thinking", text)
|
||||
}
|
||||
if sig := part.Get("signature"); sig.Exists() && sig.Type == gjson.String {
|
||||
result, _ = sjson.Set(result, "signature", sig.String())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Not a thinking part, return as-is but strip cache_control
|
||||
return StripCacheControl(part.Raw)
|
||||
}
|
||||
|
||||
// StripCacheControl removes cache_control and providerOptions from a JSON object.
|
||||
func StripCacheControl(jsonStr string) string {
|
||||
result := jsonStr
|
||||
result, _ = sjson.Delete(result, "cache_control")
|
||||
result, _ = sjson.Delete(result, "providerOptions")
|
||||
return result
|
||||
}
|
||||
1
litellm
Submodule
1
litellm
Submodule
Submodule litellm added at 0c48826cdc
1
opencode-antigravity-auth
Submodule
1
opencode-antigravity-auth
Submodule
Submodule opencode-antigravity-auth added at 261a91f21b
1
opencode-google-antigravity-auth
Submodule
1
opencode-google-antigravity-auth
Submodule
Submodule opencode-google-antigravity-auth added at 9f9493c730
Reference in New Issue
Block a user