How to Add a New OpenAI-Compatible Provider to free-claude-code
To add a new OpenAI-compatible provider to the free-claude-code project, you create a package under providers/, subclass OpenAICompatibleProvider from providers/openai_compat.py, implement a request-body builder using build_base_request_body, expose the class in providers/__init__.py, and wire it into the factory function in api/dependencies.py.
The free-claude-code repository by Alishahryar1 abstracts all LLM interactions behind a unified provider interface, allowing seamless integration of OpenAI-compatible endpoints such as OpenRouter, NVIDIA NIM, and DeepSeek. Adding a new OpenAI-compatible provider involves six concrete steps that leverage the shared logic in OpenAICompatibleProvider to handle streaming, rate limiting, and tool calling automatically. This guide provides the exact file paths, method signatures, and code patterns found in the source code.
Understanding the Provider Architecture
The Base Provider Contract
Every provider in free-claude-code inherits from BaseProvider defined in providers/base.py. This abstract base class defines the ProviderConfig dataclass and the core interface that all implementations must satisfy. The ProviderConfig carries authentication credentials, base URLs, rate limits, and timeout settings used across the application.
The OpenAI-Compatible Abstraction
OpenAI-compatible providers share a common implementation in providers/openai_compat.py. The OpenAICompatibleProvider class extends BaseProvider and provides concrete methods for SSE streaming, retry logic, and Anthropic-to-OpenAI message conversion. When you add a new provider, you subclass OpenAICompatibleProvider and only need to implement the _build_request_body method to customize the JSON payload structure.
Step-by-Step Implementation Guide
Step 1: Scaffold the Provider Package
Create a new directory for your provider under the providers/ folder. This package requires at minimum an __init__.py file (which can be empty) and two Python modules: request.py and client.py.
For example, to create a provider named FooAI:
mkdir providers/fooai
touch providers/fooai/__init__.py
touch providers/fooai/request.py
touch providers/fooai/client.py
Step 2: Implement the Request Body Builder
In providers/fooai/request.py, import build_base_request_body from providers.common.message_converter to convert the internal Anthropic-style request into an OpenAI-compatible JSON payload. This helper handles message formatting, tool definitions, and thinking blocks.
from typing import Any
from loguru import logger
from providers.common.message_converter import build_base_request_body
FOOAI_DEFAULT_MAX_TOKENS = 4096
def build_request_body(request_data: Any, *, thinking_enabled: bool) -> dict:
"""Convert internal Anthropic-style request to FooAI's OpenAI-compatible payload."""
logger.debug(
"FOOAI_REQUEST: start model={} msgs={}",
getattr(request_data, "model", "?"),
len(getattr(request_data, "messages", [])),
)
body = build_base_request_body(
request_data,
include_thinking=thinking_enabled,
default_max_tokens=FOOAI_DEFAULT_MAX_TOKENS,
include_reasoning_for_openrouter=thinking_enabled,
)
logger.debug(
"FOOAI_REQUEST: done model={} msgs={} tools={}",
body.get("model"),
len(body.get("messages", [])),
len(body.get("tools", [])),
)
return body
Step 3: Create the Provider Class
In providers/fooai/client.py, define a class that inherits from OpenAICompatibleProvider. Implement _build_request_body to return the dictionary from your request builder. Optionally override _handle_extra_reasoning or _get_retry_request_body for provider-specific behavior.
from typing import Any, Iterator
from providers.base import ProviderConfig
from providers.openai_compat import OpenAICompatibleProvider, SSEBuilder
from .request import build_request_body
class FooAIProvider(OpenAICompatibleProvider):
"""FooAI – an OpenAI-compatible endpoint."""
def __init__(self, config: ProviderConfig):
super().__init__(
config,
provider_name="FOOAI",
base_url=config.base_url or "https://api.fooai.com/v1",
api_key=config.api_key,
)
def _build_request_body(self, request: Any) -> dict:
return build_request_body(
request,
thinking_enabled=self._is_thinking_enabled(request),
)
def _handle_extra_reasoning(
self, delta: Any, sse: SSEBuilder, *, thinking_enabled: bool
) -> Iterator[str]:
if not thinking_enabled:
return
details = getattr(delta, "reasoning_details", None)
if details and isinstance(details, list):
for item in details:
txt = item.get("text", "")
if txt:
yield from sse.ensure_thinking_block()
yield sse.emit_thinking_delta(txt)
Step 4: Register the Provider
Export your provider class in providers/__init__.py so it can be imported as providers.FooAIProvider. Append the class name to the __all__ list.
from .fooai import FooAIProvider
__all__.append("FooAIProvider")
Step 5: Wire Into the Dependency Factory
Add a new branch to the _create_provider_for_type function in api/dependencies.py. This maps the PROVIDER_TYPE environment variable value to your provider class and validates required settings.
if provider_type == "fooai":
if not settings.fooai_api_key or not settings.fooai_api_key.strip():
raise AuthenticationError(
"FOOAI_API_KEY is not set. Add it to your .env file."
)
config = ProviderConfig(
api_key=settings.fooai_api_key,
base_url="https://api.fooai.com/v1",
rate_limit=settings.provider_rate_limit,
rate_window=settings.provider_rate_window,
max_concurrency=settings.provider_max_concurrency,
http_read_timeout=settings.http_read_timeout,
http_write_timeout=settings.http_write_timeout,
http_connect_timeout=settings.http_connect_timeout,
enable_thinking=settings.enable_thinking,
proxy=proxy,
)
return FooAIProvider(config)
Step 6: Configure Environment Variables
Document the required environment variables in .env.example and your README. At minimum, define the API key variable and an optional proxy setting.
# FooAI
FOOAI_API_KEY=your-fooai-key
FOOAI_PROXY=
Once configured, users can activate the provider by setting:
PROVIDER_TYPE=fooai
FOOAI_API_KEY=your-key-here
Optional: Provider-Specific Customizations
Handling Extra Reasoning Fields
If your provider returns reasoning tokens in a custom field (such as reasoning_details), override the _handle_extra_reasoning method as shown in the FooAI example above. This method receives the raw delta object and an SSEBuilder instance to emit thinking blocks to the client.
Custom Retry Strategies
For providers that require special retry logic or modified request bodies on retry attempts, implement the _get_retry_request_body method. The NVIDIA NIM provider in providers/nvidia_nim/client.py demonstrates this pattern for handling rate-limit responses with adjusted token counts.
Summary
- Subclass
OpenAICompatibleProviderfromproviders/openai_compat.pyto inherit streaming, rate limiting, and tool-handling logic. - Implement
_build_request_bodyusingbuild_base_request_bodyfromproviders.common.message_converterto handle Anthropic-to-OpenAI message translation. - Register exports in
providers/__init__.pyand add the factory case inapi/dependencies.pyto wire the provider to thePROVIDER_TYPEenvironment variable. - Validate credentials in the factory function and document environment variables in
.env.example. - Run the test suite with
uv run pytestand format code usinguv run ruff formatbefore committing.
Frequently Asked Questions
What base class must I extend to add a new OpenAI-compatible provider?
You must extend OpenAICompatibleProvider defined in providers/openai_compat.py. This class inherits from BaseProvider and provides concrete implementations for HTTP streaming, SSE conversion, and OpenAI-compatible JSON formatting, requiring you only to implement the _build_request_body method for custom payload logic.
How do I convert the internal Anthropic request format to OpenAI format?
Use the build_base_request_body function from providers.common.message_converter within your provider's request builder module. This utility converts Anthropic-style message lists and tool definitions into the OpenAI chat completions schema, handling thinking blocks and token limits automatically.
Where should I validate environment variables for my custom provider?
Validate API keys and required settings inside the _create_provider_for_type function in api/dependencies.py before instantiating your provider class. Raise an AuthenticationError if required variables like FOOAI_API_KEY are missing or empty, ensuring clear error messages for users.
Can I add support for provider-specific response fields like reasoning tokens?
Yes. Override the _handle_extra_reasoning method in your provider class to parse custom fields such as reasoning_details from the API response delta. Yield thinking blocks using the SSEBuilder instance to stream these tokens to the client in the Anthropic-compatible format expected by free-claude-code.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →