A FastAPI service that translates Claude Messages API requests to OpenAI-compatible chat completions endpoints
Project description
Claude-to-OpenAI Forwarder
A FastAPI service that accepts Claude Messages API requests and forwards them to OpenAI-compatible chat completions backends.
It is designed as a drop-in bridge for tools such as Claude Code when the upstream model is served by OpenAI, NVIDIA NIM, Ollama, or another OpenAI-compatible endpoint.
Design
The project is organized around a small translation pipeline:
app.py: FastAPI entrypoint and Claude-compatible HTTP surfacetranslators/request.py: Claude request -> OpenAI chat completions requestbackends/: upstream transport layertranslators/response.py: OpenAI response -> Claude message responsetranslators/streaming.py: OpenAI SSE stream -> Claude SSE streamtranslators/tool_prompt.py: tool-call parsing and prompt-embedded tool helpers
The proxy supports two backend modes:
-
httpxBest when the upstream already exposes a stable OpenAI-compatible/chat/completionsAPI. -
litellmBest when provider-specific routing or LiteLLM normalization is needed.
Features
- Claude Messages API compatible
/v1/messages - Claude-compatible streaming responses over SSE
- Non-streaming and streaming tool-call translation
- Support for native OpenAI
tool_calls - Support for prompt-embedded tool calling for providers that return tool calls inside text
- Configurable model mapping from Claude model names to upstream model names
- Optional inbound API-key enforcement for Claude clients
- Local per-key rate limiting
- Persistent
httpxconnection pooling with configurable timeouts - Claude-compatible error responses
/v1/messages/count_tokensendpoint
Tool-Calling Behavior
The proxy handles several tool-call shapes that appear in practice:
- native OpenAI
tool_calls - embedded JSON such as
{"type":"tool_use", ...} - function-style calls such as
Agent({...})
The response sanitization layer also strips control/meta wrappers that should not leak into user-visible assistant text, including:
<thinking>...</thinking><result ...>...</result><analysis>...</analysis><commentary>...</commentary><final>...</final>
Configuration
Configuration is loaded from environment variables or .env.
Core settings:
| Variable | Description | Default |
|---|---|---|
BACKEND_TYPE |
httpx or litellm |
httpx |
OPENAI_API_KEY |
Upstream API key | required |
OPENAI_BASE_URL |
Upstream OpenAI-compatible base URL | https://api.openai.com/v1 |
MODEL_PROVIDER |
LiteLLM provider hint | unset |
CLAUDE_API_KEY |
Inbound client auth key | unset |
DEFAULT_OPENAI_MODEL |
Default upstream model | gpt-4o-mini |
CLAUDE_MODEL_MAP |
Claude-to-upstream model map | {} |
FORCE_TOOL_IN_PROMPT |
Use prompt-embedded tool calling | false |
FORCE_CONTENT_FLAT |
Flatten message content for upstream compatibility | false |
RATE_LIMIT_RPM |
Local requests per minute per inbound key | 40 |
HOST |
Bind host | 0.0.0.0 |
PORT |
Bind port | 8000 |
LOG_LEVEL |
Server log level | INFO |
HTTP client settings:
| Variable | Description | Default |
|---|---|---|
REQUEST_TIMEOUT |
Total request timeout | 120.0 |
CONNECT_TIMEOUT |
Connect timeout | 10.0 |
READ_TIMEOUT |
Read timeout | 120.0 |
WRITE_TIMEOUT |
Write timeout | 30.0 |
MAX_CONNECTIONS |
Max pooled connections | 100 |
MAX_KEEPALIVE_CONNECTIONS |
Max keepalive connections | 20 |
KEEPALIVE_EXPIRY |
Keepalive expiry seconds | 30.0 |
Example .env:
BACKEND_TYPE=httpx
CLAUDE_API_KEY=sk-ant-local-forwarder-key
OPENAI_API_KEY=your-upstream-key
OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1
MODEL_PROVIDER=nvidia_nim
DEFAULT_OPENAI_MODEL=meta/llama-4-maverick-17b-128e-instruct
FORCE_TOOL_IN_PROMPT=true
RATE_LIMIT_RPM=40
HOST=0.0.0.0
PORT=8000
LOG_LEVEL=INFO
Running
Install:
uv pip install -r requirements.txt
Run with Uvicorn:
uvicorn src.claude_to_openai_forwarder.app:app --host 0.0.0.0 --port 8000
Or run the packaged entrypoint:
claude-to-openai-forwarder
Usage
Send a Claude-style request:
curl http://127.0.0.1:8000/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-ant-local-forwarder-key" \
-d '{
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 256,
"messages": [
{"role": "user", "content": "Hello"}
]
}'
Count tokens:
curl http://127.0.0.1:8000/v1/messages/count_tokens \
-H "Content-Type: application/json" \
-H "x-api-key: sk-ant-local-forwarder-key" \
-d '{
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 256,
"messages": [
{"role": "user", "content": "Hello"}
]
}'
Claude Code Integration
Example Claude Code environment settings:
{
"env": {
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8000",
"ANTHROPIC_API_KEY": "sk-ant-local-forwarder-key",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-3-5-sonnet-20241022"
}
}
Recent Improvements
The current code in src/ includes the following hardening and compatibility fixes:
- local rate limiting is enabled in the request path
- rate-limit cleanup preserves active identifiers while pruning expired timestamps
httpxbackend error parsing handles nested errors, flat objects, and JSON string payloads- streaming translation no longer leaks recognized tool-call text into normal assistant text
- parser support for function-style tool calls such as
Agent({...}) - control/meta wrappers such as
<thinking>and<result ...>are stripped from visible assistant text - persistent
httpxclient pooling with configurable timeout and connection settings
Verification
Basic syntax check:
python -m compileall src/claude_to_openai_forwarder
Focused regression tests:
PYTHONPATH=src python -m unittest tests.test_backends.HttpxBackendTests
PYTHONPATH=src python -m unittest tests.test_backends.AppRateLimitTests.test_messages_endpoint_enforces_local_rate_limit
PYTHONPATH=src python -m unittest tests.test_backends.StreamingTranslatorTests.test_translate_stream_converts_function_style_agent_call_after_text
PYTHONPATH=src python -m unittest tests.test_backends.StreamingTranslatorTests.test_translate_stream_strips_thinking_wrappers_from_text
PYTHONPATH=src python -m unittest tests.test_improvements.TestRateLimiterMemoryLeakFix
Notes
MODEL_PROVIDERis mainly relevant for thelitellmbackend.- For providers such as NVIDIA NIM,
FORCE_TOOL_IN_PROMPT=truemay be required depending on model behavior. - The inbound
CLAUDE_API_KEYis optional, but it should be set in any non-local deployment.
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 Distribution
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 claude_to_openai_forwarder-0.1.2-py3-none-any.whl.
File metadata
- Download URL: claude_to_openai_forwarder-0.1.2-py3-none-any.whl
- Upload date:
- Size: 44.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b1c010327dd989aff64065bd08f840075cc11e02ebd909305156f36e2953439
|
|
| MD5 |
f85add821b6eda837eec62a281306e8e
|
|
| BLAKE2b-256 |
65dd15564ef59d5ccdfab782eeffecdbedb32378517c9649043806e68c5bcd7c
|