Spaces:
Paused
Paused
| package test | |
| import ( | |
| "fmt" | |
| "strings" | |
| "testing" | |
| "time" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator" | |
| // Import provider packages to trigger init() registration of ProviderAppliers | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/antigravity" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/claude" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/codex" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/gemini" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/geminicli" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/iflow" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/kimi" | |
| _ "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking/provider/openai" | |
| "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" | |
| "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking" | |
| sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator" | |
| "github.com/tidwall/gjson" | |
| "github.com/tidwall/sjson" | |
| ) | |
| // thinkingTestCase represents a common test case structure for both suffix and body tests. | |
| type thinkingTestCase struct { | |
| name string | |
| from string | |
| to string | |
| model string | |
| inputJSON string | |
| expectField string | |
| expectValue string | |
| includeThoughts string | |
| expectErr bool | |
| } | |
| // TestThinkingE2EMatrix_Suffix tests the thinking configuration transformation using model name suffix. | |
| // Data flow: Input JSON → TranslateRequest → ApplyThinking → Validate Output | |
| // No helper functions are used; all test data is inline. | |
| func TestThinkingE2EMatrix_Suffix(t *testing.T) { | |
| reg := registry.GetGlobalRegistry() | |
| uid := fmt.Sprintf("thinking-e2e-suffix-%d", time.Now().UnixNano()) | |
| reg.RegisterClient(uid, "test", getTestModels()) | |
| defer reg.UnregisterClient(uid) | |
| cases := []thinkingTestCase{ | |
| // level-model (Levels=minimal/low/medium/high, ZeroAllowed=false, DynamicAllowed=false) | |
| // Case 1: No suffix → injected default → medium | |
| { | |
| name: "1", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 2: Specified medium → medium | |
| { | |
| name: "2", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model(medium)", | |
| inputJSON: `{"model":"level-model(medium)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 3: Specified xhigh → out of range error | |
| { | |
| name: "3", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model(xhigh)", | |
| inputJSON: `{"model":"level-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 4: Level none → clamped to minimal (ZeroAllowed=false) | |
| { | |
| name: "4", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model(none)", | |
| inputJSON: `{"model":"level-model(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "minimal", | |
| expectErr: false, | |
| }, | |
| // Case 5: Level auto → DynamicAllowed=false → medium (mid-range) | |
| { | |
| name: "5", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model(auto)", | |
| inputJSON: `{"model":"level-model(auto)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 6: No suffix from gemini → injected default → medium | |
| { | |
| name: "6", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 7: Budget 8192 → medium | |
| { | |
| name: "7", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model(8192)", | |
| inputJSON: `{"model":"level-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 8: Budget 64000 → clamped to high | |
| { | |
| name: "8", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model(64000)", | |
| inputJSON: `{"model":"level-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 9: Budget 0 → clamped to minimal (ZeroAllowed=false) | |
| { | |
| name: "9", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model(0)", | |
| inputJSON: `{"model":"level-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "minimal", | |
| expectErr: false, | |
| }, | |
| // Case 10: Budget -1 → auto → DynamicAllowed=false → medium (mid-range) | |
| { | |
| name: "10", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model(-1)", | |
| inputJSON: `{"model":"level-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 11: Claude source no suffix → passthrough (no thinking) | |
| { | |
| name: "11", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 12: Budget 8192 → medium | |
| { | |
| name: "12", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model(8192)", | |
| inputJSON: `{"model":"level-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 13: Budget 64000 → clamped to high | |
| { | |
| name: "13", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model(64000)", | |
| inputJSON: `{"model":"level-model(64000)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 14: Budget 0 → clamped to minimal (ZeroAllowed=false) | |
| { | |
| name: "14", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model(0)", | |
| inputJSON: `{"model":"level-model(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "minimal", | |
| expectErr: false, | |
| }, | |
| // Case 15: Budget -1 → auto → DynamicAllowed=false → medium (mid-range) | |
| { | |
| name: "15", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model(-1)", | |
| inputJSON: `{"model":"level-model(-1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // level-subset-model (Levels=low/high, ZeroAllowed=false, DynamicAllowed=false) | |
| // Case 16: Budget 8192 → medium → rounded down to low | |
| { | |
| name: "16", | |
| from: "gemini", | |
| to: "openai", | |
| model: "level-subset-model(8192)", | |
| inputJSON: `{"model":"level-subset-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "low", | |
| expectErr: false, | |
| }, | |
| // Case 17: Budget 1 → minimal → clamped to low (min supported) | |
| { | |
| name: "17", | |
| from: "claude", | |
| to: "gemini", | |
| model: "level-subset-model(1)", | |
| inputJSON: `{"model":"level-subset-model(1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "low", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // gemini-budget-model (Min=128, Max=20000, ZeroAllowed=false, DynamicAllowed=true) | |
| // Case 18: No suffix → passthrough | |
| { | |
| name: "18", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 19: Effort medium → 8192 | |
| { | |
| name: "19", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model(medium)", | |
| inputJSON: `{"model":"gemini-budget-model(medium)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 20: Effort xhigh → clamped to 20000 (max) | |
| { | |
| name: "20", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model(xhigh)", | |
| inputJSON: `{"model":"gemini-budget-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 21: Effort none → clamped to 128 (min) → includeThoughts=false | |
| { | |
| name: "21", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model(none)", | |
| inputJSON: `{"model":"gemini-budget-model(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "128", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 22: Effort auto → DynamicAllowed=true → -1 | |
| { | |
| name: "22", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model(auto)", | |
| inputJSON: `{"model":"gemini-budget-model(auto)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 23: Claude source no suffix → passthrough | |
| { | |
| name: "23", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 24: Budget 8192 → 8192 | |
| { | |
| name: "24", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model(8192)", | |
| inputJSON: `{"model":"gemini-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 25: Budget 64000 → clamped to 20000 (max) | |
| { | |
| name: "25", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model(64000)", | |
| inputJSON: `{"model":"gemini-budget-model(64000)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 26: Budget 0 → clamped to 128 (min) → includeThoughts=false | |
| { | |
| name: "26", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model(0)", | |
| inputJSON: `{"model":"gemini-budget-model(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "128", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 27: Budget -1 → DynamicAllowed=true → -1 | |
| { | |
| name: "27", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model(-1)", | |
| inputJSON: `{"model":"gemini-budget-model(-1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // gemini-mixed-model (Min=128, Max=32768, Levels=low/high, ZeroAllowed=false, DynamicAllowed=true) | |
| // Case 28: OpenAI source no suffix → passthrough | |
| { | |
| name: "28", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 29: Effort high → low/high supported → high | |
| { | |
| name: "29", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model(high)", | |
| inputJSON: `{"model":"gemini-mixed-model(high)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "high", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 30: Effort xhigh → not in low/high → error | |
| { | |
| name: "30", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model(xhigh)", | |
| inputJSON: `{"model":"gemini-mixed-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 31: Effort none → clamped to low (min supported) → includeThoughts=false | |
| { | |
| name: "31", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model(none)", | |
| inputJSON: `{"model":"gemini-mixed-model(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "low", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 32: Effort auto → DynamicAllowed=true → -1 (budget) | |
| { | |
| name: "32", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model(auto)", | |
| inputJSON: `{"model":"gemini-mixed-model(auto)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 33: Claude source no suffix → passthrough | |
| { | |
| name: "33", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 34: Budget 8192 → 8192 (keep budget) | |
| { | |
| name: "34", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model(8192)", | |
| inputJSON: `{"model":"gemini-mixed-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 35: Budget 64000 → clamped to 32768 (max) | |
| { | |
| name: "35", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model(64000)", | |
| inputJSON: `{"model":"gemini-mixed-model(64000)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "32768", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 36: Budget 0 → minimal → clamped to low (min level) → includeThoughts=false | |
| { | |
| name: "36", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model(0)", | |
| inputJSON: `{"model":"gemini-mixed-model(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "low", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 37: Budget -1 → DynamicAllowed=true → -1 (budget) | |
| { | |
| name: "37", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model(-1)", | |
| inputJSON: `{"model":"gemini-mixed-model(-1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // claude-budget-model (Min=1024, Max=128000, ZeroAllowed=true, DynamicAllowed=false) | |
| // Case 38: OpenAI source no suffix → passthrough | |
| { | |
| name: "38", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 39: Effort medium → 8192 | |
| { | |
| name: "39", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model(medium)", | |
| inputJSON: `{"model":"claude-budget-model(medium)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 40: Effort xhigh → clamped to 32768 (matrix value) | |
| { | |
| name: "40", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model(xhigh)", | |
| inputJSON: `{"model":"claude-budget-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "32768", | |
| expectErr: false, | |
| }, | |
| // Case 41: Effort none → ZeroAllowed=true → disabled | |
| { | |
| name: "41", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model(none)", | |
| inputJSON: `{"model":"claude-budget-model(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.type", | |
| expectValue: "disabled", | |
| expectErr: false, | |
| }, | |
| // Case 42: Effort auto → DynamicAllowed=false → 64512 (mid-range) | |
| { | |
| name: "42", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model(auto)", | |
| inputJSON: `{"model":"claude-budget-model(auto)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "64512", | |
| expectErr: false, | |
| }, | |
| // Case 43: Gemini source no suffix → passthrough | |
| { | |
| name: "43", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 44: Budget 8192 → 8192 | |
| { | |
| name: "44", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model(8192)", | |
| inputJSON: `{"model":"claude-budget-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 45: Budget 200000 → clamped to 128000 (max) | |
| { | |
| name: "45", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model(200000)", | |
| inputJSON: `{"model":"claude-budget-model(200000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "128000", | |
| expectErr: false, | |
| }, | |
| // Case 46: Budget 0 → ZeroAllowed=true → disabled | |
| { | |
| name: "46", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model(0)", | |
| inputJSON: `{"model":"claude-budget-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "thinking.type", | |
| expectValue: "disabled", | |
| expectErr: false, | |
| }, | |
| // Case 47: Budget -1 → auto → DynamicAllowed=false → 64512 (mid-range) | |
| { | |
| name: "47", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model(-1)", | |
| inputJSON: `{"model":"claude-budget-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "64512", | |
| expectErr: false, | |
| }, | |
| // antigravity-budget-model (Min=128, Max=20000, ZeroAllowed=true, DynamicAllowed=true) | |
| // Case 48: Gemini to Antigravity no suffix → passthrough | |
| { | |
| name: "48", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 49: Effort medium → 8192 | |
| { | |
| name: "49", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(medium)", | |
| inputJSON: `{"model":"antigravity-budget-model(medium)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 50: Effort xhigh → clamped to 20000 (max) | |
| { | |
| name: "50", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(xhigh)", | |
| inputJSON: `{"model":"antigravity-budget-model(xhigh)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 51: Effort none → ZeroAllowed=true → 0 → includeThoughts=false | |
| { | |
| name: "51", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(none)", | |
| inputJSON: `{"model":"antigravity-budget-model(none)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "0", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 52: Effort auto → DynamicAllowed=true → -1 | |
| { | |
| name: "52", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(auto)", | |
| inputJSON: `{"model":"antigravity-budget-model(auto)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 53: Claude to Antigravity no suffix → passthrough | |
| { | |
| name: "53", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 54: Budget 8192 → 8192 | |
| { | |
| name: "54", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(8192)", | |
| inputJSON: `{"model":"antigravity-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 55: Budget 64000 → clamped to 20000 (max) | |
| { | |
| name: "55", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(64000)", | |
| inputJSON: `{"model":"antigravity-budget-model(64000)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 56: Budget 0 → ZeroAllowed=true → 0 → includeThoughts=false | |
| { | |
| name: "56", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(0)", | |
| inputJSON: `{"model":"antigravity-budget-model(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "0", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 57: Budget -1 → DynamicAllowed=true → -1 | |
| { | |
| name: "57", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(-1)", | |
| inputJSON: `{"model":"antigravity-budget-model(-1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // no-thinking-model (Thinking=nil) | |
| // Case 58: No thinking support → no configuration | |
| { | |
| name: "58", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 59: Budget 8192 → no thinking support → suffix stripped → no configuration | |
| { | |
| name: "59", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model(8192)", | |
| inputJSON: `{"model":"no-thinking-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 60: Budget 0 → suffix stripped → no configuration | |
| { | |
| name: "60", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model(0)", | |
| inputJSON: `{"model":"no-thinking-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 61: Budget -1 → suffix stripped → no configuration | |
| { | |
| name: "61", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model(-1)", | |
| inputJSON: `{"model":"no-thinking-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 62: Claude source no suffix → no configuration | |
| { | |
| name: "62", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 63: Budget 8192 → suffix stripped → no configuration | |
| { | |
| name: "63", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model(8192)", | |
| inputJSON: `{"model":"no-thinking-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 64: Budget 0 → suffix stripped → no configuration | |
| { | |
| name: "64", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model(0)", | |
| inputJSON: `{"model":"no-thinking-model(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 65: Budget -1 → suffix stripped → no configuration | |
| { | |
| name: "65", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model(-1)", | |
| inputJSON: `{"model":"no-thinking-model(-1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // user-defined-model (UserDefined=true, Thinking=nil) | |
| // Case 66: User defined model no suffix → passthrough | |
| { | |
| name: "66", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 67: Budget 8192 → passthrough logic → medium | |
| { | |
| name: "67", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model(8192)", | |
| inputJSON: `{"model":"user-defined-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 68: Budget 64000 → passthrough logic → xhigh | |
| { | |
| name: "68", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model(64000)", | |
| inputJSON: `{"model":"user-defined-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 69: Budget 0 → passthrough logic → none | |
| { | |
| name: "69", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model(0)", | |
| inputJSON: `{"model":"user-defined-model(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 70: Budget -1 → passthrough logic → auto | |
| { | |
| name: "70", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model(-1)", | |
| inputJSON: `{"model":"user-defined-model(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "auto", | |
| expectErr: false, | |
| }, | |
| // Case 71: Claude to Codex no suffix → injected default → medium | |
| { | |
| name: "71", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 72: Budget 8192 → passthrough logic → medium | |
| { | |
| name: "72", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model(8192)", | |
| inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 73: Budget 64000 → passthrough logic → xhigh | |
| { | |
| name: "73", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model(64000)", | |
| inputJSON: `{"model":"user-defined-model(64000)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 74: Budget 0 → passthrough logic → none | |
| { | |
| name: "74", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model(0)", | |
| inputJSON: `{"model":"user-defined-model(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 75: Budget -1 → passthrough logic → auto | |
| { | |
| name: "75", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model(-1)", | |
| inputJSON: `{"model":"user-defined-model(-1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "auto", | |
| expectErr: false, | |
| }, | |
| // Case 76: OpenAI to Gemini budget 8192 → passthrough → 8192 | |
| { | |
| name: "76", | |
| from: "openai", | |
| to: "gemini", | |
| model: "user-defined-model(8192)", | |
| inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 77: OpenAI to Claude budget 8192 → passthrough → 8192 | |
| { | |
| name: "77", | |
| from: "openai", | |
| to: "claude", | |
| model: "user-defined-model(8192)", | |
| inputJSON: `{"model":"user-defined-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 78: OpenAI-Response to Gemini budget 8192 → passthrough → 8192 | |
| { | |
| name: "78", | |
| from: "openai-response", | |
| to: "gemini", | |
| model: "user-defined-model(8192)", | |
| inputJSON: `{"model":"user-defined-model(8192)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 79: OpenAI-Response to Claude budget 8192 → passthrough → 8192 | |
| { | |
| name: "79", | |
| from: "openai-response", | |
| to: "claude", | |
| model: "user-defined-model(8192)", | |
| inputJSON: `{"model":"user-defined-model(8192)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Same-protocol passthrough tests (80-89) | |
| // Case 80: OpenAI to OpenAI, level high → passthrough reasoning_effort | |
| { | |
| name: "80", | |
| from: "openai", | |
| to: "openai", | |
| model: "level-model(high)", | |
| inputJSON: `{"model":"level-model(high)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 81: OpenAI to OpenAI, level xhigh → out of range error | |
| { | |
| name: "81", | |
| from: "openai", | |
| to: "openai", | |
| model: "level-model(xhigh)", | |
| inputJSON: `{"model":"level-model(xhigh)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 82: OpenAI-Response to Codex, level high → passthrough reasoning.effort | |
| { | |
| name: "82", | |
| from: "openai-response", | |
| to: "codex", | |
| model: "level-model(high)", | |
| inputJSON: `{"model":"level-model(high)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 83: OpenAI-Response to Codex, level xhigh → out of range error | |
| { | |
| name: "83", | |
| from: "openai-response", | |
| to: "codex", | |
| model: "level-model(xhigh)", | |
| inputJSON: `{"model":"level-model(xhigh)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 84: Gemini to Gemini, budget 8192 → passthrough thinkingBudget | |
| { | |
| name: "84", | |
| from: "gemini", | |
| to: "gemini", | |
| model: "gemini-budget-model(8192)", | |
| inputJSON: `{"model":"gemini-budget-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 85: Gemini to Gemini, budget 64000 → clamped to Max | |
| { | |
| name: "85", | |
| from: "gemini", | |
| to: "gemini", | |
| model: "gemini-budget-model(64000)", | |
| inputJSON: `{"model":"gemini-budget-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 86: Claude to Claude, budget 8192 → passthrough thinking.budget_tokens | |
| { | |
| name: "86", | |
| from: "claude", | |
| to: "claude", | |
| model: "claude-budget-model(8192)", | |
| inputJSON: `{"model":"claude-budget-model(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 87: Claude to Claude, budget 200000 → clamped to Max | |
| { | |
| name: "87", | |
| from: "claude", | |
| to: "claude", | |
| model: "claude-budget-model(200000)", | |
| inputJSON: `{"model":"claude-budget-model(200000)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "128000", | |
| expectErr: false, | |
| }, | |
| // Case 88: Gemini-CLI to Antigravity, budget 8192 → passthrough thinkingBudget | |
| { | |
| name: "88", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(8192)", | |
| inputJSON: `{"model":"antigravity-budget-model(8192)","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 89: Gemini-CLI to Antigravity, budget 64000 → clamped to Max | |
| { | |
| name: "89", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "antigravity-budget-model(64000)", | |
| inputJSON: `{"model":"antigravity-budget-model(64000)","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // iflow tests: glm-test and minimax-test (Cases 90-105) | |
| // glm-test (from: openai, claude) | |
| // Case 90: OpenAI to iflow, no suffix → passthrough | |
| { | |
| name: "90", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 91: OpenAI to iflow, (medium) → enable_thinking=true | |
| { | |
| name: "91", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test(medium)", | |
| inputJSON: `{"model":"glm-test(medium)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 92: OpenAI to iflow, (auto) → enable_thinking=true | |
| { | |
| name: "92", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test(auto)", | |
| inputJSON: `{"model":"glm-test(auto)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 93: OpenAI to iflow, (none) → enable_thinking=false | |
| { | |
| name: "93", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test(none)", | |
| inputJSON: `{"model":"glm-test(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // Case 94: Claude to iflow, no suffix → passthrough | |
| { | |
| name: "94", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 95: Claude to iflow, (8192) → enable_thinking=true | |
| { | |
| name: "95", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test(8192)", | |
| inputJSON: `{"model":"glm-test(8192)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 96: Claude to iflow, (-1) → enable_thinking=true | |
| { | |
| name: "96", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test(-1)", | |
| inputJSON: `{"model":"glm-test(-1)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 97: Claude to iflow, (0) → enable_thinking=false | |
| { | |
| name: "97", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test(0)", | |
| inputJSON: `{"model":"glm-test(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // minimax-test (from: openai, gemini) | |
| // Case 98: OpenAI to iflow, no suffix → passthrough | |
| { | |
| name: "98", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 99: OpenAI to iflow, (medium) → reasoning_split=true | |
| { | |
| name: "99", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test(medium)", | |
| inputJSON: `{"model":"minimax-test(medium)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 100: OpenAI to iflow, (auto) → reasoning_split=true | |
| { | |
| name: "100", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test(auto)", | |
| inputJSON: `{"model":"minimax-test(auto)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 101: OpenAI to iflow, (none) → reasoning_split=false | |
| { | |
| name: "101", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test(none)", | |
| inputJSON: `{"model":"minimax-test(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_split", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // Case 102: Gemini to iflow, no suffix → passthrough | |
| { | |
| name: "102", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 103: Gemini to iflow, (8192) → reasoning_split=true | |
| { | |
| name: "103", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test(8192)", | |
| inputJSON: `{"model":"minimax-test(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 104: Gemini to iflow, (-1) → reasoning_split=true | |
| { | |
| name: "104", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test(-1)", | |
| inputJSON: `{"model":"minimax-test(-1)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 105: Gemini to iflow, (0) → reasoning_split=false | |
| { | |
| name: "105", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test(0)", | |
| inputJSON: `{"model":"minimax-test(0)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning_split", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // Gemini Family Cross-Channel Consistency (Cases 106-114) | |
| // Tests that gemini/gemini-cli/antigravity as same API family should have consistent validation behavior | |
| // Case 106: Gemini to Antigravity, budget 64000 (suffix) → clamped to Max | |
| { | |
| name: "106", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "gemini-budget-model(64000)", | |
| inputJSON: `{"model":"gemini-budget-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 107: Gemini to Gemini-CLI, budget 64000 (suffix) → clamped to Max | |
| { | |
| name: "107", | |
| from: "gemini", | |
| to: "gemini-cli", | |
| model: "gemini-budget-model(64000)", | |
| inputJSON: `{"model":"gemini-budget-model(64000)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 108: Gemini-CLI to Antigravity, budget 64000 (suffix) → clamped to Max | |
| { | |
| name: "108", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "gemini-budget-model(64000)", | |
| inputJSON: `{"model":"gemini-budget-model(64000)","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 109: Gemini-CLI to Gemini, budget 64000 (suffix) → clamped to Max | |
| { | |
| name: "109", | |
| from: "gemini-cli", | |
| to: "gemini", | |
| model: "gemini-budget-model(64000)", | |
| inputJSON: `{"model":"gemini-budget-model(64000)","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 110: Gemini to Antigravity, budget 8192 → passthrough (normal value) | |
| { | |
| name: "110", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "gemini-budget-model(8192)", | |
| inputJSON: `{"model":"gemini-budget-model(8192)","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 111: Gemini-CLI to Antigravity, budget 8192 → passthrough (normal value) | |
| { | |
| name: "111", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "gemini-budget-model(8192)", | |
| inputJSON: `{"model":"gemini-budget-model(8192)","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // GitHub Copilot tests: gpt-5, gpt-5.1, gpt-5.2 (Levels=low/medium/high, some with none/xhigh) | |
| // Testing /chat/completions endpoint (openai format) - with suffix | |
| // Case 112: OpenAI to gpt-5, level high → high | |
| { | |
| name: "112", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5(high)", | |
| inputJSON: `{"model":"gpt-5(high)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 113: OpenAI to gpt-5, level none → clamped to low (ZeroAllowed=false) | |
| { | |
| name: "113", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5(none)", | |
| inputJSON: `{"model":"gpt-5(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "low", | |
| expectErr: false, | |
| }, | |
| // Case 114: OpenAI to gpt-5.1, level none → none (ZeroAllowed=true) | |
| { | |
| name: "114", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5.1(none)", | |
| inputJSON: `{"model":"gpt-5.1(none)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 115: OpenAI to gpt-5.2, level xhigh → xhigh | |
| { | |
| name: "115", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5.2(xhigh)", | |
| inputJSON: `{"model":"gpt-5.2(xhigh)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 116: OpenAI to gpt-5, level xhigh (out of range) → error | |
| { | |
| name: "116", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5(xhigh)", | |
| inputJSON: `{"model":"gpt-5(xhigh)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 117: Claude to gpt-5.1, budget 0 → none (ZeroAllowed=true) | |
| { | |
| name: "117", | |
| from: "claude", | |
| to: "github-copilot", | |
| model: "gpt-5.1(0)", | |
| inputJSON: `{"model":"gpt-5.1(0)","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // GitHub Copilot tests: /responses endpoint (codex format) - with suffix | |
| // Case 118: OpenAI-Response to gpt-5-codex, level high → high | |
| { | |
| name: "118", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5-codex(high)", | |
| inputJSON: `{"model":"gpt-5-codex(high)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 119: OpenAI-Response to gpt-5.2-codex, level xhigh → xhigh | |
| { | |
| name: "119", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5.2-codex(xhigh)", | |
| inputJSON: `{"model":"gpt-5.2-codex(xhigh)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 120: OpenAI-Response to gpt-5.2-codex, level none → none | |
| { | |
| name: "120", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5.2-codex(none)", | |
| inputJSON: `{"model":"gpt-5.2-codex(none)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 121: OpenAI-Response to gpt-5-codex, level none → clamped to low (ZeroAllowed=false) | |
| { | |
| name: "121", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5-codex(none)", | |
| inputJSON: `{"model":"gpt-5-codex(none)","input":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "low", | |
| expectErr: false, | |
| }, | |
| } | |
| runThinkingTests(t, cases) | |
| } | |
| // TestThinkingE2EMatrix_Body tests the thinking configuration transformation using request body parameters. | |
| // Data flow: Input JSON with thinking params → TranslateRequest → ApplyThinking → Validate Output | |
| func TestThinkingE2EMatrix_Body(t *testing.T) { | |
| reg := registry.GetGlobalRegistry() | |
| uid := fmt.Sprintf("thinking-e2e-body-%d", time.Now().UnixNano()) | |
| reg.RegisterClient(uid, "test", getTestModels()) | |
| defer reg.UnregisterClient(uid) | |
| cases := []thinkingTestCase{ | |
| // level-model (Levels=minimal/low/medium/high, ZeroAllowed=false, DynamicAllowed=false) | |
| // Case 1: No param → injected default → medium | |
| { | |
| name: "1", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 2: reasoning_effort=medium → medium | |
| { | |
| name: "2", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 3: reasoning_effort=xhigh → out of range error | |
| { | |
| name: "3", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 4: reasoning_effort=none → clamped to minimal | |
| { | |
| name: "4", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "minimal", | |
| expectErr: false, | |
| }, | |
| // Case 5: reasoning_effort=auto → medium (DynamicAllowed=false) | |
| { | |
| name: "5", | |
| from: "openai", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 6: No param from gemini → injected default → medium | |
| { | |
| name: "6", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 7: thinkingBudget=8192 → medium | |
| { | |
| name: "7", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 8: thinkingBudget=64000 → clamped to high | |
| { | |
| name: "8", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 9: thinkingBudget=0 → clamped to minimal | |
| { | |
| name: "9", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "minimal", | |
| expectErr: false, | |
| }, | |
| // Case 10: thinkingBudget=-1 → medium (DynamicAllowed=false) | |
| { | |
| name: "10", | |
| from: "gemini", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 11: Claude no param → passthrough (no thinking) | |
| { | |
| name: "11", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 12: thinking.budget_tokens=8192 → medium | |
| { | |
| name: "12", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 13: thinking.budget_tokens=64000 → clamped to high | |
| { | |
| name: "13", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 14: thinking.budget_tokens=0 → clamped to minimal | |
| { | |
| name: "14", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "minimal", | |
| expectErr: false, | |
| }, | |
| // Case 15: thinking.budget_tokens=-1 → medium (DynamicAllowed=false) | |
| { | |
| name: "15", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // level-subset-model (Levels=low/high, ZeroAllowed=false, DynamicAllowed=false) | |
| // Case 16: thinkingBudget=8192 → medium → rounded down to low | |
| { | |
| name: "16", | |
| from: "gemini", | |
| to: "openai", | |
| model: "level-subset-model", | |
| inputJSON: `{"model":"level-subset-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "low", | |
| expectErr: false, | |
| }, | |
| // Case 17: thinking.budget_tokens=1 → minimal → clamped to low | |
| { | |
| name: "17", | |
| from: "claude", | |
| to: "gemini", | |
| model: "level-subset-model", | |
| inputJSON: `{"model":"level-subset-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":1}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "low", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // gemini-budget-model (Min=128, Max=20000, ZeroAllowed=false, DynamicAllowed=true) | |
| // Case 18: No param → passthrough | |
| { | |
| name: "18", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 19: reasoning_effort=medium → 8192 | |
| { | |
| name: "19", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 20: reasoning_effort=xhigh → clamped to 20000 | |
| { | |
| name: "20", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 21: reasoning_effort=none → clamped to 128 → includeThoughts=false | |
| { | |
| name: "21", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "128", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 22: reasoning_effort=auto → -1 (DynamicAllowed=true) | |
| { | |
| name: "22", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 23: Claude no param → passthrough | |
| { | |
| name: "23", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 24: thinking.budget_tokens=8192 → 8192 | |
| { | |
| name: "24", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 25: thinking.budget_tokens=64000 → clamped to 20000 | |
| { | |
| name: "25", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 26: thinking.budget_tokens=0 → clamped to 128 → includeThoughts=false | |
| { | |
| name: "26", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "128", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 27: thinking.budget_tokens=-1 → -1 (DynamicAllowed=true) | |
| { | |
| name: "27", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // gemini-mixed-model (Min=128, Max=32768, Levels=low/high, ZeroAllowed=false, DynamicAllowed=true) | |
| // Case 28: No param → passthrough | |
| { | |
| name: "28", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 29: reasoning_effort=high → high | |
| { | |
| name: "29", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "high", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 30: reasoning_effort=xhigh → error (not in low/high) | |
| { | |
| name: "30", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 31: reasoning_effort=none → clamped to low → includeThoughts=false | |
| { | |
| name: "31", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "low", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 32: reasoning_effort=auto → -1 (DynamicAllowed=true) | |
| { | |
| name: "32", | |
| from: "openai", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 33: Claude no param → passthrough | |
| { | |
| name: "33", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 34: thinking.budget_tokens=8192 → 8192 (keeps budget) | |
| { | |
| name: "34", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 35: thinking.budget_tokens=64000 → clamped to 32768 (keeps budget) | |
| { | |
| name: "35", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "32768", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 36: thinking.budget_tokens=0 → clamped to low → includeThoughts=false | |
| { | |
| name: "36", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "low", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 37: thinking.budget_tokens=-1 → -1 (DynamicAllowed=true) | |
| { | |
| name: "37", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // claude-budget-model (Min=1024, Max=128000, ZeroAllowed=true, DynamicAllowed=false) | |
| // Case 38: No param → passthrough | |
| { | |
| name: "38", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 39: reasoning_effort=medium → 8192 | |
| { | |
| name: "39", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 40: reasoning_effort=xhigh → clamped to 32768 | |
| { | |
| name: "40", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "32768", | |
| expectErr: false, | |
| }, | |
| // Case 41: reasoning_effort=none → disabled | |
| { | |
| name: "41", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "thinking.type", | |
| expectValue: "disabled", | |
| expectErr: false, | |
| }, | |
| // Case 42: reasoning_effort=auto → 64512 (mid-range) | |
| { | |
| name: "42", | |
| from: "openai", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "64512", | |
| expectErr: false, | |
| }, | |
| // Case 43: Gemini no param → passthrough | |
| { | |
| name: "43", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 44: thinkingBudget=8192 → 8192 | |
| { | |
| name: "44", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 45: thinkingBudget=200000 → clamped to 128000 | |
| { | |
| name: "45", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":200000}}}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "128000", | |
| expectErr: false, | |
| }, | |
| // Case 46: thinkingBudget=0 → disabled | |
| { | |
| name: "46", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, | |
| expectField: "thinking.type", | |
| expectValue: "disabled", | |
| expectErr: false, | |
| }, | |
| // Case 47: thinkingBudget=-1 → 64512 (mid-range) | |
| { | |
| name: "47", | |
| from: "gemini", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "64512", | |
| expectErr: false, | |
| }, | |
| // antigravity-budget-model (Min=128, Max=20000, ZeroAllowed=true, DynamicAllowed=true) | |
| // Case 48: Gemini no param → passthrough | |
| { | |
| name: "48", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 49: thinkingLevel=medium → 8192 | |
| { | |
| name: "49", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"medium"}}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 50: thinkingLevel=xhigh → clamped to 20000 | |
| { | |
| name: "50", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"xhigh"}}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 51: thinkingLevel=none → 0 (ZeroAllowed=true) | |
| { | |
| name: "51", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingLevel":"none"}}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "0", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 52: thinkingBudget=-1 → -1 (DynamicAllowed=true) | |
| { | |
| name: "52", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 53: Claude no param → passthrough | |
| { | |
| name: "53", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 54: thinking.budget_tokens=8192 → 8192 | |
| { | |
| name: "54", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 55: thinking.budget_tokens=64000 → clamped to 20000 | |
| { | |
| name: "55", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 56: thinking.budget_tokens=0 → 0 (ZeroAllowed=true) | |
| { | |
| name: "56", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "0", | |
| includeThoughts: "false", | |
| expectErr: false, | |
| }, | |
| // Case 57: thinking.budget_tokens=-1 → -1 (DynamicAllowed=true) | |
| { | |
| name: "57", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "-1", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // no-thinking-model (Thinking=nil) | |
| // Case 58: Gemini no param → passthrough | |
| { | |
| name: "58", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 59: thinkingBudget=8192 → stripped | |
| { | |
| name: "59", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 60: thinkingBudget=0 → stripped | |
| { | |
| name: "60", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 61: thinkingBudget=-1 → stripped | |
| { | |
| name: "61", | |
| from: "gemini", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 62: Claude no param → passthrough | |
| { | |
| name: "62", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 63: thinking.budget_tokens=8192 → stripped | |
| { | |
| name: "63", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 64: thinking.budget_tokens=0 → stripped | |
| { | |
| name: "64", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 65: thinking.budget_tokens=-1 → stripped | |
| { | |
| name: "65", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // user-defined-model (UserDefined=true, Thinking=nil) | |
| // Case 66: Gemini no param → passthrough | |
| { | |
| name: "66", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 67: thinkingBudget=8192 → medium | |
| { | |
| name: "67", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 68: thinkingBudget=64000 → xhigh (passthrough) | |
| { | |
| name: "68", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 69: thinkingBudget=0 → none | |
| { | |
| name: "69", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 70: thinkingBudget=-1 → auto | |
| { | |
| name: "70", | |
| from: "gemini", | |
| to: "openai", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "auto", | |
| expectErr: false, | |
| }, | |
| // Case 71: Claude no param → injected default → medium | |
| { | |
| name: "71", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 72: thinking.budget_tokens=8192 → medium | |
| { | |
| name: "72", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "medium", | |
| expectErr: false, | |
| }, | |
| // Case 73: thinking.budget_tokens=64000 → xhigh (passthrough) | |
| { | |
| name: "73", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":64000}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 74: thinking.budget_tokens=0 → none | |
| { | |
| name: "74", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 75: thinking.budget_tokens=-1 → auto | |
| { | |
| name: "75", | |
| from: "claude", | |
| to: "codex", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "auto", | |
| expectErr: false, | |
| }, | |
| // Case 76: OpenAI reasoning_effort=medium to Gemini → 8192 | |
| { | |
| name: "76", | |
| from: "openai", | |
| to: "gemini", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 77: OpenAI reasoning_effort=medium to Claude → 8192 | |
| { | |
| name: "77", | |
| from: "openai", | |
| to: "claude", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 78: OpenAI-Response reasoning.effort=medium to Gemini → 8192 | |
| { | |
| name: "78", | |
| from: "openai-response", | |
| to: "gemini", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"medium"}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 79: OpenAI-Response reasoning.effort=medium to Claude → 8192 | |
| { | |
| name: "79", | |
| from: "openai-response", | |
| to: "claude", | |
| model: "user-defined-model", | |
| inputJSON: `{"model":"user-defined-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"medium"}}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Same-protocol passthrough tests (80-89) | |
| // Case 80: OpenAI to OpenAI, reasoning_effort=high → passthrough | |
| { | |
| name: "80", | |
| from: "openai", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 81: OpenAI to OpenAI, reasoning_effort=xhigh → out of range error | |
| { | |
| name: "81", | |
| from: "openai", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 82: OpenAI-Response to Codex, reasoning.effort=high → passthrough | |
| { | |
| name: "82", | |
| from: "openai-response", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"high"}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 83: OpenAI-Response to Codex, reasoning.effort=xhigh → out of range error | |
| { | |
| name: "83", | |
| from: "openai-response", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"xhigh"}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 84: Gemini to Gemini, thinkingBudget=8192 → passthrough | |
| { | |
| name: "84", | |
| from: "gemini", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 85: Gemini to Gemini, thinkingBudget=64000 → exceeds Max error | |
| { | |
| name: "85", | |
| from: "gemini", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 86: Claude to Claude, thinking.budget_tokens=8192 → passthrough | |
| { | |
| name: "86", | |
| from: "claude", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "thinking.budget_tokens", | |
| expectValue: "8192", | |
| expectErr: false, | |
| }, | |
| // Case 87: Claude to Claude, thinking.budget_tokens=200000 → exceeds Max error | |
| { | |
| name: "87", | |
| from: "claude", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":200000}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 88: Gemini-CLI to Antigravity, thinkingBudget=8192 → passthrough | |
| { | |
| name: "88", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 89: Gemini-CLI to Antigravity, thinkingBudget=64000 → exceeds Max error | |
| { | |
| name: "89", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // iflow tests: glm-test and minimax-test (Cases 90-105) | |
| // glm-test (from: openai, claude) | |
| // Case 90: OpenAI to iflow, no param → passthrough | |
| { | |
| name: "90", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 91: OpenAI to iflow, reasoning_effort=medium → enable_thinking=true | |
| { | |
| name: "91", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 92: OpenAI to iflow, reasoning_effort=auto → enable_thinking=true | |
| { | |
| name: "92", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 93: OpenAI to iflow, reasoning_effort=none → enable_thinking=false | |
| { | |
| name: "93", | |
| from: "openai", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // Case 94: Claude to iflow, no param → passthrough | |
| { | |
| name: "94", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 95: Claude to iflow, thinking.budget_tokens=8192 → enable_thinking=true | |
| { | |
| name: "95", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":8192}}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 96: Claude to iflow, thinking.budget_tokens=-1 → enable_thinking=true | |
| { | |
| name: "96", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":-1}}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 97: Claude to iflow, thinking.budget_tokens=0 → enable_thinking=false | |
| { | |
| name: "97", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // minimax-test (from: openai, gemini) | |
| // Case 98: OpenAI to iflow, no param → passthrough | |
| { | |
| name: "98", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 99: OpenAI to iflow, reasoning_effort=medium → reasoning_split=true | |
| { | |
| name: "99", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"medium"}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 100: OpenAI to iflow, reasoning_effort=auto → reasoning_split=true | |
| { | |
| name: "100", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"auto"}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 101: OpenAI to iflow, reasoning_effort=none → reasoning_split=false | |
| { | |
| name: "101", | |
| from: "openai", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "reasoning_split", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // Case 102: Gemini to iflow, no param → passthrough | |
| { | |
| name: "102", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}]}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| // Case 103: Gemini to iflow, thinkingBudget=8192 → reasoning_split=true | |
| { | |
| name: "103", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 104: Gemini to iflow, thinkingBudget=-1 → reasoning_split=true | |
| { | |
| name: "104", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":-1}}}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // Case 105: Gemini to iflow, thinkingBudget=0 → reasoning_split=false | |
| { | |
| name: "105", | |
| from: "gemini", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`, | |
| expectField: "reasoning_split", | |
| expectValue: "false", | |
| expectErr: false, | |
| }, | |
| // Gemini Family Cross-Channel Consistency (Cases 106-114) | |
| // Tests that gemini/gemini-cli/antigravity as same API family should have consistent validation behavior | |
| // Case 106: Gemini to Antigravity, thinkingBudget=64000 → exceeds Max error (same family strict validation) | |
| { | |
| name: "106", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 107: Gemini to Gemini-CLI, thinkingBudget=64000 → exceeds Max error (same family strict validation) | |
| { | |
| name: "107", | |
| from: "gemini", | |
| to: "gemini-cli", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 108: Gemini-CLI to Antigravity, thinkingBudget=64000 → exceeds Max error (same family strict validation) | |
| { | |
| name: "108", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 109: Gemini-CLI to Gemini, thinkingBudget=64000 → exceeds Max error (same family strict validation) | |
| { | |
| name: "109", | |
| from: "gemini-cli", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":64000}}}}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 110: Gemini to Antigravity, thinkingBudget=8192 → passthrough (normal value) | |
| { | |
| name: "110", | |
| from: "gemini", | |
| to: "antigravity", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // Case 111: Gemini-CLI to Antigravity, thinkingBudget=8192 → passthrough (normal value) | |
| { | |
| name: "111", | |
| from: "gemini-cli", | |
| to: "antigravity", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":8192}}}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "8192", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // GitHub Copilot tests: gpt-5, gpt-5.1, gpt-5.2 (Levels=low/medium/high, some with none/xhigh) | |
| // Testing /chat/completions endpoint (openai format) - with body params | |
| // Case 112: OpenAI to gpt-5, reasoning_effort=high → high | |
| { | |
| name: "112", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5", | |
| inputJSON: `{"model":"gpt-5","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 113: OpenAI to gpt-5, reasoning_effort=none → clamped to low (ZeroAllowed=false) | |
| { | |
| name: "113", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5", | |
| inputJSON: `{"model":"gpt-5","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "low", | |
| expectErr: false, | |
| }, | |
| // Case 114: OpenAI to gpt-5.1, reasoning_effort=none → none (ZeroAllowed=true) | |
| { | |
| name: "114", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5.1", | |
| inputJSON: `{"model":"gpt-5.1","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 115: OpenAI to gpt-5.2, reasoning_effort=xhigh → xhigh | |
| { | |
| name: "115", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5.2", | |
| inputJSON: `{"model":"gpt-5.2","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 116: OpenAI to gpt-5, reasoning_effort=xhigh (out of range) → error | |
| { | |
| name: "116", | |
| from: "openai", | |
| to: "github-copilot", | |
| model: "gpt-5", | |
| inputJSON: `{"model":"gpt-5","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, | |
| expectField: "", | |
| expectErr: true, | |
| }, | |
| // Case 117: Claude to gpt-5.1, thinking.budget_tokens=0 → none (ZeroAllowed=true) | |
| { | |
| name: "117", | |
| from: "claude", | |
| to: "github-copilot", | |
| model: "gpt-5.1", | |
| inputJSON: `{"model":"gpt-5.1","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // GitHub Copilot tests: /responses endpoint (codex format) - with body params | |
| // Case 118: OpenAI-Response to gpt-5-codex, reasoning.effort=high → high | |
| { | |
| name: "118", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5-codex", | |
| inputJSON: `{"model":"gpt-5-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"high"}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // Case 119: OpenAI-Response to gpt-5.2-codex, reasoning.effort=xhigh → xhigh | |
| { | |
| name: "119", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5.2-codex", | |
| inputJSON: `{"model":"gpt-5.2-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"xhigh"}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "xhigh", | |
| expectErr: false, | |
| }, | |
| // Case 120: OpenAI-Response to gpt-5.2-codex, reasoning.effort=none → none | |
| { | |
| name: "120", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5.2-codex", | |
| inputJSON: `{"model":"gpt-5.2-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "none", | |
| expectErr: false, | |
| }, | |
| // Case 121: OpenAI-Response to gpt-5-codex, reasoning.effort=none → clamped to low (ZeroAllowed=false) | |
| { | |
| name: "121", | |
| from: "openai-response", | |
| to: "github-copilot", | |
| model: "gpt-5-codex", | |
| inputJSON: `{"model":"gpt-5-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "low", | |
| expectErr: false, | |
| }, | |
| } | |
| runThinkingTests(t, cases) | |
| } | |
| // TestThinkingE2EClaudeAdaptive_Body tests Claude thinking.type=adaptive extended body-only cases. | |
| // These cases validate that adaptive means "thinking enabled without explicit budget", and | |
| // cross-protocol conversion should resolve to target-model maximum thinking capability. | |
| func TestThinkingE2EClaudeAdaptive_Body(t *testing.T) { | |
| reg := registry.GetGlobalRegistry() | |
| uid := fmt.Sprintf("thinking-e2e-claude-adaptive-%d", time.Now().UnixNano()) | |
| reg.RegisterClient(uid, "test", getTestModels()) | |
| defer reg.UnregisterClient(uid) | |
| cases := []thinkingTestCase{ | |
| // A1: Claude adaptive to OpenAI level model -> highest supported level | |
| { | |
| name: "A1", | |
| from: "claude", | |
| to: "openai", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "reasoning_effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // A2: Claude adaptive to Gemini level subset model -> highest supported level | |
| { | |
| name: "A2", | |
| from: "claude", | |
| to: "gemini", | |
| model: "level-subset-model", | |
| inputJSON: `{"model":"level-subset-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "high", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // A3: Claude adaptive to Gemini budget model -> max budget | |
| { | |
| name: "A3", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-budget-model", | |
| inputJSON: `{"model":"gemini-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // A4: Claude adaptive to Gemini mixed model -> highest supported level | |
| { | |
| name: "A4", | |
| from: "claude", | |
| to: "gemini", | |
| model: "gemini-mixed-model", | |
| inputJSON: `{"model":"gemini-mixed-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "generationConfig.thinkingConfig.thinkingLevel", | |
| expectValue: "high", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // A5: Claude adaptive passthrough for same protocol | |
| { | |
| name: "A5", | |
| from: "claude", | |
| to: "claude", | |
| model: "claude-budget-model", | |
| inputJSON: `{"model":"claude-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "thinking.type", | |
| expectValue: "adaptive", | |
| expectErr: false, | |
| }, | |
| // A6: Claude adaptive to Antigravity budget model -> max budget | |
| { | |
| name: "A6", | |
| from: "claude", | |
| to: "antigravity", | |
| model: "antigravity-budget-model", | |
| inputJSON: `{"model":"antigravity-budget-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "request.generationConfig.thinkingConfig.thinkingBudget", | |
| expectValue: "20000", | |
| includeThoughts: "true", | |
| expectErr: false, | |
| }, | |
| // A7: Claude adaptive to iFlow GLM -> enabled boolean | |
| { | |
| name: "A7", | |
| from: "claude", | |
| to: "iflow", | |
| model: "glm-test", | |
| inputJSON: `{"model":"glm-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "chat_template_kwargs.enable_thinking", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // A8: Claude adaptive to iFlow MiniMax -> enabled boolean | |
| { | |
| name: "A8", | |
| from: "claude", | |
| to: "iflow", | |
| model: "minimax-test", | |
| inputJSON: `{"model":"minimax-test","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "reasoning_split", | |
| expectValue: "true", | |
| expectErr: false, | |
| }, | |
| // A9: Claude adaptive to Codex level model -> highest supported level | |
| { | |
| name: "A9", | |
| from: "claude", | |
| to: "codex", | |
| model: "level-model", | |
| inputJSON: `{"model":"level-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "reasoning.effort", | |
| expectValue: "high", | |
| expectErr: false, | |
| }, | |
| // A10: Claude adaptive on non-thinking model should still be stripped | |
| { | |
| name: "A10", | |
| from: "claude", | |
| to: "openai", | |
| model: "no-thinking-model", | |
| inputJSON: `{"model":"no-thinking-model","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"adaptive"}}`, | |
| expectField: "", | |
| expectErr: false, | |
| }, | |
| } | |
| runThinkingTests(t, cases) | |
| } | |
| // getTestModels returns the shared model definitions for E2E tests. | |
| func getTestModels() []*registry.ModelInfo { | |
| return []*registry.ModelInfo{ | |
| { | |
| ID: "level-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "openai", | |
| DisplayName: "Level Model", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"minimal", "low", "medium", "high"}, ZeroAllowed: false, DynamicAllowed: false}, | |
| }, | |
| { | |
| ID: "level-subset-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "gemini", | |
| DisplayName: "Level Subset Model", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "high"}, ZeroAllowed: false, DynamicAllowed: false}, | |
| }, | |
| { | |
| ID: "gemini-budget-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "gemini", | |
| DisplayName: "Gemini Budget Model", | |
| Thinking: ®istry.ThinkingSupport{Min: 128, Max: 20000, ZeroAllowed: false, DynamicAllowed: true}, | |
| }, | |
| { | |
| ID: "gemini-mixed-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "gemini", | |
| DisplayName: "Gemini Mixed Model", | |
| Thinking: ®istry.ThinkingSupport{Min: 128, Max: 32768, Levels: []string{"low", "high"}, ZeroAllowed: false, DynamicAllowed: true}, | |
| }, | |
| { | |
| ID: "claude-budget-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "claude", | |
| DisplayName: "Claude Budget Model", | |
| Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: true, DynamicAllowed: false}, | |
| }, | |
| { | |
| ID: "antigravity-budget-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "gemini-cli", | |
| DisplayName: "Antigravity Budget Model", | |
| Thinking: ®istry.ThinkingSupport{Min: 128, Max: 20000, ZeroAllowed: true, DynamicAllowed: true}, | |
| }, | |
| { | |
| ID: "no-thinking-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "openai", | |
| DisplayName: "No Thinking Model", | |
| Thinking: nil, | |
| }, | |
| { | |
| ID: "user-defined-model", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "openai", | |
| DisplayName: "User Defined Model", | |
| UserDefined: true, | |
| Thinking: nil, | |
| }, | |
| { | |
| ID: "glm-test", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "iflow", | |
| DisplayName: "GLM Test Model", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "auto", "minimal", "low", "medium", "high", "xhigh"}}, | |
| }, | |
| { | |
| ID: "minimax-test", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "test", | |
| Type: "iflow", | |
| DisplayName: "MiniMax Test Model", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "auto", "minimal", "low", "medium", "high", "xhigh"}}, | |
| }, | |
| { | |
| ID: "gpt-5", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "github-copilot", | |
| Type: "github-copilot", | |
| DisplayName: "GPT-5", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "medium", "high"}, ZeroAllowed: false, DynamicAllowed: false}, | |
| }, | |
| { | |
| ID: "gpt-5.1", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "github-copilot", | |
| Type: "github-copilot", | |
| DisplayName: "GPT-5.1", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "low", "medium", "high"}, ZeroAllowed: true, DynamicAllowed: false}, | |
| }, | |
| { | |
| ID: "gpt-5.2", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "github-copilot", | |
| Type: "github-copilot", | |
| DisplayName: "GPT-5.2", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}, ZeroAllowed: true, DynamicAllowed: false}, | |
| }, | |
| { | |
| ID: "gpt-5-codex", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "github-copilot", | |
| Type: "github-copilot", | |
| DisplayName: "GPT-5 Codex", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "medium", "high"}, ZeroAllowed: false, DynamicAllowed: false}, | |
| }, | |
| { | |
| ID: "gpt-5.2-codex", | |
| Object: "model", | |
| Created: 1700000000, | |
| OwnedBy: "github-copilot", | |
| Type: "github-copilot", | |
| DisplayName: "GPT-5.2 Codex", | |
| Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}, ZeroAllowed: true, DynamicAllowed: false}, | |
| }, | |
| } | |
| } | |
| // runThinkingTests runs thinking test cases using the real data flow path. | |
| func runThinkingTests(t *testing.T, cases []thinkingTestCase) { | |
| for _, tc := range cases { | |
| tc := tc | |
| testName := fmt.Sprintf("Case%s_%s->%s_%s", tc.name, tc.from, tc.to, tc.model) | |
| t.Run(testName, func(t *testing.T) { | |
| suffixResult := thinking.ParseSuffix(tc.model) | |
| baseModel := suffixResult.ModelName | |
| translateTo := tc.to | |
| applyTo := tc.to | |
| if tc.to == "iflow" { | |
| translateTo = "openai" | |
| applyTo = "iflow" | |
| } | |
| if tc.to == "github-copilot" { | |
| if tc.from == "openai-response" { | |
| translateTo = "codex" | |
| applyTo = "codex" | |
| } else { | |
| translateTo = "openai" | |
| applyTo = "openai" | |
| } | |
| } | |
| body := sdktranslator.TranslateRequest( | |
| sdktranslator.FromString(tc.from), | |
| sdktranslator.FromString(translateTo), | |
| baseModel, | |
| []byte(tc.inputJSON), | |
| true, | |
| ) | |
| if applyTo == "claude" { | |
| body, _ = sjson.SetBytes(body, "max_tokens", 200000) | |
| } | |
| body, err := thinking.ApplyThinking(body, tc.model, tc.from, applyTo, applyTo) | |
| if tc.expectErr { | |
| if err == nil { | |
| t.Fatalf("expected error but got none, body=%s", string(body)) | |
| } | |
| return | |
| } | |
| if err != nil { | |
| t.Fatalf("unexpected error: %v, body=%s", err, string(body)) | |
| } | |
| if tc.expectField == "" { | |
| var hasThinking bool | |
| switch tc.to { | |
| case "gemini": | |
| hasThinking = gjson.GetBytes(body, "generationConfig.thinkingConfig").Exists() | |
| case "gemini-cli": | |
| hasThinking = gjson.GetBytes(body, "request.generationConfig.thinkingConfig").Exists() | |
| case "antigravity": | |
| hasThinking = gjson.GetBytes(body, "request.generationConfig.thinkingConfig").Exists() | |
| case "claude": | |
| hasThinking = gjson.GetBytes(body, "thinking").Exists() | |
| case "openai": | |
| hasThinking = gjson.GetBytes(body, "reasoning_effort").Exists() | |
| case "codex": | |
| hasThinking = gjson.GetBytes(body, "reasoning.effort").Exists() || gjson.GetBytes(body, "reasoning").Exists() | |
| case "iflow": | |
| hasThinking = gjson.GetBytes(body, "chat_template_kwargs.enable_thinking").Exists() || gjson.GetBytes(body, "reasoning_split").Exists() | |
| } | |
| if hasThinking { | |
| t.Fatalf("expected no thinking field but found one, body=%s", string(body)) | |
| } | |
| return | |
| } | |
| val := gjson.GetBytes(body, tc.expectField) | |
| if !val.Exists() { | |
| t.Fatalf("expected field %s not found, body=%s", tc.expectField, string(body)) | |
| } | |
| actualValue := val.String() | |
| if val.Type == gjson.Number { | |
| actualValue = fmt.Sprintf("%d", val.Int()) | |
| } | |
| if actualValue != tc.expectValue { | |
| t.Fatalf("field %s: expected %q, got %q, body=%s", tc.expectField, tc.expectValue, actualValue, string(body)) | |
| } | |
| if tc.includeThoughts != "" && (tc.to == "gemini" || tc.to == "gemini-cli" || tc.to == "antigravity") { | |
| path := "generationConfig.thinkingConfig.includeThoughts" | |
| if tc.to == "gemini-cli" || tc.to == "antigravity" { | |
| path = "request.generationConfig.thinkingConfig.includeThoughts" | |
| } | |
| itVal := gjson.GetBytes(body, path) | |
| if !itVal.Exists() { | |
| t.Fatalf("expected includeThoughts field not found, body=%s", string(body)) | |
| } | |
| actual := fmt.Sprintf("%v", itVal.Bool()) | |
| if actual != tc.includeThoughts { | |
| t.Fatalf("includeThoughts: expected %s, got %s, body=%s", tc.includeThoughts, actual, string(body)) | |
| } | |
| } | |
| // Verify clear_thinking for iFlow GLM models when enable_thinking=true | |
| if tc.to == "iflow" && tc.expectField == "chat_template_kwargs.enable_thinking" && tc.expectValue == "true" { | |
| baseModel := thinking.ParseSuffix(tc.model).ModelName | |
| isGLM := strings.HasPrefix(strings.ToLower(baseModel), "glm") | |
| ctVal := gjson.GetBytes(body, "chat_template_kwargs.clear_thinking") | |
| if isGLM { | |
| if !ctVal.Exists() { | |
| t.Fatalf("expected clear_thinking field not found for GLM model, body=%s", string(body)) | |
| } | |
| if ctVal.Bool() != false { | |
| t.Fatalf("clear_thinking: expected false, got %v, body=%s", ctVal.Bool(), string(body)) | |
| } | |
| } else if ctVal.Exists() { | |
| t.Fatalf("expected no clear_thinking field for non-GLM enable_thinking model, body=%s", string(body)) | |
| } | |
| } | |
| }) | |
| } | |
| } | |