fix(model): repair Discord Copilot /model flow

Keep Discord Copilot model switching responsive and current by refreshing picker data from the live catalog when possible, correcting the curated fallback list, and clearing stale controls before the switch completes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Nicecsh 2026-04-24 17:41:19 +08:00 committed by Teknium
parent 2e2de124af
commit fe34741f32
6 changed files with 265 additions and 11 deletions

View file

@ -0,0 +1,41 @@
"""Tests for GitHub Copilot entries shown in the /model picker."""
import os
from unittest.mock import patch
from hermes_cli.model_switch import list_authenticated_providers
@patch.dict(os.environ, {"GH_TOKEN": "test-key"}, clear=False)
def test_copilot_picker_keeps_curated_copilot_models_when_live_catalog_unavailable():
with patch("agent.models_dev.fetch_models_dev", return_value={}), \
patch("hermes_cli.models._resolve_copilot_catalog_api_key", return_value="gh-token"), \
patch("hermes_cli.models._fetch_github_models", return_value=None):
providers = list_authenticated_providers(current_provider="openrouter", max_models=50)
copilot = next((p for p in providers if p["slug"] == "copilot"), None)
assert copilot is not None
assert "gpt-5.4" in copilot["models"]
assert "claude-sonnet-4.6" in copilot["models"]
assert "claude-sonnet-4" in copilot["models"]
assert "claude-sonnet-4.5" in copilot["models"]
assert "claude-haiku-4.5" in copilot["models"]
assert "gemini-3.1-pro-preview" in copilot["models"]
assert "claude-opus-4.6" not in copilot["models"]
@patch.dict(os.environ, {"GH_TOKEN": "test-key"}, clear=False)
def test_copilot_picker_uses_live_catalog_when_available():
live_models = ["gpt-5.4", "claude-sonnet-4.6", "gemini-3.1-pro-preview"]
with patch("agent.models_dev.fetch_models_dev", return_value={}), \
patch("hermes_cli.models._resolve_copilot_catalog_api_key", return_value="gh-token"), \
patch("hermes_cli.models._fetch_github_models", return_value=live_models):
providers = list_authenticated_providers(current_provider="openrouter", max_models=50)
copilot = next((p for p in providers if p["slug"] == "copilot"), None)
assert copilot is not None
assert copilot["models"] == live_models
assert copilot["total_models"] == len(live_models)

View file

@ -220,13 +220,30 @@ class TestProviderModelIds:
patch("hermes_cli.models._fetch_github_models", return_value=["gpt-5.4", "claude-sonnet-4.6"]):
assert provider_model_ids("copilot-acp") == ["gpt-5.4", "claude-sonnet-4.6"]
def test_copilot_falls_back_to_curated_defaults_without_stale_opus(self):
with patch("hermes_cli.models._resolve_copilot_catalog_api_key", return_value="gh-token"), \
patch("hermes_cli.models._fetch_github_models", return_value=None):
ids = provider_model_ids("copilot")
assert "gpt-5.4" in ids
assert "claude-sonnet-4.6" in ids
assert "claude-sonnet-4" in ids
assert "claude-sonnet-4.5" in ids
assert "claude-haiku-4.5" in ids
assert "gemini-3.1-pro-preview" in ids
assert "claude-opus-4.6" not in ids
def test_copilot_acp_falls_back_to_copilot_defaults(self):
with patch("hermes_cli.auth.resolve_api_key_provider_credentials", side_effect=Exception("no token")), \
with patch("hermes_cli.models._resolve_copilot_catalog_api_key", return_value="gh-token"), \
patch("hermes_cli.models._fetch_github_models", return_value=None):
ids = provider_model_ids("copilot-acp")
assert "gpt-5.4" in ids
assert "claude-sonnet-4.6" in ids
assert "claude-sonnet-4" in ids
assert "gemini-3.1-pro-preview" in ids
assert "copilot-acp" not in ids
assert "claude-opus-4.6" not in ids
# -- fetch_api_models --------------------------------------------------------