DEV Community

Cover image for Codex CLI with a Third-Party Model: Four Checks Beyond Saving `config.toml`
boluo
boluo

Posted on

Codex CLI with a Third-Party Model: Four Checks Beyond Saving `config.toml`

A saved configuration is only the first checkpoint. To determine whether a third-party model is usable in Codex CLI, I separated the test into four questions:

  1. Does the current account expose the intended model ID?
  2. Does the API return visible text, not only HTTP 200?
  3. Does Codex load the intended model and provider through a Profile?
  4. Can the local command policy allow the requested tool action?

The environment was Windows PowerShell with Codex CLI 0.168.0. The configured model was deepseek-v4.1-flash, the Base URL was https://api.crazyrouter.com/v1, and the API key was read from CRAZYROUTER_API_KEY.

Check 1: verify the CLI and the current model catalog

Start with the installed Codex version:

codex --version
Enter fullscreen mode Exit fullscreen mode

The saved run returned codex-cli 0.168.0.

Codex CLI 0.168.0 returned by codex --version

The model catalog is the next boundary. The request returned HTTP 200 and 170 models. When 12 IDs from an older list were checked, only seven were still present:

  • deepseek-v4-pro
  • deepseek-v4-flash
  • deepseek-v4.1-flash
  • kimi-k2.8-preview
  • kimi-k2.7-code
  • glm-5.3
  • glm-5.3-flash

Five IDs were missing: doubao-seed-2.1-pro, doubao-seed-2.1-lite, doubao-seed-2.0-mini, doubao-seed-evolving, and minimax-m3.

The catalog returned 170 models and matched 7 of 12 checked IDs

This result confirms catalog access. It does not prove that every listed model can complete an inference request, so I did not generate a large set of Profiles from the catalog alone.

Put the provider in config.toml, not in every Profile

The provider definition used these values:

model = "deepseek-v4.1-flash"
model_provider = "crazyrouter"
forced_login_method = "api"

[model_providers.crazyrouter]
name = "Crazyrouter Gateway"
base_url = "https://api.crazyrouter.com/v1"
wire_api = "responses"
env_key = "CRAZYROUTER_API_KEY"
Enter fullscreen mode Exit fullscreen mode

The service address can be checked in the Crazyrouter documentation. The Base URL stops at /v1; Codex or the test client appends the request path.

The separate ds41-flash.config.toml Profile contained only:

model = "deepseek-v4.1-flash"
model_provider = "crazyrouter"
Enter fullscreen mode Exit fullscreen mode

The provider configuration and the separate ds41-flash Profile

This keeps the gateway and authentication settings in one place. A second model can use another small Profile without duplicating the provider block.

Store the key in the Windows user environment as CRAZYROUTER_API_KEY. After saving it, close the old PowerShell window and open a new one. Check only whether the value exists:

if ([string]::IsNullOrWhiteSpace($env:CRAZYROUTER_API_KEY)) { "not set" } else { "set" }
Enter fullscreen mode Exit fullscreen mode

Do not print the complete key or include it in screenshots and logs. Seeing set only proves that the current process can read the variable; the API request still has to validate it.

Check 2: require visible output after HTTP 200

The minimal prompt asked for OK. The recorded deepseek-v4.1-flash results were:

  • Responses API: HTTP 200, 2,249 ms, 35 input tokens, 30 output tokens, visible OK
  • Chat Completions API: HTTP 200, 2,394 ms, 35 prompt tokens, 38 completion tokens, visible OK

Both APIs returned HTTP 200 and visible OK output

Codex used wire_api = "responses", but testing both interfaces made the distinction between routing and usable text easier to see.

glm-5.3-flash produced a different outcome. With a 16-token output limit, reasoning consumed almost all available output and no final OK appeared. Increasing the limit to 64 tokens still produced no visible answer. That is evidence of successful authentication and routing under the tested conditions, not a completed response.

The short DeepSeek requests each used 35 input tokens. The full Profile session was much larger: 91,635 input tokens and 850 output tokens. It loaded session context and attempted several tool calls, so an API smoke test is a cheaper place to reject a model that cannot produce a minimal visible answer.

Check 3: start Codex with the complete Profile

From the project directory, run:

codex --profile ds41-flash
Enter fullscreen mode Exit fullscreen mode

After startup, verify both the model and provider. --profile selects the pair together; -m alone does not carry the complete provider selection.

The validation task asked Codex to read the current project's README.md and return the project purpose. The real session record showed that the ds41-flash Profile loaded, the selected model was deepseek-v4.1-flash, and Codex started a thread and a turn.

Check 4: separate model connectivity from local tool permission

The model attempted four commands:

  1. Get-Content -LiteralPath README.md -TotalCount 60
  2. cmd /c dir
  3. echo hi
  4. Get-Content README.md

The local command policy rejected all four, so Codex never read the file.

Sanitized summary from the real Codex Profile session record

This is the important boundary in the test. The Profile loaded, the model request happened, and the tool-call flow began. The file task did not complete. Changing the model ID again would not address the observed failure; the next layer to inspect is the local sandbox, approval, or command policy.

The same run reported Model metadata not found; using fallback metadata. A third-party model that is not recognized by Codex's built-in catalog may use fallback metadata. While that warning remains, context-window behavior, feature flags, and token estimates should not be assumed to match the model's published parameters exactly.

Map the symptom to the failing layer

  • 401 Unauthorized: reopen PowerShell, confirm the environment variable exists, and check the key and gateway account.
  • model not found: use the exact ID from the current catalog instead of an older list.
  • HTTP 200 with no visible body: inspect or raise the output limit, or test another model.
  • The wrong model starts: check the Profile filename, model, and model_provider together.
  • Fallback metadata warning: limit conclusions to behavior that was actually tested.
  • A tool command is rejected: inspect the local sandbox, approval, and command policy before changing the model.
  • A configuration edit does not appear: exit Codex, open a new PowerShell window, and start the Profile again.

The result of the four checks

The model catalog was reachable, deepseek-v4.1-flash returned visible output through both tested APIs, and ds41-flash entered the Codex tool-call flow. The reusable setup is to define the provider once in config.toml, keep the model selection in a separate Profile, and launch it with codex --profile ds41-flash.

The README.md task remains incomplete because four local commands were rejected. The next valid test is the same task after the local command policy is resolved, not another round of model-name changes.

References: OpenAI Codex configuration reference and OpenAI Codex CLI reference.

Top comments (0)