mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-10-07 16:23:58 +08:00
477 lines
13 KiB
Go
477 lines
13 KiB
Go
package helps
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
|
)
|
|
|
|
func TestResolveDevinChatModelUID(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
rawModel string
|
|
thinkingLevel string
|
|
budgetTokens int
|
|
want string
|
|
}{
|
|
{
|
|
name: "direct full UID unchanged",
|
|
rawModel: "claude-fable-5-1-max",
|
|
want: "claude-fable-5-1-max",
|
|
},
|
|
{
|
|
name: "direct swe-2-high unchanged",
|
|
rawModel: "swe-2-high",
|
|
want: "swe-2-high",
|
|
},
|
|
{
|
|
name: "swe-2 default to high when no effort",
|
|
rawModel: "swe-2",
|
|
thinkingLevel: "",
|
|
want: "swe-2-high",
|
|
},
|
|
{
|
|
name: "swe-2 clamp minimal to medium",
|
|
rawModel: "swe-2",
|
|
thinkingLevel: "minimal",
|
|
want: "swe-2-medium",
|
|
},
|
|
{
|
|
name: "swe-2 clamp low to medium",
|
|
rawModel: "swe-2",
|
|
thinkingLevel: "low",
|
|
want: "swe-2-medium",
|
|
},
|
|
{
|
|
name: "swe-2 with xhigh maps to max",
|
|
rawModel: "swe-2",
|
|
thinkingLevel: "xhigh",
|
|
want: "swe-2-max",
|
|
},
|
|
{
|
|
name: "swe-2 request max",
|
|
rawModel: "swe-2",
|
|
thinkingLevel: "max",
|
|
want: "swe-2-max",
|
|
},
|
|
{
|
|
name: "swe-2 suffix overrides body thinkingLevel",
|
|
rawModel: "swe-2(max)",
|
|
thinkingLevel: "medium",
|
|
want: "swe-2-max",
|
|
},
|
|
{
|
|
name: "fable-5-1 budget tokens maps to max",
|
|
rawModel: "claude-fable-5-1",
|
|
budgetTokens: 64000,
|
|
want: "claude-fable-5-1-max",
|
|
},
|
|
{
|
|
name: "fable-5-1 budget tokens maps to low",
|
|
rawModel: "claude-fable-5-1",
|
|
budgetTokens: 2048,
|
|
want: "claude-fable-5-1-low",
|
|
},
|
|
{
|
|
name: "fable-5-1 with xhigh",
|
|
rawModel: "claude-fable-5-1",
|
|
thinkingLevel: "xhigh",
|
|
want: "claude-fable-5-1-xhigh",
|
|
},
|
|
{
|
|
name: "astra with suffix in parenthesis",
|
|
rawModel: "gpt-6-astra(high)",
|
|
thinkingLevel: "",
|
|
want: "gpt-6-astra-high",
|
|
},
|
|
{
|
|
name: "glm-5-3 clamp medium to high",
|
|
rawModel: "glm-5-3",
|
|
thinkingLevel: "medium",
|
|
want: "glm-5-3-high",
|
|
},
|
|
{
|
|
name: "devin prefix glm-5-3-flash default high",
|
|
rawModel: "devin/glm-5-3-flash",
|
|
thinkingLevel: "",
|
|
want: "glm-5-3-flash-high",
|
|
},
|
|
{
|
|
name: "devin prefix glm-5-3-flash with low suffix",
|
|
rawModel: "devin/glm-5-3-flash:low",
|
|
thinkingLevel: "",
|
|
want: "glm-5-3-flash-low",
|
|
},
|
|
{
|
|
name: "devin prefix glm-5-3-flash with max suffix",
|
|
rawModel: "devin/glm-5-3-flash(max)",
|
|
thinkingLevel: "",
|
|
want: "glm-5-3-flash-max",
|
|
},
|
|
{
|
|
name: "devin prefix gpt-5-6-sol with none suffix",
|
|
rawModel: "devin/gpt-5-6-sol:none",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-6-sol-none",
|
|
},
|
|
{
|
|
name: "devin prefix gpt-5-6-sol with body none effort",
|
|
rawModel: "devin/gpt-5-6-sol",
|
|
thinkingLevel: "none",
|
|
want: "gpt-5-6-sol-none",
|
|
},
|
|
{
|
|
name: "devin prefix gpt-5-6-terra with none suffix",
|
|
rawModel: "devin/gpt-5-6-terra:none",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-6-terra-none",
|
|
},
|
|
{
|
|
name: "devin prefix nemotron-3-ultra with none suffix",
|
|
rawModel: "devin/nemotron-3-ultra:none",
|
|
thinkingLevel: "",
|
|
want: "nemotron-3-ultra-none",
|
|
},
|
|
{
|
|
name: "glm-5-2 default maps to glm-5-2",
|
|
rawModel: "devin/glm-5-2",
|
|
thinkingLevel: "high",
|
|
want: "glm-5-2",
|
|
},
|
|
{
|
|
name: "glm-5-2 with none effort maps to glm-5-2-none",
|
|
rawModel: "devin/glm-5-2:none",
|
|
thinkingLevel: "",
|
|
want: "glm-5-2-none",
|
|
},
|
|
{
|
|
name: "glm-5-2 suffix max maps to max",
|
|
rawModel: "devin/glm-5-2(max)",
|
|
thinkingLevel: "",
|
|
want: "glm-5-2-max",
|
|
},
|
|
{
|
|
name: "devin prefix lowercase swe-2",
|
|
rawModel: "devin/swe-2",
|
|
thinkingLevel: "",
|
|
want: "swe-2-high",
|
|
},
|
|
{
|
|
name: "Devin prefix capitalized swe-2 with suffix",
|
|
rawModel: "Devin/swe-2(max)",
|
|
thinkingLevel: "",
|
|
want: "swe-2-max",
|
|
},
|
|
{
|
|
name: "devin prefix claude-fable-5-1",
|
|
rawModel: "devin/claude-fable-5-1",
|
|
thinkingLevel: "",
|
|
want: "claude-fable-5-1-medium",
|
|
},
|
|
{
|
|
name: "Devin prefix gpt-6-astra with suffix",
|
|
rawModel: "Devin/gpt-6-astra(high)",
|
|
thinkingLevel: "",
|
|
want: "gpt-6-astra-high",
|
|
},
|
|
{
|
|
name: "devin prefix direct effort UID",
|
|
rawModel: "devin/swe-2-high",
|
|
thinkingLevel: "",
|
|
want: "swe-2-high",
|
|
},
|
|
{
|
|
name: "devin prefix gemini-3-8-flash default high",
|
|
rawModel: "devin/gemini-3-8-flash",
|
|
thinkingLevel: "",
|
|
want: "gemini-3-8-flash-high",
|
|
},
|
|
{
|
|
name: "devin prefix gemini-3.8-flash with low suffix",
|
|
rawModel: "devin/gemini-3.8-flash(low)",
|
|
thinkingLevel: "",
|
|
want: "gemini-3-8-flash-low",
|
|
},
|
|
{
|
|
name: "devin prefix grok-4-6 default high",
|
|
rawModel: "devin/grok-4-6",
|
|
thinkingLevel: "",
|
|
want: "grok-4-6-high",
|
|
},
|
|
{
|
|
name: "devin prefix grok-4.6 with xhigh suffix",
|
|
rawModel: "devin/grok-4.6:xhigh",
|
|
thinkingLevel: "",
|
|
want: "grok-4-6-xhigh",
|
|
},
|
|
{
|
|
name: "devin prefix deepseek-v4-flash default high",
|
|
rawModel: "devin/deepseek-v4-flash",
|
|
thinkingLevel: "",
|
|
want: "deepseek-v4-flash-high",
|
|
},
|
|
{
|
|
name: "devin prefix deepseek-v4.1-flash with max suffix",
|
|
rawModel: "devin/deepseek-v4.1-flash(max)",
|
|
thinkingLevel: "",
|
|
want: "deepseek-v4-1-flash-max",
|
|
},
|
|
{
|
|
name: "swe-1-7 default maps to swe-1-7",
|
|
rawModel: "devin/swe-1-7",
|
|
thinkingLevel: "",
|
|
want: "swe-1-7",
|
|
},
|
|
{
|
|
name: "swe-1-7 with medium maps to swe-1-7-medium",
|
|
rawModel: "devin/swe-1-7:medium",
|
|
thinkingLevel: "",
|
|
want: "swe-1-7-medium",
|
|
},
|
|
{
|
|
name: "claude-haiku-4-5 maps to MODEL_PRIVATE_11",
|
|
rawModel: "devin/claude-haiku-4-5",
|
|
thinkingLevel: "",
|
|
want: "MODEL_PRIVATE_11",
|
|
},
|
|
{
|
|
name: "claude-sonnet-4-5 non-thinking maps to MODEL_PRIVATE_2",
|
|
rawModel: "devin/claude-sonnet-4-5",
|
|
thinkingLevel: "",
|
|
want: "MODEL_PRIVATE_2",
|
|
},
|
|
{
|
|
name: "claude-sonnet-4-5 thinking maps to MODEL_PRIVATE_3",
|
|
rawModel: "devin/claude-sonnet-4-5:high",
|
|
thinkingLevel: "",
|
|
want: "MODEL_PRIVATE_3",
|
|
},
|
|
{
|
|
name: "gpt-4-1 maps to MODEL_CHAT_GPT_4_1_2025_04_14",
|
|
rawModel: "devin/gpt-4-1",
|
|
thinkingLevel: "",
|
|
want: "MODEL_CHAT_GPT_4_1_2025_04_14",
|
|
},
|
|
{
|
|
name: "gemini-3-flash alias maps to gemini-3-8-flash-high",
|
|
rawModel: "devin/gemini-3-flash",
|
|
thinkingLevel: "",
|
|
want: "gemini-3-8-flash-high",
|
|
},
|
|
{
|
|
name: "gpt-5-6-luna default maps to low",
|
|
rawModel: "devin/gpt-5-6-luna",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-6-luna-low",
|
|
},
|
|
{
|
|
name: "gpt-5-6-luna with high suffix",
|
|
rawModel: "devin/gpt-5-6-luna(high)",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-6-luna-high",
|
|
},
|
|
{
|
|
name: "gpt-5-6-luna with none suffix",
|
|
rawModel: "devin/gpt-5-6-luna:none",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-6-luna-none",
|
|
},
|
|
{
|
|
name: "gpt-5-6-sol default maps to low",
|
|
rawModel: "devin/gpt-5-6-sol",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-6-sol-low",
|
|
},
|
|
{
|
|
name: "gpt-5-6-terra default maps to low",
|
|
rawModel: "devin/gpt-5-6-terra",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-6-terra-low",
|
|
},
|
|
{
|
|
name: "gpt-5-5 default maps to low",
|
|
rawModel: "devin/gpt-5-5",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-5-low",
|
|
},
|
|
{
|
|
name: "gpt-5-4 default maps to low",
|
|
rawModel: "devin/gpt-5-4",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-4-low",
|
|
},
|
|
{
|
|
name: "gpt-5-4-mini default maps to medium",
|
|
rawModel: "devin/gpt-5-4-mini",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-4-mini-medium",
|
|
},
|
|
{
|
|
name: "gpt-5-3-codex default maps to medium",
|
|
rawModel: "devin/gpt-5-3-codex",
|
|
thinkingLevel: "",
|
|
want: "gpt-5-3-codex-medium",
|
|
},
|
|
{
|
|
name: "claude-opus-5 default maps to medium",
|
|
rawModel: "devin/claude-opus-5",
|
|
thinkingLevel: "",
|
|
want: "claude-opus-5-medium",
|
|
},
|
|
{
|
|
name: "claude-opus-4-8 default maps to medium",
|
|
rawModel: "devin/claude-opus-4-8",
|
|
thinkingLevel: "",
|
|
want: "claude-opus-4-8-medium",
|
|
},
|
|
{
|
|
name: "claude-opus-4-7 default maps to medium",
|
|
rawModel: "devin/claude-opus-4-7",
|
|
thinkingLevel: "",
|
|
want: "claude-opus-4-7-medium",
|
|
},
|
|
{
|
|
name: "claude-sonnet-5 default maps to medium",
|
|
rawModel: "devin/claude-sonnet-5",
|
|
thinkingLevel: "",
|
|
want: "claude-sonnet-5-medium",
|
|
},
|
|
{
|
|
name: "gemini-3-7-flash default maps to high",
|
|
rawModel: "devin/gemini-3-7-flash",
|
|
thinkingLevel: "",
|
|
want: "gemini-3-7-flash-high",
|
|
},
|
|
{
|
|
name: "gemini-3-6-flash default maps to high",
|
|
rawModel: "devin/gemini-3-6-flash",
|
|
thinkingLevel: "",
|
|
want: "gemini-3-6-flash-high",
|
|
},
|
|
{
|
|
name: "gemini-3-5-flash default maps to high",
|
|
rawModel: "devin/gemini-3-5-flash",
|
|
thinkingLevel: "",
|
|
want: "gemini-3-5-flash-high",
|
|
},
|
|
{
|
|
name: "deepseek-v4-pro default maps to high",
|
|
rawModel: "devin/deepseek-v4-pro",
|
|
thinkingLevel: "",
|
|
want: "deepseek-v4-pro-high",
|
|
},
|
|
{
|
|
name: "grok-4-5 default maps to high",
|
|
rawModel: "devin/grok-4-5",
|
|
thinkingLevel: "",
|
|
want: "grok-4-5-high",
|
|
},
|
|
{
|
|
name: "kimi-k3 default maps to high",
|
|
rawModel: "devin/kimi-k3",
|
|
thinkingLevel: "",
|
|
want: "kimi-k3-high",
|
|
},
|
|
{
|
|
name: "nemotron-3-ultra default maps to high",
|
|
rawModel: "devin/nemotron-3-ultra",
|
|
thinkingLevel: "",
|
|
want: "nemotron-3-ultra-high",
|
|
},
|
|
{
|
|
name: "swe-1-6 default maps to swe-1-6",
|
|
rawModel: "devin/swe-1-6",
|
|
thinkingLevel: "",
|
|
want: "swe-1-6",
|
|
},
|
|
{
|
|
name: "swe-1-6 with fast maps to swe-1-6-fast",
|
|
rawModel: "devin/swe-1-6:fast",
|
|
thinkingLevel: "",
|
|
want: "swe-1-6-fast",
|
|
},
|
|
{
|
|
name: "swe-1-6-slow default maps to swe-1-6-slow",
|
|
rawModel: "devin/swe-1-6-slow",
|
|
thinkingLevel: "",
|
|
want: "swe-1-6-slow",
|
|
},
|
|
{
|
|
name: "swe-1-6-slow without prefix maps to swe-1-6-slow",
|
|
rawModel: "swe-1-6-slow",
|
|
thinkingLevel: "",
|
|
want: "swe-1-6-slow",
|
|
},
|
|
{
|
|
name: "swe-1-6-slow with low maps to swe-1-6-slow",
|
|
rawModel: "devin/swe-1-6-slow:low",
|
|
thinkingLevel: "",
|
|
want: "swe-1-6-slow",
|
|
},
|
|
{
|
|
name: "swe-1-6-slow with high maps to swe-1-6-slow",
|
|
rawModel: "devin/swe-1-6-slow:high",
|
|
thinkingLevel: "",
|
|
want: "swe-1-6-slow",
|
|
},
|
|
{
|
|
name: "kimi-k2-6 default maps to kimi-k2-6",
|
|
rawModel: "devin/kimi-k2-6",
|
|
thinkingLevel: "",
|
|
want: "kimi-k2-6",
|
|
},
|
|
{
|
|
name: "kimi-k2-7 default maps to kimi-k2-7",
|
|
rawModel: "devin/kimi-k2-7",
|
|
thinkingLevel: "",
|
|
want: "kimi-k2-7",
|
|
},
|
|
{
|
|
name: "claude-opus-4-6 default maps to claude-opus-4-6",
|
|
rawModel: "devin/claude-opus-4-6",
|
|
thinkingLevel: "",
|
|
want: "claude-opus-4-6",
|
|
},
|
|
{
|
|
name: "claude-sonnet-4-6 default maps to claude-sonnet-4-6",
|
|
rawModel: "devin/claude-sonnet-4-6",
|
|
thinkingLevel: "",
|
|
want: "claude-sonnet-4-6",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := ResolveDevinChatModelUID(tt.rawModel, tt.thinkingLevel, tt.budgetTokens)
|
|
if got != tt.want {
|
|
t.Errorf("ResolveDevinChatModelUID(%q, %q, %d) = %q, want %q", tt.rawModel, tt.thinkingLevel, tt.budgetTokens, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolveDevinChatModelUID_AllCatalogModels(t *testing.T) {
|
|
models := registry.GetDevinModels()
|
|
if len(models) == 0 {
|
|
t.Fatal("GetDevinModels() returned empty list")
|
|
}
|
|
|
|
testEfforts := []string{"", "none", "low", "medium", "high", "xhigh", "max"}
|
|
for _, m := range models {
|
|
baseID := strings.TrimPrefix(m.ID, "devin/")
|
|
for _, eff := range testEfforts {
|
|
resolved := ResolveDevinChatModelUID("devin/"+baseID, eff, 0)
|
|
if resolved == "" {
|
|
t.Errorf("model %q with effort %q resolved to empty string", baseID, eff)
|
|
}
|
|
// If model defines thinking levels, resolved UID must have an effort suffix
|
|
if m.Thinking != nil && len(m.Thinking.Levels) > 0 {
|
|
if !HasDevinEffortSuffix(resolved) && baseID != "swe-1-7" && baseID != "glm-5-2" && baseID != "swe-1-6-slow" {
|
|
t.Errorf("thinking model %q with effort %q resolved to bare UID %q without effort suffix", baseID, eff, resolved)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|