Files
codex-mmo/docs/PROVIDER_DRIVERS.md
T
2026-08-24 08:11:59 -07:00

14 KiB
Raw Blame History

Route drivers

Codex MMO is model-maker neutral, but protocols and access products are not interchangeable. In MMO generation 8, an executable route selects one driver and records its API operator, access product, wire protocol, billing mode, transport capabilities, credential names, and resource group.

The filename remains PROVIDER_DRIVERS.md for documentation organization only; the public catalog object is routes, not the old provider-shaped schema.

Driver matrix

Driver Execution path Typical use Managed gateway
codex_builtin Codex reserved provider ChatGPT-backed Codex models no
codex_custom Direct Codex Responses endpoint Conforming private/third-party Responses API no
codex_oss Codex native local integration Ollama or LM Studio no
switchyard Immutable snapshot-specific route Chat, Responses, Anthropic Messages, compatible local APIs yes
catalog_only Metadata only Media/service endpoint or unsupported transport no execution

Common route identity

[routes.example_openai_chat]
name = "Example API"
api_operator = "example"
access_product = "example_api"
wire_protocol = "openai_chat"
billing_mode = "api"
driver = "switchyard"
base_url = "https://models.example.test/v1"
credential_envs = ["EXAMPLE_API_KEY"]
resource_group = "example_pool"
transport_modalities = ["text"]
transport_output_modalities = ["text"]
tool_calling = true
parallel_tool_calls = false
max_retries = 1

Required identity fields are never inferred from a route key. A recognized field that does not apply to the selected driver is rejected instead of retained inertly.

codex_builtin

[routes.codex_chatgpt_builtin]
name = "Built-in Codex with ChatGPT authentication"
api_operator = "openai"
access_product = "chatgpt_codex"
wire_protocol = "codex_builtin"
billing_mode = "chatgpt_subscription"
driver = "codex_builtin"
provider_id = "openai"
auth = "chatgpt"
resource_group = "chatgpt_subscription"
transport_modalities = ["text", "image"]
tool_calling = true
parallel_tool_calls = true

This preserves Codexs native authentication, entitlement, tools, images, and active model catalog. A generated home uses the configured base homes file-backed auth.json according to auth_link_mode; Codex 0.149 keyring-only state cannot be transferred to another CODEX_HOME.

Account-visible models and capabilities remain authoritative. The bundled catalog is a release baseline, not an entitlement claim.

codex_custom

[routes.private_responses]
name = "Private Responses"
api_operator = "example"
access_product = "private_responses"
wire_protocol = "openai_responses"
billing_mode = "api"
driver = "codex_custom"
base_url = "https://llm.example.internal/v1"
wire_api = "responses"
credential_envs = ["PRIVATE_LLM_API_KEY"]
transport_modalities = ["text", "image"]
preserves_tool_media = true
tool_result_modalities = ["text", "image"]

[routes.private_responses.http_headers]
"X-Static-Tenant" = "engineering"

[routes.private_responses.env_http_headers]
"X-Private-Token" = "PRIVATE_HEADER_VALUE"

Use direct mode only when the endpoint faithfully implements Responses and direct transport is preferable to translation. The Codex process necessarily receives declared credential/header variables, so place sensitive work in a suitable OS boundary.

wire_api is currently responses. Direct retry fields are request_max_retries, stream_max_retries, and stream_idle_timeout_ms. Codex interprets zero as immediate expiry, not “disabled,” so route validation rejects it. Generated homes raise the effective idle floor to at least one hour and twice the role's warning-only stall interval. That value detects a genuinely broken stream; it is not a task deadline, goal budget, or instruction for the model to keep time.

codex_oss

[routes.ollama_codex_oss]
name = "Codex native Ollama OSS mode"
api_operator = "local"
access_product = "ollama"
wire_protocol = "codex_oss"
billing_mode = "local"
driver = "codex_oss"
provider_id = "ollama"
resource_group = "local_gpu_0"
transport_modalities = ["text"]

Supported provider_id values are ollama and lmstudio. Switchyard variants also exist for compatible local Chat endpoints when common routing and telemetry matter more than native transport.

