Standalone FastAPI proxy service that makes Chat Completions style providers such as DeepSeek usable through the OpenAI Responses API.
It accepts Responses API style requests, forwards them to an upstream chat/completions API, and converts the result back into Responses API style responses. The main goal of this project is to let services that only expose Chat API semantics behave like a Responses API endpoint for local Codex/OpenAI-compatible workflows.
License: Apache-2.0. See LICENSE.
- Provides
/health,/v1/models, and/v1/responses - Loads local YAML config instead of depending on an external project config store
- Supports multiple upstream providers
- Supports multiple
base_urlsper provider with round-robin selection - Allows per-request provider override through the
providerfield - Keeps SSE streaming support
Codex_ChatAPI/
├── app/
│ ├── config.py
│ ├── main.py
│ └── service.py
├── config/
├── docs/
│ └── README.zh-CN.md
├── examples/
│ └── providers.yaml
├── scripts/
│ ├── init_config.sh
│ └── start.sh
├── requirements.txt
└── README.md
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtThe recommended way is to initialize the local config from the example file, then start the service with the helper script:
bash scripts/init_config.sh
bash scripts/start.sh deepseek 8000This is the preferred flow because it keeps config/providers.yaml local, uses the example template under examples/, and makes provider selection explicit at startup.
On first setup, copy the example config into config/:
bash scripts/init_config.shIf you want to overwrite an existing local config:
bash scripts/init_config.sh --forceThen edit config/providers.yaml.
Example:
default_provider: deepseek
providers:
deepseek:
api_key: "sk-xxxx"
model: "deepseek-chat"
timeout: 300
base_urls:
- "https://api.deepseek.com"
custom_vendor:
api_key: "sk-xxxx"
model: "gpt-4.1"
timeout: 300
base_urls:
- "https://proxy-a.example.com"
- "https://proxy-b.example.com"Fields:
default_provider: default provider nameproviders.<name>.api_key: default API key for that providerproviders.<name>.model: default model for that providerproviders.<name>.timeout: request timeout in secondsproviders.<name>.base_urls: one or more upstream base URLs
Important:
- Configure each upstream
base_urlas the provider's API root ending at/v1 - Do not set it to
/v1/responses, because/v1/responsesis this proxy service's own endpoint - Example: use
https://api.deepseek.comorhttps://openrouter.ai/api, and the proxy will normalize them to the upstream/v1path
bash scripts/start.sh deepseek 8000Arguments:
- First argument: default provider, for example
deepseek - Second argument: listening port, for example
8000
Environment variables also work:
CODEX_PROVIDER=openrouter PORT=8010 bash scripts/start.shuvicorn app.main:app --host 0.0.0.0 --port 8000To use another config file:
CODEX_CONFIG_PATH=/path/to/providers.yaml uvicorn app.main:app --host 0.0.0.0 --port 8000After this service is running, you can point Codex at it through config.toml.
Example for deepseek-v4-flash:
model = "deepseek-v4-flash"
model_provider = "dashscope_http"
[model_providers.dashscope_http]
name = "DashScope HTTP"
base_url = "http://<host>:<port>/v1"
wire_api = "responses"
supports_websockets = falseNotes:
base_urlshould point to this proxy service, for examplehttp://127.0.0.1:8000/v1wire_api = "responses"tells Codex to use theResponses APIagainst this proxy
By default, Codex usually keeps CODEX_HOME under the user's home directory (~).
If you want to customize it, set the environment variable before starting Codex:
export CODEX_HOME=<custom-dir>/.codex_homeThen start Codex normally.
curl http://127.0.0.1:8000/healthDefault provider:
curl http://127.0.0.1:8000/v1/modelsSpecific provider:
curl "http://127.0.0.1:8000/v1/models?provider=deepseek"curl http://127.0.0.1:8000/v1/responses \
-H 'Content-Type: application/json' \
-d '{
"provider": "deepseek",
"model": "deepseek-chat",
"input": "Hello, introduce yourself",
"stream": false
}'For streaming responses, set "stream": true. The proxy will call the upstream Chat Completions stream and re-emit it as Responses API style SSE events such as response.created, response.output_text.delta, response.output_text.done, and response.completed.
curl -N http://127.0.0.1:8000/v1/responses \
-H 'Content-Type: application/json' \
-d '{
"provider": "deepseek",
"model": "deepseek-chat",
"input": "Write a short hello world program in Python",
"stream": true
}'In streaming mode:
- The response uses
text/event-stream - Text is emitted incrementally through
Responses APIstyle SSE events - The stream ends with
response.completed, followed bydata: [DONE]
Request priority:
- Request body
provider CODEX_PROVIDERfrom the start commanddefault_providerin YAML
- The upstream service must support OpenAI-style
/v1/chat/completionsand/v1/models - If a provider defines multiple
base_urls, the service rotates them per request - Client requests should call this project at
/v1/responses, while configured upstreambase_urlsshould point to the provider API root for/v1