Skip to content

Commit

Permalink
Add an openrouter provider
Browse files Browse the repository at this point in the history
OpenRouter is a "muxing provider" which itself provides access to
multiple models and providers. It speaks a dialect of the OpenAI protocol, but
for our purposes, we can say it's OpenAI.

There are some differences in handling the requests, though:
1) we need to know where to forward the request to, by default this is
   `https://openrouter.ai/api/v1`, this is done by setting the base_url
   parameter
2) we need to prefix the model with `openrouter/`. This is a
   lite-LLM-ism (see https://docs.litellm.ai/docs/providers/openrouter)
   which we'll be able to remove once we ditch litellm

Initially I was considering just exposing the OpenAI provider on an
additional route and handling the prefix based on the route, but I think
having an explicit provider class is better as it allows us to handle
any differences in OpenRouter dialect easily in the future.

Related: #878
  • Loading branch information
jhrozek committed Feb 4, 2025
1 parent b50b28d commit e0150f0
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/codegate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# Default provider URLs
DEFAULT_PROVIDER_URLS = {
"openai": "https://api.openai.com/v1",
"openrouter": "https://openrouter.ai/api/v1",
"anthropic": "https://api.anthropic.com/v1",
"vllm": "http://localhost:8000", # Base URL without /v1 path
"ollama": "http://localhost:11434", # Default Ollama server URL
Expand Down
2 changes: 2 additions & 0 deletions src/codegate/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
from codegate.providers.base import BaseProvider
from codegate.providers.ollama.provider import OllamaProvider
from codegate.providers.openai.provider import OpenAIProvider
from codegate.providers.openrouter.provider import OpenRouterProvider
from codegate.providers.registry import ProviderRegistry
from codegate.providers.vllm.provider import VLLMProvider

__all__ = [
"BaseProvider",
"ProviderRegistry",
"OpenAIProvider",
"OpenRouterProvider",
"AnthropicProvider",
"VLLMProvider",
"OllamaProvider",
Expand Down
3 changes: 3 additions & 0 deletions src/codegate/providers/openai/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from codegate.providers.openai.provider import OpenAIProvider

__all__ = ["OpenAIProvider"]
47 changes: 47 additions & 0 deletions src/codegate/providers/openrouter/provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import json

from fastapi import Header, HTTPException, Request

from codegate.clients.detector import DetectClient
from codegate.pipeline.factory import PipelineFactory
from codegate.providers.openai import OpenAIProvider


class OpenRouterProvider(OpenAIProvider):
def __init__(self, pipeline_factory: PipelineFactory):
super().__init__(pipeline_factory)

@property
def provider_route_name(self) -> str:
return "openrouter"

def _setup_routes(self):
@self.router.post(f"/{self.provider_route_name}/api/v1/chat/completions")
@self.router.post(f"/{self.provider_route_name}/chat/completions")
@DetectClient()
async def create_completion(
request: Request,
authorization: str = Header(..., description="Bearer token"),
):
if not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Invalid authorization header")

api_key = authorization.split(" ")[1]
body = await request.body()
data = json.loads(body)

base_url = self._get_base_url()
data["base_url"] = base_url

# litellm workaround - add openrouter/ prefix to model name to make it openai-compatible
# once we get rid of litellm, this can simply be removed
original_model = data.get("model", "")
if not original_model.startswith("openrouter/"):
data["model"] = f"openrouter/{original_model}"

return await self.process_request(
data,
api_key,
request.url.path,
request.state.detected_client,
)
1 change: 0 additions & 1 deletion src/codegate/providers/vllm/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from codegate.clients.clients import ClientType
from codegate.clients.detector import DetectClient
from codegate.config import Config
from codegate.pipeline.factory import PipelineFactory
from codegate.providers.base import BaseProvider, ModelFetchError
from codegate.providers.litellmshim import LiteLLmShim, sse_stream_generator
Expand Down
5 changes: 5 additions & 0 deletions src/codegate/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from codegate.providers.lm_studio.provider import LmStudioProvider
from codegate.providers.ollama.provider import OllamaProvider
from codegate.providers.openai.provider import OpenAIProvider
from codegate.providers.openrouter.provider import OpenRouterProvider
from codegate.providers.registry import ProviderRegistry, get_provider_registry
from codegate.providers.vllm.provider import VLLMProvider

Expand Down Expand Up @@ -75,6 +76,10 @@ async def log_user_agent(request: Request, call_next):
ProviderType.openai,
OpenAIProvider(pipeline_factory),
)
registry.add_provider(
ProviderType.openai,
OpenRouterProvider(pipeline_factory),
)
registry.add_provider(
ProviderType.anthropic,
AnthropicProvider(
Expand Down
4 changes: 2 additions & 2 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ def test_provider_registration(mock_registry, mock_secrets_mgr, mock_pipeline_fa
# Verify all providers were registered
registry_instance = mock_registry.return_value
assert (
registry_instance.add_provider.call_count == 6
) # openai, anthropic, llamacpp, vllm, ollama, lm_studio
registry_instance.add_provider.call_count == 7
) # openai, anthropic, llamacpp, vllm, ollama, lm_studio, openrouter

# Verify specific providers were registered
provider_names = [call.args[0] for call in registry_instance.add_provider.call_args_list]
Expand Down

0 comments on commit e0150f0

Please sign in to comment.