go-llm-proxy supports OpenAI's Codex CLI and any other client that uses the Responses API (POST /v1/responses). This works with both native Responses API backends (OpenAI, Azure OpenAI) and backends that only support Chat Completions (vLLM, llama-server, etc.).
The easiest path is the built-in config generator (--serve-config-generator flag). Select Codex from the coding assistant dropdown, pick your model, and it generates either:
- Configuration file — a
config.tomlto save as~/.codex/config.toml - Start command — a shell script that passes all settings via
codex -coverrides, requiring no config file changes
For manual setup, see Manual configuration below.
The proxy auto-detects whether each backend supports the Responses API natively:
- First request: the proxy forwards the Responses API request directly to the backend.
- Backend returns 200: the response is streamed through transparently. The backend is cached as native.
- Backend returns 404: the proxy falls back to Chat Completions translation and caches the result. All subsequent requests skip the probe.
This means:
- OpenAI, Azure OpenAI and other native backends work at full fidelity — encrypted compaction, built-in tools, reasoning tokens, everything passes through untouched.
- vLLM, llama-server, Ollama and other Chat Completions backends get automatic translation — the proxy converts Responses API requests to Chat Completions and translates the streaming response back.
The detection happens once per backend URL and endpoint path (so a backend that supports /responses but not /responses/compact is handled correctly). Results are cached for the lifetime of the proxy process.
Control how the proxy handles Responses API requests per model:
| Value | Behavior |
|---|---|
auto |
Default. Probe backend on first request, cache the result, fall back to translation if 404. |
native |
Always passthrough. Never translate. Use for backends you know support /v1/responses. |
translate |
Always translate to Chat Completions. Skip the probe entirely. |
models:
# Auto-detect (default — omit responses_mode or set to "auto"):
- name: qwen-3.5
backend: http://192.168.13.30:8000/v1
# Force translation (skip probe for backends you know are Chat Completions only):
- name: MiniMax-M2.5
backend: http://192.168.100.10:8000/v1
responses_mode: translate
# Force native passthrough (for OpenAI or backends with full Responses API):
- name: gpt-4.1
backend: https://api.openai.com/v1
responses_mode: nativeIn auto mode, the one-time probe adds a single round-trip on the first request per backend. Use translate to avoid it for backends you know are Chat Completions only, or native to skip it for backends you know support the Responses API.
When translating for non-native backends, the proxy maps between the two API formats:
| Responses API | Chat Completions |
|---|---|
input (string) |
Single user message |
input (array of items) |
Messages array (see below) |
instructions |
System message (prepended) |
tools (function type) |
tools with nested function wrapper |
tool_choice |
tool_choice (pass through) |
max_output_tokens |
max_completion_tokens |
reasoning.effort |
reasoning_effort |
text.format |
response_format |
temperature, top_p, user |
Pass through |
Input item type mapping:
| Responses input item | Chat Completions message |
|---|---|
{role: "user", content: "..."} |
{role: "user", content: "..."} |
{role: "developer", content: "..."} |
{role: "system", content: "..."} |
{type: "message", role: "assistant", content: [...]} |
{role: "assistant", content: "..."} |
{type: "function_call", call_id, name, arguments} |
Merged into assistant message tool_calls |
{type: "function_call_output", call_id, output} |
{role: "tool", tool_call_id, content} |
{type: "reasoning", ...} |
Dropped (no equivalent) |
{type: "compaction", ...} |
Dropped (no equivalent) |
Non-function tool definitions (web_search_preview, code_interpreter, etc.) are dropped since Chat Completions backends cannot execute them. Codex sends local_shell_call and custom_tool_call as function-type tools, so they translate normally.
Streaming responses are translated event-by-event:
| Chat Completions chunk | Responses API event |
|---|---|
First chunk with delta.role |
response.created, output_item.added, content_part.added |
delta.content |
response.output_text.delta |
delta.tool_calls[i] with id |
response.output_item.added (function_call) |
delta.tool_calls[i].function.arguments |
response.function_call_arguments.delta |
finish_reason: "stop" |
All *.done events, response.completed |
finish_reason: "tool_calls" |
Tool call *.done events, response.completed |
finish_reason: "length" |
response.incomplete |
| Usage chunk | Included in response.completed |
Non-streaming responses are translated as a single JSON object.
Long Codex sessions may trigger context compaction to manage the growing conversation history.
Native backends: The /v1/responses/compact request is passed through directly, preserving OpenAI's encrypted compaction format.
Translated backends: The proxy sends the conversation to the backend with a summarization prompt and returns the result as preserved user messages plus a summary assistant message. This is functionally similar to Codex's own inline compaction path for non-OpenAI providers.
Note: When using the config generator's custom provider setup (model_provider = "go-llm-proxy"), Codex automatically uses its client-side inline compaction, which goes through the regular /v1/responses endpoint. The /v1/responses/compact handler serves as a fallback for alternative configurations.
Codex's built-in web_search_preview tool is a server-side OpenAI feature. It works with native OpenAI backends but not with local models directly.
The proxy can handle web search transparently for local models:
Option 1: Proxy-side search (recommended) — Configure a Tavily API key in the proxy's processors block:
processors:
web_search_key: tvly-your-keyThe proxy automatically converts Codex's web_search_preview tool to a function tool. When the backend model calls web_search, the proxy executes a Tavily search and injects the results. Codex sees only the final response. No client-side configuration needed — the config generator omits MCP setup when the proxy has search configured.
Option 2: Client-side MCP — For local/proxy models, the config generator disables the built-in search (web_search = "disabled") and optionally configures Tavily as an MCP server. Enter your Tavily API key in the config generator to include it.
The generated TOML includes:
web_search = "disabled"
[mcp_servers.tavily]
url = "https://mcp.tavily.com/mcp"
bearer_token_env_var = "TAVILY_API_KEY"Set the environment variable before running Codex:
export TAVILY_API_KEY=tvly-your-keyIf you prefer not to use the config generator, create ~/.codex/config.toml:
model = "your-model-name"
model_provider = "go-llm-proxy"
model_reasoning_effort = "medium"
web_search = "disabled"
[model_providers.go-llm-proxy]
name = "Go-LLM-Proxy"
base_url = "http://your-proxy-host:8080/v1"
env_key = "OPENAI_API_KEY"
wire_api = "responses"Then set your proxy API key:
export OPENAI_API_KEY=your-proxy-api-key| Field | Purpose |
|---|---|
model_provider |
Must match the name in [model_providers.*] |
base_url |
Your proxy URL including /v1 |
env_key |
Name of the env var holding the API key |
wire_api = "responses" |
Tells Codex to use the Responses API (required) |
model_reasoning_effort |
"low", "medium", or "high" |
web_search |
Set to "disabled" for non-OpenAI backends |
Codex uses the model's context window size to manage conversation history and trigger compaction. The proxy auto-detects this from each backend at startup:
- vLLM backends: reads
max_model_lenfromGET /v1/models - llama-server backends: reads
meta.n_ctx_trainfromGET /v1/models - Anthropic backends: reads
max_input_tokensfromGET /v1/models/{model_id} - Cloud APIs without a models endpoint (e.g., MiniMax cloud, Zhipu): detection fails gracefully — a warning is logged
Detection runs asynchronously at startup and does not block the server. Results are cached and served through the proxy's /v1/models endpoint in the max_model_len field.
For backends that don't report their context window, set it explicitly in config.yaml:
- name: MiniMax-M2.7
backend: https://api.minimax.io/anthropic
type: anthropic
context_window: 1048576 # 1M tokensOmit the field or set it to 0 for auto-detection. Any positive value skips the backend query and uses the configured value directly.
The Codex config generator includes a Context Window dropdown. When the selected model has a detected context window, the hint shows the value (e.g., "Detected: 192K tokens"). When detection returned nothing, the dropdown is highlighted amber with "Not detected — set manually for best results" to prompt manual selection. The chosen value is included as model_context_window in the generated Codex config.
These limitations apply only to translated backends (Chat Completions). Native passthrough backends (OpenAI, Azure OpenAI, or any backend that supports /v1/responses) have full fidelity — the proxy is transparent and all Responses API features work exactly as they would with a direct connection.
The translation layer targets Codex CLI and coding-agent workflows: text generation, function calling, and tool use. It is not a spec-complete generic Responses API adapter. Specifically:
- Non-function tools are converted or dropped.
web_search_previewis converted to a proxy-side function tool whenweb_search_keyis configured in theprocessorsblock (the proxy executes Tavily searches transparently and emitsweb_search_calloutput items for Codex's native search UI). Other server-side tools (code_interpreter,file_search) are dropped since Chat Completions backends cannot execute them. - Reasoning is fully supported. Backend reasoning tokens (
delta.reasoningin Chat Completions) are translated to Responses APIreasoningoutput items with streamingreasoning_summary_text.deltaevents, giving Codex its native thinking display. - Compaction is fully supported. The proxy's
/v1/responses/compactendpoint uses a summarization fallback for Chat Completions backends — not protocol-equivalent to OpenAI's encrypted compaction, but functionally equivalent for maintaining conversation context. - All tool call output types are supported.
function_call_output,local_shell_call_output,custom_tool_call_output, andmcp_tool_call_outputare all translated to Chat Completionstoolmessages. - Some input item types are dropped.
compaction,tool_search_call,tool_search_output,web_search_call,image_generation_call, andmcp_list_toolsitems from conversation history are skipped during translation since they have no Chat Completions equivalent. These only appear in history from previous native OpenAI sessions — the proxy never produces them, so this is a non-issue in practice. - Assistant content is simplified. Structured content arrays on assistant messages are reduced to concatenated text from
output_textparts. Other content types (e.g., images fromimage_generation_call) are not preserved through translation.
The translated Chat Completions output uses standard fields (messages, tools, stream, max_completion_tokens, reasoning_effort, response_format, etc.). Compatibility depends on what each backend supports:
- vLLM has comprehensive Chat Completions support including tool calling,
response_format, andmax_completion_tokens. Recent versions expose/v1/responsesbut multi-turn input validation may not be fully compatible with Codex. Recommendresponses_mode: translatefor vLLM unless you have explicitly validated your version with Codex end-to-end. - llama-server (llama.cpp) supports core Chat Completions (
/v1/chat/completions) but feature coverage varies by version —response_format,parallel_tool_calls, and reasoning-related fields may not be available in older builds. Test with your version. - Ollama supports basic Chat Completions. Tool calling support varies.
The proxy only includes optional fields (like response_format or reasoning_effort) when the Codex client sends them, so a basic session will work with minimal backend support.
- Token counts are not available for native passthrough. Usage logging records request/response byte counts but not token counts for passthrough requests (tokens would require buffering and parsing the full response).
- WebSocket transport is not supported. Codex falls back to HTTP SSE automatically, which the proxy supports fully.
On the proxy side, no special configuration is needed. Any OpenAI-type model in your config.yaml is automatically available via the Responses API. The proxy handles detection and translation transparently.
The only proxy-side option is responses_mode on a model, which controls whether the proxy probes, passthroughs, or translates Responses API requests. See responses_mode above for details.