From a78631879d8935a78fd88d304745a25cf9199fa8 Mon Sep 17 00:00:00 2001 From: dinghengda Date: Wed, 1 Jul 2026 20:30:38 +0800 Subject: [PATCH] sandbox: openai model provider uses ChatGPT oauth (requires_openai_auth) --model-provider openai now emits requires_openai_auth = true (and no env_key) so codex authenticates that provider with the injected ChatGPT oauth token instead of an api key. --model-name and --model-base-url pass through; the model and base_url default to codex's ChatGPT endpoint. Ark providers (model_square / coding_plan / agent_plan) are unchanged. --- agentkit/toolkit/cli/sandbox/model_config.py | 40 ++++++++++++++++---- tests/toolkit/cli/test_cli_create_tool.py | 28 ++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/agentkit/toolkit/cli/sandbox/model_config.py b/agentkit/toolkit/cli/sandbox/model_config.py index e54865b..d8fb791 100644 --- a/agentkit/toolkit/cli/sandbox/model_config.py +++ b/agentkit/toolkit/cli/sandbox/model_config.py @@ -41,7 +41,15 @@ CODEX_CONFIG_TOML_ENV = "CODEX_CONFIG_TOML" CODEX_MODEL_CATALOG_JSON_ENV = "CODEX_MODEL_CATALOG_JSON" CODEX_MODEL_CATALOG_PATH = f"{CODE_ENV_CODEX_HOME}/model-catalog.json" +# Reserved provider ids that authenticate with the user's ChatGPT OAuth login (auth.json, +# injected by `agentkit sandbox codex-login`) instead of an API key. codex uses this via the +# `requires_openai_auth` provider field, so the config carries no `env_key` for these. CODEX_RESERVED_MODEL_PROVIDER_IDS = {"openai"} +# codex's built-in ChatGPT-subscription endpoint - the default base_url for an openai-auth provider +# when the caller does not pass --model-base-url (e.g. a regional proxy in front of OpenAI). +CODEX_CHATGPT_BASE_URL = "https://chatgpt.com/backend-api/codex" +# Default model for an openai-auth provider when --model-name is omitted. +DEFAULT_OPENAI_AUTH_MODEL = "gpt-5-codex" class ModelProviderType(str, Enum): @@ -303,6 +311,15 @@ def codex_model_provider_id( return resolved_provider +def provider_requires_openai_auth( + model_provider: str | ModelProviderType | None, +) -> bool: + """Whether this provider authenticates with the user's ChatGPT OAuth login (auth.json) + instead of an API key. True for the reserved ``openai`` provider - codex then uses the token + injected by ``agentkit sandbox codex-login`` and the config carries no ``env_key``.""" + return normalize_model_provider(model_provider) in CODEX_RESERVED_MODEL_PROVIDER_IDS + + def is_custom_model_base_url( *, model_provider: str | ModelProviderType | None, @@ -338,14 +355,21 @@ def build_codex_config_toml( model_base_url: Optional[str] = None, ) -> str: resolved_provider = normalize_model_provider(model_provider) + requires_openai_auth = provider_requires_openai_auth(resolved_provider) config = get_model_provider_config_if_known(resolved_provider) resolved_model_base_url = normalize_model_base_url(model_base_url) - provider_base_url = resolved_model_base_url or ( - config.model_base_url - if config - else MODEL_PROVIDER_CONFIGS[DEFAULT_MODEL_PROVIDER].model_base_url - ) - resolved_model_name = resolve_model_name(model_name, resolved_provider) + if requires_openai_auth: + # ChatGPT-subscription provider: default to codex's built-in ChatGPT endpoint (or the + # caller's --model-base-url, e.g. a regional proxy) and a ChatGPT model. + provider_base_url = resolved_model_base_url or CODEX_CHATGPT_BASE_URL + resolved_model_name = (model_name or "").strip() or DEFAULT_OPENAI_AUTH_MODEL + else: + provider_base_url = resolved_model_base_url or ( + config.model_base_url + if config + else MODEL_PROVIDER_CONFIGS[DEFAULT_MODEL_PROVIDER].model_base_url + ) + resolved_model_name = resolve_model_name(model_name, resolved_provider) resolved_codex_provider = codex_model_provider_id(resolved_provider) quoted_model = _toml_quote(resolved_model_name) lines = [ @@ -374,7 +398,9 @@ def build_codex_config_toml( f"name = {_toml_quote(resolved_codex_provider)}", f"base_url = {_toml_quote(provider_base_url)}", 'wire_api = "responses"', - 'env_key = "CODEX_API_KEY"', + # OAuth (ChatGPT login) providers carry no API key - codex uses the injected + # auth.json via `requires_openai_auth`; all others use the CODEX_API_KEY env. + ("requires_openai_auth = true" if requires_openai_auth else 'env_key = "CODEX_API_KEY"'), "", "[tui]", "show_tooltips = false", diff --git a/tests/toolkit/cli/test_cli_create_tool.py b/tests/toolkit/cli/test_cli_create_tool.py index 883b387..2c55785 100644 --- a/tests/toolkit/cli/test_cli_create_tool.py +++ b/tests/toolkit/cli/test_cli_create_tool.py @@ -817,6 +817,34 @@ def test_build_create_tool_request_renames_reserved_codex_provider(monkeypatch): assert "[model_providers.openai]" not in envs["CODEX_CONFIG_TOML"] assert "model_catalog_json" not in envs["CODEX_CONFIG_TOML"] assert "CODEX_MODEL_CATALOG_JSON" not in envs + # OAuth (ChatGPT) provider: authenticates with the injected auth.json, never an API key, + # and passes model-name / base-url through. + assert "requires_openai_auth = true" in envs["CODEX_CONFIG_TOML"] + assert "env_key" not in envs["CODEX_CONFIG_TOML"] + assert 'base_url = "https://models.example.com/v1"' in envs["CODEX_CONFIG_TOML"] + assert 'model = "custom-model"' in envs["CODEX_CONFIG_TOML"] + + +def test_build_codex_config_toml_openai_auth_defaults(): + from agentkit.toolkit.cli.sandbox import model_config + + # openai provider, no --model-name / --model-base-url -> ChatGPT defaults, OAuth auth + toml = model_config.build_codex_config_toml("", model_provider="openai") + assert "requires_openai_auth = true" in toml + assert 'env_key = "CODEX_API_KEY"' not in toml + assert f'base_url = "{model_config.CODEX_CHATGPT_BASE_URL}"' in toml + assert f'model = "{model_config.DEFAULT_OPENAI_AUTH_MODEL}"' in toml + assert model_config.provider_requires_openai_auth("openai") is True + + +def test_build_codex_config_toml_volc_provider_keeps_api_key(): + from agentkit.toolkit.cli.sandbox import model_config + + # a built-in Ark provider still authenticates with the CODEX_API_KEY env, unchanged + toml = model_config.build_codex_config_toml("", model_provider="agent_plan") + assert 'env_key = "CODEX_API_KEY"' in toml + assert "requires_openai_auth" not in toml + assert model_config.provider_requires_openai_auth("agent_plan") is False def test_build_create_tool_request_allows_arbitrary_model_provider_without_base_url(