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 OpenAICompatibleProvider from providers/openai_compat.py to inherit streaming, rate limiting, and tool-handling logic.
  • Implement _build_request_body using build_base_request_body from providers.common.message_converter to handle Anthropic-to-OpenAI message translation.
  • Register exports in providers/__init__.py and add the factory case in api/dependencies.py to wire the provider to the PROVIDER_TYPE environment variable.
  • Validate credentials in the factory function and document environment variables in .env.example.
  • Run the test suite with uv run pytest and format code using uv run ruff format before 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →