Unified · Split
| @@ -88,7 +88,7 @@ func GetAntigravityModels() []*ModelInfo { · 87 unmodified lines | ||
| 88 | 88 | |
| 89 | 89 | var staticDevinModels = []*ModelInfo{ |
| 90 | 90 | { |
| 91 | − ID: "swe-2", | |
| 91 | + ID: "devin/swe-2", | |
| 92 | 92 | Type: "devin", |
| 93 | 93 | OwnedBy: "cognition", |
| 94 | 94 | DisplayName: "SWE-2", |
| @@ -99,7 +99,7 @@ var staticDevinModels = []*ModelInfo{ · 4 unmodified lines | ||
| 99 | 99 | }, |
| 100 | 100 | }, |
| 101 | 101 | { |
| 102 | − ID: "claude-fable-5-1", | |
| 102 | + ID: "devin/claude-fable-5-1", | |
| 103 | 103 | Type: "devin", |
| 104 | 104 | OwnedBy: "anthropic", |
| 105 | 105 | DisplayName: "Claude Fable 5.1", |
| @@ -110,7 +110,7 @@ var staticDevinModels = []*ModelInfo{ · 4 unmodified lines | ||
| 110 | 110 | }, |
| 111 | 111 | }, |
| 112 | 112 | { |
| 113 | − ID: "gpt-6-astra", | |
| 113 | + ID: "devin/gpt-6-astra", | |
| 114 | 114 | Type: "devin", |
| 115 | 115 | OwnedBy: "openai", |
| 116 | 116 | DisplayName: "GPT-6 Astra", |
| @@ -121,7 +121,7 @@ var staticDevinModels = []*ModelInfo{ · 4 unmodified lines | ||
| 121 | 121 | }, |
| 122 | 122 | }, |
| 123 | 123 | { |
| 124 | − ID: "swe-1-7-lightning", | |
| 124 | + ID: "devin/swe-1-7-lightning", | |
| 125 | 125 | Type: "devin", |
| 126 | 126 | OwnedBy: "cognition", |
| 127 | 127 | DisplayName: "SWE-1.7 Lightning", |
| @@ -132,7 +132,7 @@ var staticDevinModels = []*ModelInfo{ · 4 unmodified lines | ||
| 132 | 132 | }, |
| 133 | 133 | }, |
| 134 | 134 | { |
| 135 | − ID: "glm-5-2", | |
| 135 | + ID: "devin/glm-5-2", | |
| 136 | 136 | Type: "devin", |
| 137 | 137 | OwnedBy: "zhipu", |
| 138 | 138 | DisplayName: "GLM-5.2", |
| @@ -143,7 +143,7 @@ var staticDevinModels = []*ModelInfo{ · 4 unmodified lines | ||
| 143 | 143 | }, |
| 144 | 144 | }, |
| 145 | 145 | { |
| 146 | − ID: "glm-5-3", | |
| 146 | + ID: "devin/glm-5-3", | |
| 147 | 147 | Type: "devin", |
| 148 | 148 | OwnedBy: "zhipu", |
| 149 | 149 | DisplayName: "GLM-5.3", |
| @@ -164,21 +164,21 @@ func TestGetDevinModelsFallback(t *testing.T) { · 163 unmodified lines | ||
| 164 | 164 | foundSWE2 := false |
| 165 | 165 | foundFable := false |
| 166 | 166 | for _, m := range devinModels { |
| 167 | − if m != nil && m.ID == "swe-2" { | |
| 167 | + if m != nil && m.ID == "devin/swe-2" { | |
| 168 | 168 | foundSWE2 = true |
| 169 | 169 | if m.Type != "devin" { |
| 170 | − t.Errorf("swe-2 Type = %q, want devin", m.Type) | |
| 170 | + t.Errorf("devin/swe-2 Type = %q, want devin", m.Type) | |
| 171 | 171 | } |
| 172 | 172 | } |
| 173 | − if m != nil && m.ID == "claude-fable-5-1" { | |
| 173 | + if m != nil && m.ID == "devin/claude-fable-5-1" { | |
| 174 | 174 | foundFable = true |
| 175 | 175 | } |
| 176 | 176 | } |
| 177 | 177 | if !foundSWE2 { |
| 178 | − t.Error("expected swe-2 in GetDevinModels()") | |
| 178 | + t.Error("expected devin/swe-2 in GetDevinModels()") | |
| 179 | 179 | } |
| 180 | 180 | if !foundFable { |
| 181 | − t.Error("expected claude-fable-5-1 in GetDevinModels()") | |
| 181 | + t.Error("expected devin/claude-fable-5-1 in GetDevinModels()") | |
| 182 | 182 | } |
| 183 | 183 | |
| 184 | 184 | byChannel := GetStaticModelDefinitionsByChannel("devin") |
| @@ -186,9 +186,9 @@ func TestGetDevinModelsFallback(t *testing.T) { · 1 unmodified line | ||
| 186 | 186 | t.Fatal("GetStaticModelDefinitionsByChannel(\"devin\") returned empty list") |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | − info := LookupStaticModelInfo("swe-2") | |
| 189 | + info := LookupStaticModelInfo("devin/swe-2") | |
| 190 | 190 | if info == nil { |
| 191 | − t.Fatal("LookupStaticModelInfo(\"swe-2\") = nil, want valid model") | |
| 191 | + t.Fatal("LookupStaticModelInfo(\"devin/swe-2\") = nil, want valid model") | |
| 192 | 192 | } |
| 193 | 193 | if info.DisplayName != "SWE-2" { |
| 194 | 194 | t.Errorf("info.DisplayName = %q, want SWE-2", info.DisplayName) |
| @@ -79,13 +79,19 @@ func ResolveDevinChatModelUID(rawModel string, thinkingLevel string, budgetToken · 78 unmodified lines | ||
| 79 | 79 | return "swe-2-high" |
| 80 | 80 | } |
| 81 | 81 | |
| 82 | − // 1. If already ends with an exact Devin effort suffix, use directly | |
| 83 | − if HasDevinEffortSuffix(model) { | |
| 84 | − return model | |
| 82 | + // 1. Strip devin/ prefix if present (case-insensitive) | |
| 83 | + cleanModel := model | |
| 84 | + if strings.HasPrefix(strings.ToLower(cleanModel), "devin/") { | |
| 85 | + cleanModel = cleanModel[6:] | |
| 85 | 86 | } |
| 86 | 87 | |
| 87 | − // 2. Strip CPA colon or parenthesis suffix (suffix overrides body per CPA convention) | |
| 88 | − parsedSuffix := thinking.ParseSuffix(model) | |
| 88 | + // 2. If already ends with an exact Devin effort suffix, use directly | |
| 89 | + if HasDevinEffortSuffix(cleanModel) { | |
| 90 | + return cleanModel | |
| 91 | + } | |
| 92 | + | |
| 93 | + // 3. Strip CPA colon or parenthesis suffix (suffix overrides body per CPA convention) | |
| 94 | + parsedSuffix := thinking.ParseSuffix(cleanModel) | |
| 89 | 95 | baseModel := strings.TrimSpace(parsedSuffix.ModelName) |
| 90 | 96 | if parsedSuffix.HasSuffix { |
| 91 | 97 | thinkingLevel = parsedSuffix.RawSuffix |
| @@ -88,6 +88,36 @@ func TestResolveDevinChatModelUID(t *testing.T) { · 87 unmodified lines | ||
| 88 | 88 | thinkingLevel: "medium", |
| 89 | 89 | want: "glm-5-3-high", |
| 90 | 90 | }, |
| 91 | + { | |
| 92 | + name: "devin prefix lowercase swe-2", | |
| 93 | + rawModel: "devin/swe-2", | |
| 94 | + thinkingLevel: "", | |
| 95 | + want: "swe-2-high", | |
| 96 | + }, | |
| 97 | + { | |
| 98 | + name: "Devin prefix capitalized swe-2 with suffix", | |
| 99 | + rawModel: "Devin/swe-2(max)", | |
| 100 | + thinkingLevel: "", | |
| 101 | + want: "swe-2-max", | |
| 102 | + }, | |
| 103 | + { | |
| 104 | + name: "devin prefix claude-fable-5-1", | |
| 105 | + rawModel: "devin/claude-fable-5-1", | |
| 106 | + thinkingLevel: "", | |
| 107 | + want: "claude-fable-5-1-medium", | |
| 108 | + }, | |
| 109 | + { | |
| 110 | + name: "Devin prefix gpt-6-astra with suffix", | |
| 111 | + rawModel: "Devin/gpt-6-astra(high)", | |
| 112 | + thinkingLevel: "", | |
| 113 | + want: "gpt-6-astra-high", | |
| 114 | + }, | |
| 115 | + { | |
| 116 | + name: "devin prefix direct effort UID", | |
| 117 | + rawModel: "devin/swe-2-high", | |
| 118 | + thinkingLevel: "", | |
| 119 | + want: "swe-2-high", | |
| 120 | + }, | |
| 91 | 121 | } |
| 92 | 122 | |
| 93 | 123 | for _, tt := range tests { |
| @@ -139,11 +139,10 @@ func WrapConnectEnvelope(protoBytes []byte) []byte { · 138 unmodified lines | ||
| 139 | 139 | |
| 140 | 140 | // WrapConnectEnvelopeWithFlag wraps payload bytes with an explicit Connect flag. |
| 141 | 141 | func WrapConnectEnvelopeWithFlag(flag byte, protoBytes []byte) []byte { |
| 142 | − out := make([]byte, 5+len(protoBytes)) | |
| 143 | − out[0] = flag | |
| 144 | − binary.BigEndian.PutUint32(out[1:5], uint32(len(protoBytes))) | |
| 145 | − copy(out[5:], protoBytes) | |
| 146 | − return out | |
| 142 | + header := make([]byte, 5, 5+len(protoBytes)) | |
| 143 | + header[0] = flag | |
| 144 | + binary.BigEndian.PutUint32(header[1:5], uint32(len(protoBytes))) | |
| 145 | + return append(header, protoBytes...) | |
| 147 | 146 | } |
| 148 | 147 | |
| 149 | 148 | // ReadConnectFrame reads a single framed message from a Connect-proto stream. |
| @@ -65,6 +65,11 @@ func GetProviderName(modelName string) []string { · 64 unmodified lines | ||
| 65 | 65 | for _, provider := range registry.GetGlobalRegistry().GetModelProviders(modelName) { |
| 66 | 66 | appendProvider(provider) |
| 67 | 67 | } |
| 68 | + if len(providers) == 0 && strings.ToLower(modelName) != modelName { | |
| 69 | + for _, provider := range registry.GetGlobalRegistry().GetModelProviders(strings.ToLower(modelName)) { | |
| 70 | + appendProvider(provider) | |
| 71 | + } | |
| 72 | + } | |
| 68 | 73 | |
| 69 | 74 | if len(providers) > 0 { |
| 70 | 75 | return providers |