Responses API ↔ Chat Completions translation bridge for Codex CLI
Project description
codex-relay
A lightweight Rust proxy that translates the OpenAI Responses API (used by Codex CLI) into the Chat Completions API, letting Codex work with any OpenAI-compatible provider — DeepSeek, Kimi, Qwen, Mistral, Groq, xAI, OpenRouter, and more.
Why
Codex CLI speaks the OpenAI Responses API, which is an OpenAI-proprietary stateful protocol. Every other provider exposes the standard Chat Completions API. codex-relay sits between Codex and your chosen provider, translating on the fly — no code changes to Codex required.
Install
# From PyPI — prebuilt binary for your platform
pip install codex-relay
# From crates.io
cargo install codex-relay
Quick start
1. Start the relay
CODEX_RELAY_UPSTREAM=https://api.deepseek.com/v1 \
CODEX_RELAY_API_KEY=$DEEPSEEK_API_KEY \
CODEX_RELAY_PORT=4446 \
codex-relay
On startup, the relay logs the available upstream models and prints a hint:
ℹ upstream models: deepseek-chat, deepseek-reasoner
⚠ To configure Codex with model metadata, run: codex-relay --print-config --upstream ...
2. Generate your Codex config
codex-relay --print-config \
--upstream https://api.deepseek.com/v1 \
--api-key $DEEPSEEK_API_KEY
This prints a ready-to-use ~/.codex/config.toml snippet that includes
model_properties for every upstream model, so Codex knows model capabilities
and you won't see the "Model metadata … not found" warning.
If you prefer to write the config by hand, here is the minimal form:
model = "deepseek-chat"
model_provider = "deepseek-relay"
[model_providers.deepseek-relay]
name = "DeepSeek"
base_url = "http://127.0.0.1:4446/v1"
wire_api = "responses"
env_key = "DEEPSEEK_API_KEY"
[model_properties."deepseek-chat"]
context_window = 262144
max_context_window = 1048576
supports_parallel_tool_calls = true
supports_reasoning_summaries = false
input_modalities = ["text"]
⚠️ Without
model_properties, Codex CLI defaults to fallback metadata for any model it doesn't recognize natively. This can degrade performance, tool-call reliability, and context-window management. The relay logs a reminder at startup and offers--print-configto eliminate this class of problem entirely.
3. Use Codex normally — it routes through the relay transparently.
CLI reference
| Flag | Env var | Default | Description |
|---|---|---|---|
--port |
CODEX_RELAY_PORT |
4444 |
Listen port |
--upstream |
CODEX_RELAY_UPSTREAM |
https://openrouter.ai/api/v1 |
Upstream Chat Completions base URL |
--api-key |
CODEX_RELAY_API_KEY |
(empty) | API key forwarded to upstream |
--model-map |
CODEX_RELAY_MODEL_MAP |
(empty) | Comma-separated source:target model name translations |
--print-config |
(none) | — | Print a Codex config snippet with model_properties and exit |
--session-ttl-hours |
CODEX_RELAY_SESSION_TTL_HOURS |
168 |
Retain idle previous_response_id history and reasoning state for this many hours |
--max-sessions |
CODEX_RELAY_MAX_SESSIONS |
256 |
Maximum completed response histories retained for continuation |
--max-session-memory-mb |
CODEX_RELAY_MAX_SESSION_MEMORY_MB |
512 |
Approximate memory budget for retained session/reasoning state |
Supported providers
| Provider | Base URL | Suggested port |
|---|---|---|
| DeepSeek | https://api.deepseek.com/v1 |
4446 |
| Kimi (Moonshot) | https://api.moonshot.cn/v1 |
4447 |
| Qwen | https://dashscope.aliyuncs.com/compatible-mode/v1 |
4448 |
| Mistral | https://api.mistral.ai/v1 |
4449 |
| Groq | https://api.groq.com/openai/v1 |
4450 |
| xAI | https://api.x.ai/v1 |
4451 |
| OpenRouter | https://openrouter.ai/api/v1 |
4452 |
Any OpenAI-compatible endpoint works.
Features
- Streaming — full SSE streaming with correct event sequencing
- Tool calls — accumulates streaming deltas and emits structured function_call items
- Parallel tool calls — consecutive function_call input items merged into one assistant message
- Reasoning models — preserves
reasoning_contentacross turns (Kimi k2.6, DeepSeek-R1) - Model catalog — proxies
/v1/modelsfrom the upstream provider - Auto-config —
--print-configgenerates a complete Codex config with model metadata
Configuration
| Variable | Default | Description |
|---|---|---|
CODEX_RELAY_PORT |
4444 |
Port to listen on |
CODEX_RELAY_UPSTREAM |
https://openrouter.ai/api/v1 |
Upstream Chat Completions base URL |
CODEX_RELAY_API_KEY |
(empty) | API key forwarded to upstream |
CODEX_RELAY_MODEL_MAP |
(empty) | Comma-separated source:target model name translations (e.g., gpt-5.4:deepseek-v4-pro) |
CODEX_RELAY_TOOL_DENYLIST |
(empty) | Comma-separated tool names to remove before forwarding tools to the upstream model |
CODEX_RELAY_SESSION_TTL_HOURS |
168 |
Retain idle session/reasoning state for this many hours |
CODEX_RELAY_MAX_SESSIONS |
256 |
Maximum completed response histories retained for previous_response_id |
CODEX_RELAY_MAX_SESSION_MEMORY_MB |
512 |
Approximate memory budget for retained session/reasoning state |
CODEX_RELAY_HISTORY_STORE |
memory |
Retained history backend: memory or disk |
CODEX_RELAY_HISTORY_DIR |
.codex-relay-history |
Directory for disk-backed history records |
RUST_LOG |
codex_relay=info |
Log verbosity |
Python API
from codex_relay import start
proc = start(port=4446, upstream="https://api.deepseek.com/v1", api_key="sk-...")
# ... use Codex ...
proc.terminate()
Testing
Two layers — offline tests pin behavior against captured Codex wire-shape; live tests pin behavior against real provider APIs.
Debugging tool round-trips
For tool-routing issues, enable debug logs:
RUST_LOG=codex_relay=debug codex-relay
The relay logs tool names only, never tool arguments or message content:
response tools=...— tools received from Codex's Responses API requestupstream tools=...— tools forwarded to the Chat Completions upstreamupstream function_calls=...— function calls returned by a blocking upstream responseupstream stream function_calls=...— function calls returned by a streaming upstream response
These lines are useful for checking whether a tool such as spawn_agent
was preserved by the relay, and whether the failure happened before or after
the model selected that tool.
Disk-backed history
By default, codex-relay keeps retained previous_response_id histories and
reasoning lookups in memory. For longer-running processes or deeper debugging,
you can opt into an inspectable on-disk store:
CODEX_RELAY_HISTORY_STORE=disk \
CODEX_RELAY_HISTORY_DIR=.codex-relay-history \
codex-relay
The disk backend writes JSON records under:
.codex-relay-history/
sessions/
reasoning/
turns/
Session records contain the translated Chat Completions messages retained for
a response id. Reasoning records keep call-id and turn-fingerprint lookups used
to round-trip provider reasoning content. The relay keeps only an in-memory
index for disk-backed entries and loads payloads on demand.
Treat this directory as sensitive: records may contain prompts, tool outputs, and other conversation data. The same TTL/count/byte retention knobs apply to disk-backed records, and evicted entries are removed from disk.
Subagent tool routing
Codex subagent tools such as spawn_agent, wait_agent, and close_agent
are runtime tools. The relay can preserve them in the tool schema and round-trip
the model's selected function call, but it cannot reliably detect whether the
local Codex app-server daemon is new enough to execute those calls.
If Codex shows unsupported call: spawn_agent, first verify that the Codex CLI
and app-server daemon versions match. A stale daemon can expose a newer tool
schema to the model while lacking the handler that executes the returned call.
Also check your Codex config: [features] subagents = true is not recognized;
use [features] multi_agent = true only if you need to override the default.
As an escape hatch for affected runtimes, remove unsupported tools before they reach the upstream model:
CODEX_RELAY_TOOL_DENYLIST=spawn_agent,wait_agent,close_agent codex-relay
The denylist matches the tool name forwarded to Chat Completions. Namespaced
MCP tools use their flattened name, for example
mcp__codex_apps__github-_fetch_issue.
Offline (always green, default cargo test)
Replays Codex CLI fixtures through the translation layer and asserts
role/tool/reasoning behavior. Each fixture pins a Codex CLI version under
tests/fixtures/codex_<major>_<minor>_<patch>/.
cargo test
Live (gated on provider API key, #[ignore] by default)
Spawns the relay binary on a random port, points it at the real provider, and
exercises /v1/models, blocking + streaming, tool calls, and (for thinking
models) the reasoning_content round-trip via an in-process recording proxy.
DEEPSEEK_API_KEY=sk-... cargo test --test compat_deepseek_live -- --ignored --test-threads=1
Regenerating fixtures after a Codex upgrade
- Add a debug dump to the relay (write
bodybytes fromhandle_responsesto a file before parsing). - Run a real
codex execagainst it; copyinbound_*.jsonto a newtests/fixtures/codex_<major>_<minor>_<patch>/folder. - Trim each payload down to the smallest one that exercises the feature you want to lock in.
- Add a row to
tests/fixtures/VERSIONS.mdand a test pointing at the new directory.
The old fixture directory stays as a regression net so the relay keeps working with the previous Codex CLI release.
Disclaimer
This project is not affiliated with, endorsed by, or sponsored by OpenAI. "Codex" refers to OpenAI Codex CLI, an open-source project licensed under Apache-2.0. codex-relay is an independent, community-built translation proxy.
Contributors
- myk5010 — system/developer message ordering fix and model name mapping (#4)
- qcnhy — streaming usage, MCP namespace bug reports, namespace tool-routing analysis, and independent verification (#5, #6, #17)
- JasonC93 — subagent tool-routing and spawned-agent context isolation reports (#10, #12)
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codex_relay-0.3.1-py3-none-win_amd64.whl.
File metadata
- Download URL: codex_relay-0.3.1-py3-none-win_amd64.whl
- Upload date:
- Size: 2.7 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b419af627c701e65463b78084c9b2918f3bb4d1bf24b18c11ef494bd4fd6ac28
|
|
| MD5 |
63a609ebec0561e81b3e74ea8c384824
|
|
| BLAKE2b-256 |
910236d3a7d59297c9b61d4dca1c3241a619f60c2c36357c2f2bea96189bda2a
|
Provenance
The following attestation bundles were made for codex_relay-0.3.1-py3-none-win_amd64.whl:
Publisher:
publish.yml on MetaFARS/codex-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codex_relay-0.3.1-py3-none-win_amd64.whl -
Subject digest:
b419af627c701e65463b78084c9b2918f3bb4d1bf24b18c11ef494bd4fd6ac28 - Sigstore transparency entry: 1719430688
- Sigstore integration time:
-
Permalink:
MetaFARS/codex-relay@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/MetaFARS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Trigger Event:
push
-
Statement type:
File details
Details for the file codex_relay-0.3.1-py3-none-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: codex_relay-0.3.1-py3-none-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 3.2 MB
- Tags: Python 3, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3745fa8e0183cd974dcef39d1a1bf034f1873e1b3d14b1ad75693aaf8b62109
|
|
| MD5 |
4bc562db4bb7a5d2ce5b41020b5bf59a
|
|
| BLAKE2b-256 |
8864910b29f9cbfc5d6c78d254161a2697d0399b403a390f5212d2b37fb02c17
|
Provenance
The following attestation bundles were made for codex_relay-0.3.1-py3-none-manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on MetaFARS/codex-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codex_relay-0.3.1-py3-none-manylinux_2_28_x86_64.whl -
Subject digest:
b3745fa8e0183cd974dcef39d1a1bf034f1873e1b3d14b1ad75693aaf8b62109 - Sigstore transparency entry: 1719430336
- Sigstore integration time:
-
Permalink:
MetaFARS/codex-relay@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/MetaFARS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Trigger Event:
push
-
Statement type:
File details
Details for the file codex_relay-0.3.1-py3-none-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: codex_relay-0.3.1-py3-none-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 3.2 MB
- Tags: Python 3, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e15d72a28b5634e1391bd71884b5b6e718ed6cbb4b1eefc66bc8295f278107d
|
|
| MD5 |
7eab4d7ef4e72381fb1ae582e49485ac
|
|
| BLAKE2b-256 |
3f24d620daef9c506b766e263fb1fa5278ab1d126676fab352bc78ed14a2997a
|
Provenance
The following attestation bundles were made for codex_relay-0.3.1-py3-none-manylinux_2_28_aarch64.whl:
Publisher:
publish.yml on MetaFARS/codex-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codex_relay-0.3.1-py3-none-manylinux_2_28_aarch64.whl -
Subject digest:
7e15d72a28b5634e1391bd71884b5b6e718ed6cbb4b1eefc66bc8295f278107d - Sigstore transparency entry: 1719430570
- Sigstore integration time:
-
Permalink:
MetaFARS/codex-relay@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/MetaFARS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Trigger Event:
push
-
Statement type:
File details
Details for the file codex_relay-0.3.1-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: codex_relay-0.3.1-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.0 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
181ec25eaa2850dc9346c6398c34671c9e55abb7ad539e6c3339b49591e22b59
|
|
| MD5 |
19f7c456280b0505af9cb532b400d888
|
|
| BLAKE2b-256 |
b472d6989534a35e4a0d60619606aa57ef688835fd42cb1c289d5e78367b85b9
|
Provenance
The following attestation bundles were made for codex_relay-0.3.1-py3-none-macosx_11_0_arm64.whl:
Publisher:
publish.yml on MetaFARS/codex-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codex_relay-0.3.1-py3-none-macosx_11_0_arm64.whl -
Subject digest:
181ec25eaa2850dc9346c6398c34671c9e55abb7ad539e6c3339b49591e22b59 - Sigstore transparency entry: 1719430453
- Sigstore integration time:
-
Permalink:
MetaFARS/codex-relay@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/MetaFARS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Trigger Event:
push
-
Statement type:
File details
Details for the file codex_relay-0.3.1-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: codex_relay-0.3.1-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 3.1 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2414c972b76af029fa88e94ff5cd78c0960553737df79be016a0f6b1809770c0
|
|
| MD5 |
4f025a5f106f2f96ae62ac011089de59
|
|
| BLAKE2b-256 |
1e3ac3311229802678711b5d8c69f15dd3e9f8fd0d902d40c9d88544ff9413c0
|
Provenance
The following attestation bundles were made for codex_relay-0.3.1-py3-none-macosx_10_12_x86_64.whl:
Publisher:
publish.yml on MetaFARS/codex-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codex_relay-0.3.1-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
2414c972b76af029fa88e94ff5cd78c0960553737df79be016a0f6b1809770c0 - Sigstore transparency entry: 1719430906
- Sigstore integration time:
-
Permalink:
MetaFARS/codex-relay@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/MetaFARS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d5525e460222e909d1ab4c3793b37334f9cfd76b -
Trigger Event:
push
-
Statement type: