diff --git a/plugins/_oauth/helpers/codex.py b/plugins/_oauth/helpers/codex.py index ad32cd271..824e19a92 100644 --- a/plugins/_oauth/helpers/codex.py +++ b/plugins/_oauth/helpers/codex.py @@ -35,7 +35,7 @@ except ImportError: AUTH_FILENAME = "auth.json" ACCESS_EXPIRY_MARGIN = timedelta(minutes=5) REFRESH_INTERVAL = timedelta(minutes=55) -FALLBACK_CODEX_VERSION = "0.124.0" +DEFAULT_CODEX_MODEL = "gpt-5.5" OAUTH_ERROR_KEYS = ("error_description", "error") DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60 WINDOWS_LOCK_RETRY_SECONDS = 0.05 @@ -590,9 +590,11 @@ def fetch_models() -> list[str]: if configured: return configured + client_version = resolve_codex_version() + params = {"client_version": client_version} if client_version else None response = request_codex( "/models", - params={"client_version": resolve_codex_version()}, + params=params, ) if not response.ok: raise RuntimeError(upstream_error_message(response, "Failed to load Codex models.")) @@ -772,7 +774,7 @@ def chat_messages_to_response_body(body: dict[str, Any]) -> dict[str, Any]: ) response_body: dict[str, Any] = { - "model": body.get("model") or "gpt-5.2", + "model": body.get("model") or DEFAULT_CODEX_MODEL, "input": response_input, "instructions": "\n\n".join(instructions), "store": False, @@ -980,7 +982,7 @@ def resolve_codex_version() -> str: return version except Exception: pass - return FALLBACK_CODEX_VERSION + return "" def resolve_auth_file_candidates() -> list[Path]: diff --git a/plugins/_oauth/helpers/providers/codex.py b/plugins/_oauth/helpers/providers/codex.py index 7a2eb7218..d619927da 100644 --- a/plugins/_oauth/helpers/providers/codex.py +++ b/plugins/_oauth/helpers/providers/codex.py @@ -20,7 +20,7 @@ from plugins._oauth.helpers.state import ( ) -CODEX_DEFAULT_MODELS = ["gpt-5.2-codex", "gpt-5.2"] +CODEX_DEFAULT_MODEL = "gpt-5.5" CODEX_FALLBACK_CONFIG = { "enabled": True, "models": [], @@ -35,7 +35,7 @@ class CodexOAuthProvider: def metadata(self) -> OAuthProviderMetadata: cfg = _codex_config() - models = cfg["models"] or CODEX_DEFAULT_MODELS + models = list(cfg["models"]) return OAuthProviderMetadata( provider_id=CODEX_PROVIDER_ID, display_name="Codex/ChatGPT", @@ -43,7 +43,7 @@ class CodexOAuthProvider: model_provider_id=CODEX_PROVIDER_ID, icon="openai", auth_flow="device_code", - default_model=models[0] if models else "gpt-5.2-codex", + default_model=models[0] if models else CODEX_DEFAULT_MODEL, default_models=models, proxy_base_path=cfg["proxy_base_path"], callback_path=cfg["callback_path"], diff --git a/plugins/_oauth/helpers/providers/github_copilot.py b/plugins/_oauth/helpers/providers/github_copilot.py index 2adbc411f..bf60db627 100644 --- a/plugins/_oauth/helpers/providers/github_copilot.py +++ b/plugins/_oauth/helpers/providers/github_copilot.py @@ -35,7 +35,6 @@ COPILOT_HEADERS = { "Copilot-Integration-Id": "vscode-chat", } CURATED_MODELS = [ - "gpt-5.2-codex", "gpt-5.2", "claude-sonnet-4.5", "claude-opus-4.5", @@ -203,7 +202,7 @@ class GitHubCopilotOAuthProvider: model_provider_id=GITHUB_COPILOT_PROVIDER_ID, icon="github", auth_flow="device_code", - default_model="gpt-5.2-codex", + default_model=CURATED_MODELS[0], default_models=list(CURATED_MODELS), proxy_base_path="/oauth/github-copilot", supports_enterprise_domain=True, diff --git a/tests/test_oauth_codex.py b/tests/test_oauth_codex.py index 4a2fb31e6..89c0a6e17 100644 --- a/tests/test_oauth_codex.py +++ b/tests/test_oauth_codex.py @@ -15,6 +15,7 @@ import yaml sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from plugins._oauth.helpers import codex from plugins._oauth.helpers import routes +from plugins._oauth.helpers.providers import codex as codex_provider from plugins._oauth.extensions.python._functions.models.get_api_key.end import ( _20_oauth_account_dummy_key as oauth_dummy_key, ) @@ -90,6 +91,52 @@ def test_chat_messages_to_response_body_extracts_instructions(): assert body["reasoning"] == {"effort": "high"} +def test_chat_messages_to_response_body_uses_current_codex_default_model(): + body = codex.chat_messages_to_response_body( + { + "messages": [ + {"role": "user", "content": "Hello"}, + ], + } + ) + + assert body["model"] == "gpt-5.5" + + +def test_codex_provider_metadata_uses_upstream_models_only_by_default(monkeypatch): + monkeypatch.setattr( + codex_provider, + "_codex_config", + lambda: { + "models": [], + "proxy_base_path": "/oauth/codex", + "callback_path": "/auth/callback", + }, + ) + + metadata = codex_provider.CodexOAuthProvider().metadata() + + assert metadata.default_model == "gpt-5.5" + assert metadata.default_models == [] + + +def test_codex_fetch_models_omits_fake_client_version(monkeypatch): + calls = [] + + class FakeResponse: + ok = True + + def json(self): + return {"models": [{"slug": "upstream-model"}]} + + monkeypatch.setattr(codex, "codex_config", lambda: {"models": []}) + monkeypatch.setattr(codex, "resolve_codex_version", lambda: "") + monkeypatch.setattr(codex, "request_codex", lambda path, params=None: calls.append((path, params)) or FakeResponse()) + + assert codex.fetch_models() == ["upstream-model"] + assert calls == [("/models", None)] + + def test_chat_messages_to_response_body_preserves_image_parts_for_responses(): data_url = "data:image/png;base64,abcd" diff --git a/tests/test_oauth_github_copilot.py b/tests/test_oauth_github_copilot.py index 2382f8c87..6af8dea08 100644 --- a/tests/test_oauth_github_copilot.py +++ b/tests/test_oauth_github_copilot.py @@ -120,7 +120,7 @@ def test_models_returns_curated_list_without_network(monkeypatch): models = provider.models() - assert models[:3] == ["gpt-5.2-codex", "gpt-5.2", "claude-sonnet-4.5"] + assert models[:3] == ["gpt-5.2", "claude-sonnet-4.5", "claude-opus-4.5"] assert "grok-code-fast-1" in models