No description
  • Rust 98.3%
  • Python 1.7%
Find a file
Andrew Zah 1d9caaabfc
add temporary support for synthetic.new
* synthetic sends over `reasoning` instead of `reasoning_content`. fixes:
  1. add `reasoning` field to ChatDelta.
  2. check `reasoning` if `reasoning_content` isn't present
* synthetic doesn't seem to send "[DONE]". fixes:
  1. set `stream_err` bool on error in consuming the stream
  2. set `stream_done` if content was received without `[DONE]`
  this seems to work so far.
2026-05-26 13:46:18 +09:00
.claude feat: initial codex-bridge scaffold with streaming translation 2026-04-23 12:03:53 +08:00
.github/workflows fix: resolve 4 CodeQL security alerts (SSRF validation + workflow permissions) 2026-04-24 17:42:41 +08:00
codex_relay refactor: rename CODEX_BRIDGE_* env vars to CODEX_RELAY_* 2026-04-23 16:08:55 +08:00
src add temporary support for synthetic.new 2026-05-26 13:46:18 +09:00
tests Fix streaming usage and MCP namespaces 2026-05-24 12:23:41 +08:00
.gitignore feat: initial codex-bridge scaffold with streaming translation 2026-04-23 12:03:53 +08:00
Cargo.lock Release v0.2.1 2026-05-24 13:49:24 +08:00
Cargo.toml Release v0.2.1 2026-05-24 13:49:24 +08:00
LICENSE chore: add README, LICENSE, fix maturin config for binary wheel 2026-04-23 15:42:48 +08:00
pyproject.toml chore: add README, LICENSE, fix maturin config for binary wheel 2026-04-23 15:42:48 +08:00
README.md Release v0.2.1 2026-05-24 13:49:24 +08:00

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-config to 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

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_content across turns (Kimi k2.6, DeepSeek-R1)
  • Model catalog — proxies /v1/models from the upstream provider
  • Auto-config--print-config generates 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)
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.

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

  1. Add a debug dump to the relay (write body bytes from handle_responses to a file before parsing).
  2. Run a real codex exec against it; copy inbound_*.json to a new tests/fixtures/codex_<major>_<minor>_<patch>/ folder.
  3. Trim each payload down to the smallest one that exercises the feature you want to lock in.
  4. Add a row to tests/fixtures/VERSIONS.md and 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 and MCP namespace bug reports plus independent verification (#5, #6)

License

MIT