mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-06-22 03:42:51 +08:00
feat(thinking): remove thinkingConfig for ModeNone with zero budget and no level
- Updated Gemini, Gemini CLI, and Antigravity logic to delete `thinkingConfig` when `ModeNone` is set, `Budget=0`, and `Level` is empty. - Adjusted tests to validate this behavior across multiple scenarios and models with zero-allowed configurations. - Extended test cases for additional coverage of mixed-model behavior. Closes: #3138
This commit is contained in:
@@ -102,6 +102,10 @@ func (a *Applier) applyLevelFormat(body []byte, config thinking.ThinkingConfig)
|
||||
result, _ = sjson.DeleteBytes(result, "request.generationConfig.thinkingConfig.include_thoughts")
|
||||
|
||||
if config.Mode == thinking.ModeNone {
|
||||
if config.Budget == 0 && config.Level == "" {
|
||||
result, _ = sjson.DeleteBytes(result, "request.generationConfig.thinkingConfig")
|
||||
return result, nil
|
||||
}
|
||||
result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.includeThoughts", false)
|
||||
if config.Level != "" {
|
||||
result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.thinkingLevel", string(config.Level))
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
//
|
||||
// Gemini-specific behavior:
|
||||
// - Gemini 2.5: thinkingBudget format, flash series supports ZeroAllowed
|
||||
// - Gemini 3.x: thinkingLevel format, cannot be disabled
|
||||
// - Gemini 3.x: thinkingLevel format, disable by removing thinkingConfig when zero is allowed
|
||||
// - Use ThinkingSupport.Levels to decide output format
|
||||
type Applier struct{}
|
||||
|
||||
@@ -114,7 +114,7 @@ func (a *Applier) applyCompatible(body []byte, config thinking.ThinkingConfig) (
|
||||
|
||||
func (a *Applier) applyLevelFormat(body []byte, config thinking.ThinkingConfig) ([]byte, error) {
|
||||
// ModeNone semantics:
|
||||
// - ModeNone + Budget=0: completely disable thinking (not possible for Level-only models)
|
||||
// - ModeNone + Budget=0: remove thinkingConfig to disable thinking
|
||||
// - ModeNone + Budget>0: forced to think but hide output (includeThoughts=false)
|
||||
// ValidateConfig sets config.Level to the lowest level when ModeNone + Budget > 0.
|
||||
|
||||
@@ -126,6 +126,10 @@ func (a *Applier) applyLevelFormat(body []byte, config thinking.ThinkingConfig)
|
||||
result, _ = sjson.DeleteBytes(result, "generationConfig.thinkingConfig.include_thoughts")
|
||||
|
||||
if config.Mode == thinking.ModeNone {
|
||||
if config.Budget == 0 && config.Level == "" {
|
||||
result, _ = sjson.DeleteBytes(result, "generationConfig.thinkingConfig")
|
||||
return result, nil
|
||||
}
|
||||
result, _ = sjson.SetBytes(result, "generationConfig.thinkingConfig.includeThoughts", false)
|
||||
if config.Level != "" {
|
||||
result, _ = sjson.SetBytes(result, "generationConfig.thinkingConfig.thinkingLevel", string(config.Level))
|
||||
|
||||
@@ -87,6 +87,10 @@ func (a *Applier) applyLevelFormat(body []byte, config thinking.ThinkingConfig)
|
||||
result, _ = sjson.DeleteBytes(result, "request.generationConfig.thinkingConfig.include_thoughts")
|
||||
|
||||
if config.Mode == thinking.ModeNone {
|
||||
if config.Budget == 0 && config.Level == "" {
|
||||
result, _ = sjson.DeleteBytes(result, "request.generationConfig.thinkingConfig")
|
||||
return result, nil
|
||||
}
|
||||
result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.includeThoughts", false)
|
||||
if config.Level != "" {
|
||||
result, _ = sjson.SetBytes(result, "request.generationConfig.thinkingConfig.thinkingLevel", string(config.Level))
|
||||
|
||||
@@ -1515,6 +1515,66 @@ func TestThinkingE2EMatrix_Body(t *testing.T) {
|
||||
includeThoughts: "false",
|
||||
expectErr: false,
|
||||
},
|
||||
// Case 31A: reasoning_effort=none with zero allowed → delete thinkingConfig
|
||||
{
|
||||
name: "31A",
|
||||
from: "openai",
|
||||
to: "gemini",
|
||||
model: "gemini-zero-mixed-model",
|
||||
inputJSON: `{"model":"gemini-zero-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`,
|
||||
expectField: "",
|
||||
expectErr: false,
|
||||
},
|
||||
// Case 31B: reasoning_effort=none with zero allowed to Gemini CLI → delete thinkingConfig
|
||||
{
|
||||
name: "31B",
|
||||
from: "openai",
|
||||
to: "gemini-cli",
|
||||
model: "gemini-zero-mixed-model",
|
||||
inputJSON: `{"model":"gemini-zero-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`,
|
||||
expectField: "",
|
||||
expectErr: false,
|
||||
},
|
||||
// Case 31C: reasoning_effort=none with zero allowed to Antigravity → delete thinkingConfig
|
||||
{
|
||||
name: "31C",
|
||||
from: "openai",
|
||||
to: "antigravity",
|
||||
model: "gemini-zero-mixed-model",
|
||||
inputJSON: `{"model":"gemini-zero-mixed-model","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`,
|
||||
expectField: "",
|
||||
expectErr: false,
|
||||
},
|
||||
// Case 31D: reasoning.effort=none with zero allowed → delete thinkingConfig
|
||||
{
|
||||
name: "31D",
|
||||
from: "openai-response",
|
||||
to: "gemini",
|
||||
model: "gemini-zero-mixed-model",
|
||||
inputJSON: `{"model":"gemini-zero-mixed-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`,
|
||||
expectField: "",
|
||||
expectErr: false,
|
||||
},
|
||||
// Case 31E: reasoning.effort=none with zero allowed to Gemini CLI → delete thinkingConfig
|
||||
{
|
||||
name: "31E",
|
||||
from: "openai-response",
|
||||
to: "gemini-cli",
|
||||
model: "gemini-zero-mixed-model",
|
||||
inputJSON: `{"model":"gemini-zero-mixed-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`,
|
||||
expectField: "",
|
||||
expectErr: false,
|
||||
},
|
||||
// Case 31F: reasoning.effort=none with zero allowed to Antigravity → delete thinkingConfig
|
||||
{
|
||||
name: "31F",
|
||||
from: "openai-response",
|
||||
to: "antigravity",
|
||||
model: "gemini-zero-mixed-model",
|
||||
inputJSON: `{"model":"gemini-zero-mixed-model","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`,
|
||||
expectField: "",
|
||||
expectErr: false,
|
||||
},
|
||||
// Case 32: reasoning_effort=auto → -1 (DynamicAllowed=true)
|
||||
{
|
||||
name: "32",
|
||||
@@ -2957,6 +3017,15 @@ func getTestModels() []*registry.ModelInfo {
|
||||
DisplayName: "Gemini Mixed Model",
|
||||
Thinking: ®istry.ThinkingSupport{Min: 128, Max: 32768, Levels: []string{"low", "high"}, ZeroAllowed: false, DynamicAllowed: true},
|
||||
},
|
||||
{
|
||||
ID: "gemini-zero-mixed-model",
|
||||
Object: "model",
|
||||
Created: 1700000000,
|
||||
OwnedBy: "test",
|
||||
Type: "gemini",
|
||||
DisplayName: "Gemini Zero Mixed Model",
|
||||
Thinking: ®istry.ThinkingSupport{Min: 1, Max: 65535, Levels: []string{"minimal", "low", "medium", "high"}, ZeroAllowed: true, DynamicAllowed: true},
|
||||
},
|
||||
{
|
||||
ID: "claude-budget-model",
|
||||
Object: "model",
|
||||
|
||||
Reference in New Issue
Block a user