fix(copilot): allow a few more models (#252)

Andrey Nering created

Closes #251.

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>

Change summary

cmd/copilot/main.go                     | 21 +++++++++++-
internal/providers/configs/copilot.json | 44 ++++++++++++++++++++++++--
2 files changed, 59 insertions(+), 6 deletions(-)

Detailed changes

cmd/copilot/main.go 🔗

@@ -90,9 +90,26 @@ func run() error {
 		return err
 	}
 
-	// NOTE(@andreynering): Exclude versioned models and keep only the main version of each.
+	// Exclude versioned/duplicate models and keep only the main version of each.
+	aliasedVersions := make(map[string]bool, len(copilotModels))
+	for _, m := range copilotModels {
+		if m.ID != m.Version {
+			aliasedVersions[m.Version] = true
+		}
+	}
+
+	copilotModels = slices.DeleteFunc(copilotModels, func(m Model) bool {
+		return aliasedVersions[m.ID] || versionedModelRegexp.MatchString(m.ID) || strings.Contains(m.ID, "embedding") || strings.HasPrefix(m.ID, "accounts/msft/routers") || strings.HasPrefix(m.ID, "oswe-vscode") || m.ID == "gpt-4-o-preview"
+	})
+
+	// Deduplicate by ID (API can return duplicates)
+	seen := make(map[string]bool, len(copilotModels))
 	copilotModels = slices.DeleteFunc(copilotModels, func(m Model) bool {
-		return m.ID != m.Version || versionedModelRegexp.MatchString(m.ID) || strings.Contains(m.ID, "embedding") || strings.HasPrefix(m.ID, "accounts/msft/routers")
+		if seen[m.ID] {
+			return true
+		}
+		seen[m.ID] = true
+		return false
 	})
 
 	catwalkModels := modelsToCatwalk(copilotModels)

internal/providers/configs/copilot.json 🔗

@@ -139,7 +139,7 @@
       "supports_attachments": true
     },
     {
-      "id": "gpt-3.5-turbo-0613",
+      "id": "gpt-3.5-turbo",
       "name": "GPT 3.5 Turbo",
       "cost_per_1m_in": 0,
       "cost_per_1m_out": 0,
@@ -150,6 +150,18 @@
       "can_reason": false,
       "supports_attachments": false
     },
+    {
+      "id": "gpt-4",
+      "name": "GPT 4",
+      "cost_per_1m_in": 0,
+      "cost_per_1m_out": 0,
+      "cost_per_1m_in_cached": 0,
+      "cost_per_1m_out_cached": 0,
+      "context_window": 32768,
+      "default_max_tokens": 4096,
+      "can_reason": false,
+      "supports_attachments": false
+    },
     {
       "id": "gpt-4-0125-preview",
       "name": "GPT 4 Turbo",
@@ -163,13 +175,37 @@
       "supports_attachments": false
     },
     {
-      "id": "gpt-4-0613",
-      "name": "GPT 4",
+      "id": "gpt-4.1",
+      "name": "GPT-4.1",
       "cost_per_1m_in": 0,
       "cost_per_1m_out": 0,
       "cost_per_1m_in_cached": 0,
       "cost_per_1m_out_cached": 0,
-      "context_window": 32768,
+      "context_window": 128000,
+      "default_max_tokens": 16384,
+      "can_reason": false,
+      "supports_attachments": true
+    },
+    {
+      "id": "gpt-4o",
+      "name": "GPT-4o",
+      "cost_per_1m_in": 0,
+      "cost_per_1m_out": 0,
+      "cost_per_1m_in_cached": 0,
+      "cost_per_1m_out_cached": 0,
+      "context_window": 128000,
+      "default_max_tokens": 4096,
+      "can_reason": false,
+      "supports_attachments": true
+    },
+    {
+      "id": "gpt-4o-mini",
+      "name": "GPT-4o mini",
+      "cost_per_1m_in": 0,
+      "cost_per_1m_out": 0,
+      "cost_per_1m_in_cached": 0,
+      "cost_per_1m_out_cached": 0,
+      "context_window": 128000,
       "default_max_tokens": 4096,
       "can_reason": false,
       "supports_attachments": false