Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions agentkit/toolkit/cli/sandbox/model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 = [
Expand Down Expand Up @@ -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",
Expand Down
28 changes: 28 additions & 0 deletions tests/toolkit/cli/test_cli_create_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down