diff --git a/internal/registry/model_definitions.go b/internal/registry/model_definitions.go index c2a0ceed0..9f56d4278 100644 --- a/internal/registry/model_definitions.go +++ b/internal/registry/model_definitions.go @@ -88,7 +88,7 @@ func GetAntigravityModels() []*ModelInfo { var staticDevinModels = []*ModelInfo{ { - ID: "swe-2", + ID: "devin/swe-2", Type: "devin", OwnedBy: "cognition", DisplayName: "SWE-2", @@ -99,7 +99,7 @@ var staticDevinModels = []*ModelInfo{ }, }, { - ID: "claude-fable-5-1", + ID: "devin/claude-fable-5-1", Type: "devin", OwnedBy: "anthropic", DisplayName: "Claude Fable 5.1", @@ -110,7 +110,7 @@ var staticDevinModels = []*ModelInfo{ }, }, { - ID: "gpt-6-astra", + ID: "devin/gpt-6-astra", Type: "devin", OwnedBy: "openai", DisplayName: "GPT-6 Astra", @@ -121,7 +121,7 @@ var staticDevinModels = []*ModelInfo{ }, }, { - ID: "swe-1-7-lightning", + ID: "devin/swe-1-7-lightning", Type: "devin", OwnedBy: "cognition", DisplayName: "SWE-1.7 Lightning", @@ -132,7 +132,7 @@ var staticDevinModels = []*ModelInfo{ }, }, { - ID: "glm-5-2", + ID: "devin/glm-5-2", Type: "devin", OwnedBy: "zhipu", DisplayName: "GLM-5.2", @@ -143,7 +143,7 @@ var staticDevinModels = []*ModelInfo{ }, }, { - ID: "glm-5-3", + ID: "devin/glm-5-3", Type: "devin", OwnedBy: "zhipu", DisplayName: "GLM-5.3", diff --git a/internal/registry/model_definitions_test.go b/internal/registry/model_definitions_test.go index ecb0b5f58..a678e8638 100644 --- a/internal/registry/model_definitions_test.go +++ b/internal/registry/model_definitions_test.go @@ -164,21 +164,21 @@ func TestGetDevinModelsFallback(t *testing.T) { foundSWE2 := false foundFable := false for _, m := range devinModels { - if m != nil && m.ID == "swe-2" { + if m != nil && m.ID == "devin/swe-2" { foundSWE2 = true if m.Type != "devin" { - t.Errorf("swe-2 Type = %q, want devin", m.Type) + t.Errorf("devin/swe-2 Type = %q, want devin", m.Type) } } - if m != nil && m.ID == "claude-fable-5-1" { + if m != nil && m.ID == "devin/claude-fable-5-1" { foundFable = true } } if !foundSWE2 { - t.Error("expected swe-2 in GetDevinModels()") + t.Error("expected devin/swe-2 in GetDevinModels()") } if !foundFable { - t.Error("expected claude-fable-5-1 in GetDevinModels()") + t.Error("expected devin/claude-fable-5-1 in GetDevinModels()") } byChannel := GetStaticModelDefinitionsByChannel("devin") @@ -186,9 +186,9 @@ func TestGetDevinModelsFallback(t *testing.T) { t.Fatal("GetStaticModelDefinitionsByChannel(\"devin\") returned empty list") } - info := LookupStaticModelInfo("swe-2") + info := LookupStaticModelInfo("devin/swe-2") if info == nil { - t.Fatal("LookupStaticModelInfo(\"swe-2\") = nil, want valid model") + t.Fatal("LookupStaticModelInfo(\"devin/swe-2\") = nil, want valid model") } if info.DisplayName != "SWE-2" { t.Errorf("info.DisplayName = %q, want SWE-2", info.DisplayName) diff --git a/internal/runtime/executor/helps/devin_models.go b/internal/runtime/executor/helps/devin_models.go index ca91427c0..79ff7641a 100644 --- a/internal/runtime/executor/helps/devin_models.go +++ b/internal/runtime/executor/helps/devin_models.go @@ -79,13 +79,19 @@ func ResolveDevinChatModelUID(rawModel string, thinkingLevel string, budgetToken return "swe-2-high" } - // 1. If already ends with an exact Devin effort suffix, use directly - if HasDevinEffortSuffix(model) { - return model + // 1. Strip devin/ prefix if present (case-insensitive) + cleanModel := model + if strings.HasPrefix(strings.ToLower(cleanModel), "devin/") { + cleanModel = cleanModel[6:] } - // 2. Strip CPA colon or parenthesis suffix (suffix overrides body per CPA convention) - parsedSuffix := thinking.ParseSuffix(model) + // 2. If already ends with an exact Devin effort suffix, use directly + if HasDevinEffortSuffix(cleanModel) { + return cleanModel + } + + // 3. Strip CPA colon or parenthesis suffix (suffix overrides body per CPA convention) + parsedSuffix := thinking.ParseSuffix(cleanModel) baseModel := strings.TrimSpace(parsedSuffix.ModelName) if parsedSuffix.HasSuffix { thinkingLevel = parsedSuffix.RawSuffix diff --git a/internal/runtime/executor/helps/devin_models_test.go b/internal/runtime/executor/helps/devin_models_test.go index a85916b90..7aae5c73c 100644 --- a/internal/runtime/executor/helps/devin_models_test.go +++ b/internal/runtime/executor/helps/devin_models_test.go @@ -88,6 +88,36 @@ func TestResolveDevinChatModelUID(t *testing.T) { thinkingLevel: "medium", want: "glm-5-3-high", }, + { + 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", + }, } for _, tt := range tests { diff --git a/internal/runtime/executor/helps/devin_wire.go b/internal/runtime/executor/helps/devin_wire.go index ec2dcf493..5e53d197d 100644 --- a/internal/runtime/executor/helps/devin_wire.go +++ b/internal/runtime/executor/helps/devin_wire.go @@ -139,11 +139,10 @@ func WrapConnectEnvelope(protoBytes []byte) []byte { // WrapConnectEnvelopeWithFlag wraps payload bytes with an explicit Connect flag. func WrapConnectEnvelopeWithFlag(flag byte, protoBytes []byte) []byte { - out := make([]byte, 5+len(protoBytes)) - out[0] = flag - binary.BigEndian.PutUint32(out[1:5], uint32(len(protoBytes))) - copy(out[5:], protoBytes) - return out + header := make([]byte, 5, 5+len(protoBytes)) + header[0] = flag + binary.BigEndian.PutUint32(header[1:5], uint32(len(protoBytes))) + return append(header, protoBytes...) } // ReadConnectFrame reads a single framed message from a Connect-proto stream. diff --git a/internal/util/provider.go b/internal/util/provider.go index ae25a6314..741b26e85 100644 --- a/internal/util/provider.go +++ b/internal/util/provider.go @@ -65,6 +65,11 @@ func GetProviderName(modelName string) []string { for _, provider := range registry.GetGlobalRegistry().GetModelProviders(modelName) { appendProvider(provider) } + if len(providers) == 0 && strings.ToLower(modelName) != modelName { + for _, provider := range registry.GetGlobalRegistry().GetModelProviders(strings.ToLower(modelName)) { + appendProvider(provider) + } + } if len(providers) > 0 { return providers