switchyard

Switchyard 0.2.0 supports these route protocols:

  • openai_chat;
  • openai_responses;
  • anthropic_messages.

The compiler emits only routes used by one immutable snapshot. A profile with no Switchyard binding starts no gateway. Managed ingress is restricted to a loopback IP literal because the generated gateway has no client-auth layer.

Bundled Switchyard routes include:

  • OpenCode Go Chat, Responses, and Anthropic Messages through OPENCODE_API_KEY;
  • OpenCode Zen Chat, Responses, and Anthropic Messages through the same OPENCODE_API_KEY;
  • OpenRouter Chat through OPENROUTER_API_KEY;
  • Z.AI Coding Plan Chat and Responses through ZAI_CODING_API_KEY;
  • Z.AI General API through the separately explicit ZAI_API_KEY;
  • a bundled llama.cpp model binding;
  • direct OpenAI/Anthropic and LM Studio/Ollama route definitions for explicit user catalog extensions. A route without a model record is not an executable bundled model binding.

Switchyard receives extra_headers, not environment-backed per-header mappings. API authentication uses credential_envs. MMO emits max_retries = 1 by default, meaning one additional attempt; the accepted range is 010.

Temporary Codex MCP namespace bridge

Codex 0.149 represents MCP servers as Responses namespace tools. Switchyard 0.2.0's Responses decoder retains top-level function and ID tools but does not retain that namespace shape. For a gateway whose recorded executable version is exactly 0.2.0, MMO therefore asks the same Codex app-server for its granted MCP tool schemas, registers equivalent flat mmo_mcp__SERVER__TOOL dynamic functions when a thread is first created, and routes each dynamic call back through Codex's mcpServer/tool/call. The MMO-owned prefix is deliberate because Codex rejects dynamic declarations using its reserved native mcp__ prefix. Codex remains the MCP connection owner and enforces its configured server/tool allowlists; the bridge does not read supervisor files or connect to MCP servers independently. MCP text, image, audio, error state, and non-redundant structured content are preserved; a structured value is omitted only when an existing text block parses to that exact value (including the common single-key result wrapper), avoiding duplicate context without discarding distinct information. Durable Codex threads retain the dynamic tool declarations, while every replacement host reconstructs only the in-memory routing map.

This is a release-scoped compatibility shim, not a second permanent MCP architecture. It is disabled for every Switchyard version other than 0.2.0. The baseline/bridge guard test deliberately fails when the reviewed Switchyard baseline changes. Before accepting a newer baseline:

  1. prove with the exact candidate binary that Codex 0.149 namespace tools reach both the Go and Zen model routes and that a real MCP call completes;
  2. prove new-thread, resumed-thread, worker, allowlist, error, and cancellation behavior without dynamicTools;
  3. remove the bridge constants, dynamic schema discovery/call routing, root/worker injection, and their compatibility tests;
  4. update this section, the architecture/veracity records, changelog, package manifest, and installed runtime.

An operator-supplied newer Switchyard is never assigned the 0.2.0 shim merely because the profile uses the switchyard driver. Its native namespace path must pass live qualification before production use.

Open removal tracker

Upstream Switchyard PR #384, merged as commit c7beccd4891fa5cfe3a3b94fdd376f5765864507 on 2026-08-20, implements native Codex namespace preservation. The current v0.2.0 tag predates that commit, and no published tag contains it as of 2026-08-23. This tracker remains open until the first official switchyard-server release whose tag contains that commit is available.

The release containing that commit is an upgrade/removal trigger, not permission to retain both paths. The baseline bump must be one atomic change: run the qualification matrix above against the exact released binary, delete the dynamic bridge and its version constant, then update the pin. The existing test_temporary_switchyard_bridge_must_be_removed_when_baseline_advances test blocks a baseline-only update. If native qualification fails, keep 0.2.0 pinned and record the failure; do not extend the bridge to the newer release.

Z.AI Coding Plan uses distinct documented bases by wire protocol: Responses/Codex uses https://api.z.ai/api/v1, while OpenAI-compatible Chat uses https://api.z.ai/api/coding/paas/v4. Switchyard appends the protocol endpoint suffix. MMO does not retry one route through the other and does not fall back to the separately credentialed General API.

