feat(thinking): enhance adaptive thinking support across models and update test cases
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
@@ -115,12 +116,60 @@ func ConvertGeminiRequestToClaude(modelName string, inputRawJSON []byte, stream
|
|||||||
// Include thoughts configuration for reasoning process visibility
|
// Include thoughts configuration for reasoning process visibility
|
||||||
// Translator only does format conversion, ApplyThinking handles model capability validation.
|
// Translator only does format conversion, ApplyThinking handles model capability validation.
|
||||||
if thinkingConfig := genConfig.Get("thinkingConfig"); thinkingConfig.Exists() && thinkingConfig.IsObject() {
|
if thinkingConfig := genConfig.Get("thinkingConfig"); thinkingConfig.Exists() && thinkingConfig.IsObject() {
|
||||||
|
hasLevel := func(levels []string, target string) bool {
|
||||||
|
for _, level := range levels {
|
||||||
|
if strings.EqualFold(strings.TrimSpace(level), target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mi := registry.LookupModelInfo(modelName, "claude")
|
||||||
|
supportsAdaptive := mi != nil && mi.Thinking != nil && len(mi.Thinking.Levels) > 0
|
||||||
|
supportsMax := supportsAdaptive && hasLevel(mi.Thinking.Levels, "max")
|
||||||
|
mapToEffort := func(level string) (string, bool) {
|
||||||
|
level = strings.ToLower(strings.TrimSpace(level))
|
||||||
|
switch level {
|
||||||
|
case "":
|
||||||
|
return "", false
|
||||||
|
case "minimal":
|
||||||
|
return "low", true
|
||||||
|
case "low", "medium", "high":
|
||||||
|
return level, true
|
||||||
|
case "xhigh", "max":
|
||||||
|
if supportsMax {
|
||||||
|
return "max", true
|
||||||
|
}
|
||||||
|
return "high", true
|
||||||
|
case "auto":
|
||||||
|
return "high", true
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
thinkingLevel := thinkingConfig.Get("thinkingLevel")
|
thinkingLevel := thinkingConfig.Get("thinkingLevel")
|
||||||
if !thinkingLevel.Exists() {
|
if !thinkingLevel.Exists() {
|
||||||
thinkingLevel = thinkingConfig.Get("thinking_level")
|
thinkingLevel = thinkingConfig.Get("thinking_level")
|
||||||
}
|
}
|
||||||
if thinkingLevel.Exists() {
|
if thinkingLevel.Exists() {
|
||||||
level := strings.ToLower(strings.TrimSpace(thinkingLevel.String()))
|
level := strings.ToLower(strings.TrimSpace(thinkingLevel.String()))
|
||||||
|
if supportsAdaptive {
|
||||||
|
switch level {
|
||||||
|
case "":
|
||||||
|
case "none":
|
||||||
|
out, _ = sjson.Set(out, "thinking.type", "disabled")
|
||||||
|
out, _ = sjson.Delete(out, "thinking.budget_tokens")
|
||||||
|
out, _ = sjson.Delete(out, "output_config.effort")
|
||||||
|
default:
|
||||||
|
effort, ok := mapToEffort(level)
|
||||||
|
if ok {
|
||||||
|
out, _ = sjson.Set(out, "thinking.type", "adaptive")
|
||||||
|
out, _ = sjson.Delete(out, "thinking.budget_tokens")
|
||||||
|
out, _ = sjson.Set(out, "output_config.effort", effort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
switch level {
|
switch level {
|
||||||
case "":
|
case "":
|
||||||
case "none":
|
case "none":
|
||||||
@@ -135,6 +184,7 @@ func ConvertGeminiRequestToClaude(modelName string, inputRawJSON []byte, stream
|
|||||||
out, _ = sjson.Set(out, "thinking.budget_tokens", budget)
|
out, _ = sjson.Set(out, "thinking.budget_tokens", budget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
thinkingBudget := thinkingConfig.Get("thinkingBudget")
|
thinkingBudget := thinkingConfig.Get("thinkingBudget")
|
||||||
if !thinkingBudget.Exists() {
|
if !thinkingBudget.Exists() {
|
||||||
@@ -142,6 +192,24 @@ func ConvertGeminiRequestToClaude(modelName string, inputRawJSON []byte, stream
|
|||||||
}
|
}
|
||||||
if thinkingBudget.Exists() {
|
if thinkingBudget.Exists() {
|
||||||
budget := int(thinkingBudget.Int())
|
budget := int(thinkingBudget.Int())
|
||||||
|
if supportsAdaptive {
|
||||||
|
switch budget {
|
||||||
|
case 0:
|
||||||
|
out, _ = sjson.Set(out, "thinking.type", "disabled")
|
||||||
|
out, _ = sjson.Delete(out, "thinking.budget_tokens")
|
||||||
|
out, _ = sjson.Delete(out, "output_config.effort")
|
||||||
|
default:
|
||||||
|
level, ok := thinking.ConvertBudgetToLevel(budget)
|
||||||
|
if ok {
|
||||||
|
effort, ok := mapToEffort(level)
|
||||||
|
if ok {
|
||||||
|
out, _ = sjson.Set(out, "thinking.type", "adaptive")
|
||||||
|
out, _ = sjson.Delete(out, "thinking.budget_tokens")
|
||||||
|
out, _ = sjson.Set(out, "output_config.effort", effort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
switch budget {
|
switch budget {
|
||||||
case 0:
|
case 0:
|
||||||
out, _ = sjson.Set(out, "thinking.type", "disabled")
|
out, _ = sjson.Set(out, "thinking.type", "disabled")
|
||||||
@@ -153,6 +221,7 @@ func ConvertGeminiRequestToClaude(modelName string, inputRawJSON []byte, stream
|
|||||||
out, _ = sjson.Set(out, "thinking.type", "enabled")
|
out, _ = sjson.Set(out, "thinking.type", "enabled")
|
||||||
out, _ = sjson.Set(out, "thinking.budget_tokens", budget)
|
out, _ = sjson.Set(out, "thinking.budget_tokens", budget)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if includeThoughts := thinkingConfig.Get("includeThoughts"); includeThoughts.Exists() && includeThoughts.Type == gjson.True {
|
} else if includeThoughts := thinkingConfig.Get("includeThoughts"); includeThoughts.Exists() && includeThoughts.Type == gjson.True {
|
||||||
out, _ = sjson.Set(out, "thinking.type", "enabled")
|
out, _ = sjson.Set(out, "thinking.type", "enabled")
|
||||||
} else if includeThoughts := thinkingConfig.Get("include_thoughts"); includeThoughts.Exists() && includeThoughts.Type == gjson.True {
|
} else if includeThoughts := thinkingConfig.Get("include_thoughts"); includeThoughts.Exists() && includeThoughts.Type == gjson.True {
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
effort = strings.ToLower(strings.TrimSpace(v.String()))
|
effort = strings.ToLower(strings.TrimSpace(v.String()))
|
||||||
}
|
}
|
||||||
switch effort {
|
switch effort {
|
||||||
case "low", "medium", "high":
|
case "minimal", "low", "medium", "high":
|
||||||
reasoningEffort = effort
|
reasoningEffort = effort
|
||||||
case "max":
|
case "max":
|
||||||
reasoningEffort = string(thinking.LevelXHigh)
|
reasoningEffort = string(thinking.LevelXHigh)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
@@ -151,7 +152,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map Anthropic thinking -> Gemini thinkingBudget/include_thoughts when enabled
|
// Map Anthropic thinking -> Gemini thinking config when enabled
|
||||||
// Translator only does format conversion, ApplyThinking handles model capability validation.
|
// Translator only does format conversion, ApplyThinking handles model capability validation.
|
||||||
if t := gjson.GetBytes(rawJSON, "thinking"); t.Exists() && t.IsObject() {
|
if t := gjson.GetBytes(rawJSON, "thinking"); t.Exists() && t.IsObject() {
|
||||||
switch t.Get("type").String() {
|
switch t.Get("type").String() {
|
||||||
@@ -162,9 +163,31 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.includeThoughts", true)
|
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.includeThoughts", true)
|
||||||
}
|
}
|
||||||
case "adaptive", "auto":
|
case "adaptive", "auto":
|
||||||
// Keep adaptive/auto as a high level sentinel; ApplyThinking resolves it
|
// For adaptive thinking:
|
||||||
// to model-specific max capability.
|
// - If output_config.effort is explicitly present, map it to thinkingLevel.
|
||||||
|
// - Otherwise, treat it as "enabled with target-model maximum" and emit thinkingBudget=max.
|
||||||
|
effort := ""
|
||||||
|
if v := gjson.GetBytes(rawJSON, "output_config.effort"); v.Exists() && v.Type == gjson.String {
|
||||||
|
effort = strings.ToLower(strings.TrimSpace(v.String()))
|
||||||
|
}
|
||||||
|
if effort != "" {
|
||||||
|
level := effort
|
||||||
|
switch level {
|
||||||
|
case "xhigh", "max":
|
||||||
|
level = "high"
|
||||||
|
}
|
||||||
|
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.thinkingLevel", level)
|
||||||
|
} else {
|
||||||
|
maxBudget := 0
|
||||||
|
if mi := registry.LookupModelInfo(modelName, "gemini"); mi != nil && mi.Thinking != nil {
|
||||||
|
maxBudget = mi.Thinking.Max
|
||||||
|
}
|
||||||
|
if maxBudget > 0 {
|
||||||
|
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.thinkingBudget", maxBudget)
|
||||||
|
} else {
|
||||||
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.thinkingLevel", "high")
|
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.thinkingLevel", "high")
|
||||||
|
}
|
||||||
|
}
|
||||||
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.includeThoughts", true)
|
out, _ = sjson.Set(out, "generationConfig.thinkingConfig.includeThoughts", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ func ConvertClaudeRequestToOpenAI(modelName string, inputRawJSON []byte, stream
|
|||||||
effort = strings.ToLower(strings.TrimSpace(v.String()))
|
effort = strings.ToLower(strings.TrimSpace(v.String()))
|
||||||
}
|
}
|
||||||
switch effort {
|
switch effort {
|
||||||
case "low", "medium", "high":
|
case "minimal", "low", "medium", "high":
|
||||||
out, _ = sjson.Set(out, "reasoning_effort", effort)
|
out, _ = sjson.Set(out, "reasoning_effort", effort)
|
||||||
case "max":
|
case "max":
|
||||||
out, _ = sjson.Set(out, "reasoning_effort", string(thinking.LevelXHigh))
|
out, _ = sjson.Set(out, "reasoning_effort", string(thinking.LevelXHigh))
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ type thinkingTestCase struct {
|
|||||||
inputJSON string
|
inputJSON string
|
||||||
expectField string
|
expectField string
|
||||||
expectValue string
|
expectValue string
|
||||||
|
expectField2 string
|
||||||
|
expectValue2 string
|
||||||
includeThoughts string
|
includeThoughts string
|
||||||
expectErr bool
|
expectErr bool
|
||||||
}
|
}
|
||||||
@@ -2590,9 +2592,8 @@ func TestThinkingE2EMatrix_Body(t *testing.T) {
|
|||||||
runThinkingTests(t, cases)
|
runThinkingTests(t, cases)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestThinkingE2EClaudeAdaptive_Body tests Claude thinking.type=adaptive extended body-only cases.
|
// TestThinkingE2EClaudeAdaptive_Body covers Group 3 cases in docs/thinking-e2e-test-cases.md.
|
||||||
// These cases validate that adaptive means "thinking enabled without explicit budget", and
|
// It focuses on Claude 4.6 adaptive thinking and effort/level cross-protocol semantics (body-only).
|
||||||
// cross-protocol conversion should resolve to target-model maximum thinking capability.
|
|
||||||
func TestThinkingE2EClaudeAdaptive_Body(t *testing.T) {
|
func TestThinkingE2EClaudeAdaptive_Body(t *testing.T) {
|
||||||
reg := registry.GetGlobalRegistry()
|
reg := registry.GetGlobalRegistry()
|
||||||
uid := fmt.Sprintf("thinking-e2e-claude-adaptive-%d", time.Now().UnixNano())
|
uid := fmt.Sprintf("thinking-e2e-claude-adaptive-%d", time.Now().UnixNano())
|
||||||
@@ -2601,32 +2602,347 @@ func TestThinkingE2EClaudeAdaptive_Body(t *testing.T) {
|
|||||||
defer reg.UnregisterClient(uid)
|
defer reg.UnregisterClient(uid)
|
||||||
|
|
||||||
cases := []thinkingTestCase{
|
cases := []thinkingTestCase{
|
||||||
// A1: Claude adaptive to OpenAI level model -> highest supported level
|
// A subgroup: OpenAI -> Claude (reasoning_effort -> output_config.effort)
|
||||||
{
|
{
|
||||||
name: "A1",
|
name: "A1",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"minimal"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A2",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"low"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A3",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "medium",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A4",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A5",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-opus-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-opus-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "max",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A6",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A7",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-opus-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-opus-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"max"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "max",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A8",
|
||||||
|
from: "openai",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"max"}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// B subgroup: Gemini -> Claude (thinkingLevel/thinkingBudget -> output_config.effort)
|
||||||
|
{
|
||||||
|
name: "B1",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"minimal"}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B2",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"low"}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B3",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"medium"}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "medium",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B4",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"high"}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B5",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-opus-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-opus-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"xhigh"}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "max",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B6",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"xhigh"}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B7",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":512}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B8",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":1024}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B9",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "medium",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B10",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":24576}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B11",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-opus-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-opus-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":32768}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "max",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B12",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":32768}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B13",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`,
|
||||||
|
expectField: "thinking.type",
|
||||||
|
expectValue: "disabled",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "B14",
|
||||||
|
from: "gemini",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`,
|
||||||
|
expectField: "output_config.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// C subgroup: Claude adaptive + effort cross-protocol conversion
|
||||||
|
{
|
||||||
|
name: "C1",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "openai",
|
to: "openai",
|
||||||
model: "level-model",
|
model: "level-model",
|
||||||
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"minimal"}}`,
|
||||||
|
expectField: "reasoning_effort",
|
||||||
|
expectValue: "minimal",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C2",
|
||||||
|
from: "claude",
|
||||||
|
to: "openai",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"low"}}`,
|
||||||
|
expectField: "reasoning_effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C3",
|
||||||
|
from: "claude",
|
||||||
|
to: "openai",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"medium"}}`,
|
||||||
|
expectField: "reasoning_effort",
|
||||||
|
expectValue: "medium",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C4",
|
||||||
|
from: "claude",
|
||||||
|
to: "openai",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
expectField: "reasoning_effort",
|
expectField: "reasoning_effort",
|
||||||
expectValue: "high",
|
expectValue: "high",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A2: Claude adaptive to Gemini level subset model -> highest supported level
|
|
||||||
{
|
{
|
||||||
name: "A2",
|
name: "C5",
|
||||||
|
from: "claude",
|
||||||
|
to: "openai",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"xhigh"}}`,
|
||||||
|
expectField: "reasoning_effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C6",
|
||||||
|
from: "claude",
|
||||||
|
to: "openai",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"max"}}`,
|
||||||
|
expectField: "reasoning_effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C7",
|
||||||
|
from: "claude",
|
||||||
|
to: "openai",
|
||||||
|
model: "no-thinking-model",
|
||||||
|
inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
|
expectField: "",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "C8",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "gemini",
|
to: "gemini",
|
||||||
model: "level-subset-model",
|
model: "level-subset-model",
|
||||||
inputJSON: `{"model":"level-subset-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"level-subset-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
expectField: "generationConfig.thinkingConfig.thinkingLevel",
|
expectField: "generationConfig.thinkingConfig.thinkingLevel",
|
||||||
expectValue: "high",
|
expectValue: "high",
|
||||||
includeThoughts: "true",
|
includeThoughts: "true",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A3: Claude adaptive to Gemini budget model -> max budget
|
|
||||||
{
|
{
|
||||||
name: "A3",
|
name: "C9",
|
||||||
|
from: "claude",
|
||||||
|
to: "gemini",
|
||||||
|
model: "gemini-budget-model",
|
||||||
|
inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"low"}}`,
|
||||||
|
expectField: "generationConfig.thinkingConfig.thinkingBudget",
|
||||||
|
expectValue: "1024",
|
||||||
|
includeThoughts: "true",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C10",
|
||||||
|
from: "claude",
|
||||||
|
to: "gemini",
|
||||||
|
model: "gemini-budget-model",
|
||||||
|
inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"medium"}}`,
|
||||||
|
expectField: "generationConfig.thinkingConfig.thinkingBudget",
|
||||||
|
expectValue: "8192",
|
||||||
|
includeThoughts: "true",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C11",
|
||||||
|
from: "claude",
|
||||||
|
to: "gemini",
|
||||||
|
model: "gemini-budget-model",
|
||||||
|
inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
|
expectField: "generationConfig.thinkingConfig.thinkingBudget",
|
||||||
|
expectValue: "20000",
|
||||||
|
includeThoughts: "true",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C12",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "gemini",
|
to: "gemini",
|
||||||
model: "gemini-budget-model",
|
model: "gemini-budget-model",
|
||||||
@@ -2636,32 +2952,91 @@ func TestThinkingE2EClaudeAdaptive_Body(t *testing.T) {
|
|||||||
includeThoughts: "true",
|
includeThoughts: "true",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A4: Claude adaptive to Gemini mixed model -> highest supported level
|
|
||||||
{
|
{
|
||||||
name: "A4",
|
name: "C13",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "gemini",
|
to: "gemini",
|
||||||
model: "gemini-mixed-model",
|
model: "gemini-mixed-model",
|
||||||
inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
expectField: "generationConfig.thinkingConfig.thinkingLevel",
|
expectField: "generationConfig.thinkingConfig.thinkingLevel",
|
||||||
expectValue: "high",
|
expectValue: "high",
|
||||||
includeThoughts: "true",
|
includeThoughts: "true",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A5: Claude adaptive passthrough for same protocol
|
|
||||||
{
|
{
|
||||||
name: "A5",
|
name: "C14",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "claude",
|
to: "codex",
|
||||||
model: "claude-budget-model",
|
model: "level-model",
|
||||||
inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"minimal"}}`,
|
||||||
expectField: "thinking.type",
|
expectField: "reasoning.effort",
|
||||||
expectValue: "adaptive",
|
expectValue: "minimal",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A6: Claude adaptive to Antigravity budget model -> max budget
|
|
||||||
{
|
{
|
||||||
name: "A6",
|
name: "C15",
|
||||||
|
from: "claude",
|
||||||
|
to: "codex",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"low"}}`,
|
||||||
|
expectField: "reasoning.effort",
|
||||||
|
expectValue: "low",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C16",
|
||||||
|
from: "claude",
|
||||||
|
to: "codex",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
|
expectField: "reasoning.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C17",
|
||||||
|
from: "claude",
|
||||||
|
to: "codex",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"xhigh"}}`,
|
||||||
|
expectField: "reasoning.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C18",
|
||||||
|
from: "claude",
|
||||||
|
to: "codex",
|
||||||
|
model: "level-model",
|
||||||
|
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"max"}}`,
|
||||||
|
expectField: "reasoning.effort",
|
||||||
|
expectValue: "high",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "C19",
|
||||||
|
from: "claude",
|
||||||
|
to: "iflow",
|
||||||
|
model: "glm-test",
|
||||||
|
inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"minimal"}}`,
|
||||||
|
expectField: "chat_template_kwargs.enable_thinking",
|
||||||
|
expectValue: "true",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C20",
|
||||||
|
from: "claude",
|
||||||
|
to: "iflow",
|
||||||
|
model: "minimax-test",
|
||||||
|
inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
|
expectField: "reasoning_split",
|
||||||
|
expectValue: "true",
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "C21",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "antigravity",
|
to: "antigravity",
|
||||||
model: "antigravity-budget-model",
|
model: "antigravity-budget-model",
|
||||||
@@ -2671,48 +3046,66 @@ func TestThinkingE2EClaudeAdaptive_Body(t *testing.T) {
|
|||||||
includeThoughts: "true",
|
includeThoughts: "true",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A7: Claude adaptive to iFlow GLM -> enabled boolean
|
|
||||||
{
|
{
|
||||||
name: "A7",
|
name: "C22",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "iflow",
|
to: "claude",
|
||||||
model: "glm-test",
|
model: "claude-sonnet-4-6-model",
|
||||||
inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"medium"}}`,
|
||||||
expectField: "chat_template_kwargs.enable_thinking",
|
expectField: "thinking.type",
|
||||||
expectValue: "true",
|
expectValue: "adaptive",
|
||||||
|
expectField2: "output_config.effort",
|
||||||
|
expectValue2: "medium",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A8: Claude adaptive to iFlow MiniMax -> enabled boolean
|
|
||||||
{
|
{
|
||||||
name: "A8",
|
name: "C23",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "iflow",
|
to: "claude",
|
||||||
model: "minimax-test",
|
model: "claude-opus-4-6-model",
|
||||||
inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"claude-opus-4-6-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"max"}}`,
|
||||||
expectField: "reasoning_split",
|
expectField: "thinking.type",
|
||||||
expectValue: "true",
|
expectValue: "adaptive",
|
||||||
|
expectField2: "output_config.effort",
|
||||||
|
expectValue2: "max",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A9: Claude adaptive to Codex level model -> highest supported level
|
|
||||||
{
|
{
|
||||||
name: "A9",
|
name: "C24",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "codex",
|
to: "claude",
|
||||||
model: "level-model",
|
model: "claude-opus-4-6-model",
|
||||||
inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"claude-opus-4-6-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"xhigh"}}`,
|
||||||
expectField: "reasoning.effort",
|
expectErr: true,
|
||||||
expectValue: "high",
|
},
|
||||||
|
{
|
||||||
|
name: "C25",
|
||||||
|
from: "claude",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"high"}}`,
|
||||||
|
expectField: "thinking.type",
|
||||||
|
expectValue: "adaptive",
|
||||||
|
expectField2: "output_config.effort",
|
||||||
|
expectValue2: "high",
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
// A10: Claude adaptive on non-thinking model should still be stripped
|
|
||||||
{
|
{
|
||||||
name: "A10",
|
name: "C26",
|
||||||
from: "claude",
|
from: "claude",
|
||||||
to: "openai",
|
to: "claude",
|
||||||
model: "no-thinking-model",
|
model: "claude-sonnet-4-6-model",
|
||||||
inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`,
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"max"}}`,
|
||||||
expectField: "",
|
expectErr: true,
|
||||||
expectErr: false,
|
},
|
||||||
|
{
|
||||||
|
name: "C27",
|
||||||
|
from: "claude",
|
||||||
|
to: "claude",
|
||||||
|
model: "claude-sonnet-4-6-model",
|
||||||
|
inputJSON: `{"model":"claude-sonnet-4-6-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"},"output_config":{"effort":"xhigh"}}`,
|
||||||
|
expectErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2767,6 +3160,29 @@ func getTestModels() []*registry.ModelInfo {
|
|||||||
DisplayName: "Claude Budget Model",
|
DisplayName: "Claude Budget Model",
|
||||||
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: true, DynamicAllowed: false},
|
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: true, DynamicAllowed: false},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: "claude-sonnet-4-6-model",
|
||||||
|
Object: "model",
|
||||||
|
Created: 1771372800, // 2026-02-17
|
||||||
|
OwnedBy: "anthropic",
|
||||||
|
Type: "claude",
|
||||||
|
DisplayName: "Claude 4.6 Sonnet",
|
||||||
|
ContextLength: 200000,
|
||||||
|
MaxCompletionTokens: 64000,
|
||||||
|
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: true, DynamicAllowed: false, Levels: []string{"low", "medium", "high"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "claude-opus-4-6-model",
|
||||||
|
Object: "model",
|
||||||
|
Created: 1770318000, // 2026-02-05
|
||||||
|
OwnedBy: "anthropic",
|
||||||
|
Type: "claude",
|
||||||
|
DisplayName: "Claude 4.6 Opus",
|
||||||
|
Description: "Premium model combining maximum intelligence with practical performance",
|
||||||
|
ContextLength: 1000000,
|
||||||
|
MaxCompletionTokens: 128000,
|
||||||
|
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: true, DynamicAllowed: false, Levels: []string{"low", "medium", "high", "max"}},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ID: "antigravity-budget-model",
|
ID: "antigravity-budget-model",
|
||||||
Object: "model",
|
Object: "model",
|
||||||
@@ -2879,17 +3295,23 @@ func runThinkingTests(t *testing.T, cases []thinkingTestCase) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val := gjson.GetBytes(body, tc.expectField)
|
assertField := func(fieldPath, expected string) {
|
||||||
|
val := gjson.GetBytes(body, fieldPath)
|
||||||
if !val.Exists() {
|
if !val.Exists() {
|
||||||
t.Fatalf("expected field %s not found, body=%s", tc.expectField, string(body))
|
t.Fatalf("expected field %s not found, body=%s", fieldPath, string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
actualValue := val.String()
|
actualValue := val.String()
|
||||||
if val.Type == gjson.Number {
|
if val.Type == gjson.Number {
|
||||||
actualValue = fmt.Sprintf("%d", val.Int())
|
actualValue = fmt.Sprintf("%d", val.Int())
|
||||||
}
|
}
|
||||||
if actualValue != tc.expectValue {
|
if actualValue != expected {
|
||||||
t.Fatalf("field %s: expected %q, got %q, body=%s", tc.expectField, tc.expectValue, actualValue, string(body))
|
t.Fatalf("field %s: expected %q, got %q, body=%s", fieldPath, expected, actualValue, string(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertField(tc.expectField, tc.expectValue)
|
||||||
|
if tc.expectField2 != "" {
|
||||||
|
assertField(tc.expectField2, tc.expectValue2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tc.includeThoughts != "" && (tc.to == "gemini" || tc.to == "gemini-cli" || tc.to == "antigravity") {
|
if tc.includeThoughts != "" && (tc.to == "gemini" || tc.to == "gemini-cli" || tc.to == "antigravity") {
|
||||||
|
|||||||
Reference in New Issue
Block a user