Skip to main content

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 surface
  • translators/request.py: Claude request -> OpenAI chat completions request
  • backends/: upstream transport layer
  • translators/response.py: OpenAI response -> Claude message response
  • translators/streaming.py: OpenAI SSE stream -> Claude SSE stream
  • translators/tool_prompt.py: tool-call parsing and prompt-embedded tool helpers

The proxy supports two backend modes:

  • httpx Best when the upstream already exposes a stable OpenAI-compatible /chat/completions API.

  • litellm Best 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 httpx connection pooling with configurable timeouts
  • Claude-compatible error responses
  • /v1/messages/count_tokens endpoint

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
  • httpx backend 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 httpx client 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_PROVIDER is mainly relevant for the litellm backend.
  • For providers such as NVIDIA NIM, FORCE_TOOL_IN_PROMPT=true may be required depending on model behavior.
  • The inbound CLAUDE_API_KEY is optional, but it should be set in any non-local deployment.

Metadata

Release files for claude-to-openai-forwarder 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for claude-to-openai-forwarder 0.1.2
File Interpreter ABI Platform
claude_to_openai_forwarder-0.1.2-py3-none-any.whl Python 3 none any Details

Release files / claude_to_openai_forwarder-0.1.2-py3-none-any.whl

Download URL claude_to_openai_forwarder-0.1.2-py3-none-any.whl
Size 44.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2b1c010327dd989aff64065bd08f840075cc11e02ebd909305156f36e2953439
BLAKE2b-256 checksum
How to use checksums
65dd15564ef59d5ccdfab782eeffecdbedb32378517c9649043806e68c5bcd7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.1.2 This release

1 release file

0.1.1

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page