The Coding Plan Anthropic-compatible endpoint remains catalog-only: its documented Bearer authentication cannot be represented by Switchyard 0.2.0s fixed Anthropic x-api-key behavior. No hidden transport fallback is attempted.

catalog_only

Catalog-only records preserve complete inventory information without pretending the endpoint can run a Codex agent. Examples include OCR, image/video generation, audio transcription, hosted application agents, Google-native Zen models without an installed driver, and the incompatible Coding Plan Anthropic route.

[routes.zai_general_catalog]
name = "Z.AI media and specialist API catalog"
api_operator = "zai"
access_product = "zai_general_api"
wire_protocol = "catalog_only"
billing_mode = "catalog_only"
driver = "catalog_only"
transport_modalities = ["text", "image", "video", "audio", "file"]

[models.zai_general_catalog__glm_image]
maker = "zai"
route = "zai_general_catalog"
upstream_id = "glm-image"
kind = "image_generation"
agent_compatible = false

Driver-specific fields

Field Drivers Meaning
api_operator all API operator identity
access_product all Plan/service identity
wire_protocol all Exact protocol enum
billing_mode all api, subscription, chatgpt_subscription, local, or catalog_only
provider_id built-in/OSS Codex-owned provider identifier
base_url custom/Switchyard/catalog-only Absolute HTTP(S) API root
credential_envs custom/Switchyard/catalog-only Ordered valid environment names
wire_api custom Direct Codex API; responses
http_headers custom Static Codex headers
env_http_headers custom Header name to environment name
extra_headers Switchyard Static Switchyard headers
max_retries Switchyard Additional attempts, 010
request_max_retries custom Direct request retries
stream_max_retries custom Stream reconnections
stream_idle_timeout_ms custom Provider stream-idle floor (at least 1,000 ms); generated app-server configs raise it through the role's execution window plus finalization grace when that is longer
transport_modalities all Inputs the complete path preserves
transport_output_modalities all Outputs the complete path preserves
preserves_tool_media all Whether tool media survives transport
tool_result_modalities all Preserved tool-result media types
supports_documents all Complete file/document path
openrouter_policy OpenRouter only Route-wide typed serving policy

There are no singular credential aliases or legacy header-name aliases in the v2 contract. Use exactly credential_envs, http_headers, env_http_headers, and extra_headers for their documented drivers.

base_url must be an absolute HTTP(S) root with a nonempty host. Userinfo, control/space characters, backslashes, queries, and fragments are rejected. Header names and values are validated before downstream configuration is generated.

Model transport fields

[models.example_openai_chat__example_coder]
maker = "example"
route = "example_openai_chat"
upstream_id = "example-coder"
kind = "chat"
agent_compatible = true
modalities = ["text"]
output_modalities = ["text"]
tool_calling = true
parallel_tool_calls = false
structured_output = true
context_window = 200000
max_output_tokens = 32000
reasoning_levels = ["none", "high"]
default_reasoning = "high"
resource_group = "example_pool"

The compiler checks model capabilities against route capabilities. A maker/model name alone is never evidence that a particular transport preserves tools, images, documents, reasoning selectors, or structured output. extra_body is supported only for Switchyard-backed model targets and must contain finite JSON-compatible values.

Credentials

Credential assignments belong in ~/.config/codex-mmo/credentials.env or the launch environment. Profiles, catalogs, snapshots, and generated static configuration store names, never values. Worker/gateway environments filter conventional unrelated credential names and explicitly re-admit only the selected route and Tool MCP variables.

That filtering is name-based, not an OS sandbox or secret oracle. Use conventional secret names and sanitize unusual variables before launching untrusted work.

Adding a route

Add a generation-8 fragment under ~/.config/codex-mmo/catalog.d/, then run:

codex-mmo catalog verify
codex-mmo catalog routes
codex-mmo catalog models --route ROUTE_KEY
codex-mmo profile validate PROFILE

No runtime plugin is needed when an existing driver fits. Executable driver plugins are intentionally outside profile packs.