diff --git a/.env.example b/.env.example index ed163bac..0a0219bf 100644 --- a/.env.example +++ b/.env.example @@ -61,3 +61,10 @@ # ANTHROPIC_DEFAULT_OPUS_MODEL=kimi-k2.5 # ANTHROPIC_DEFAULT_SONNET_MODEL=kimi-k2.5 # ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.5 +# +# OrcaRouter: +# ANTHROPIC_BASE_URL=https://api.orcarouter.ai +# ANTHROPIC_AUTH_TOKEN=your-orcarouter-api-key +# ANTHROPIC_DEFAULT_OPUS_MODEL=anthropic/claude-opus-5 +# ANTHROPIC_DEFAULT_SONNET_MODEL=anthropic/claude-sonnet-5 +# ANTHROPIC_DEFAULT_HAIKU_MODEL=anthropic/claude-haiku-4.5 diff --git a/CLAUDE.md b/CLAUDE.md index 6f59910b..3926a4dd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -418,11 +418,11 @@ Run coding agents via Google Cloud Vertex AI: **Note:** Use `@` instead of `-` in model names for Vertex AI. -### Alternative API Providers (GLM, Ollama, Kimi, Custom) +### Alternative API Providers (GLM, Ollama, Kimi, OrcaRouter, Custom) Alternative providers are configured via the **Settings UI** (gear icon > API Provider section). Select a provider, set the base URL, auth token, and model — no `.env` changes needed. -**Available providers:** Claude (default), GLM (Zhipu AI), Ollama (local models), Kimi (Moonshot), Custom +**Available providers:** Claude (default), GLM (Zhipu AI), Ollama (local models), Kimi (Moonshot), OrcaRouter, Custom **Ollama notes:** - Requires Ollama v0.14.0+ with Anthropic API compatibility diff --git a/README.md b/README.md index 0ad71c94..5b1e7499 100644 --- a/README.md +++ b/README.md @@ -332,14 +332,16 @@ When test progress increases, the agent sends: } ``` -### Alternative API Providers (GLM, Ollama, Kimi, Custom) +### Alternative API Providers (GLM, Ollama, Kimi, OrcaRouter, Custom) Alternative providers are configured via the **Settings UI** (gear icon > API Provider). Select your provider, set the base URL, auth token, and model directly in the UI — no `.env` changes needed. -Available providers: **Claude** (default), **GLM** (Zhipu AI), **Ollama** (local models), **Kimi** (Moonshot), **Custom** +Available providers: **Claude** (default), **GLM** (Zhipu AI), **Ollama** (local models), **Kimi** (Moonshot), **OrcaRouter**, **Custom** For Ollama, install [Ollama v0.14.0+](https://ollama.com), run `ollama serve`, and pull a coding model (e.g., `ollama pull qwen3-coder`). Then select "Ollama" in the Settings UI. +For OrcaRouter, get an API key at [https://www.orcarouter.ai](https://www.orcarouter.ai), then select "OrcaRouter" in the Settings UI and paste your API key. OrcaRouter routes to Claude and other models through a unified Anthropic-compatible endpoint (`https://api.orcarouter.ai/v1/messages`). + ### Using Vertex AI Add these variables to your `.env` file to run agents via Google Cloud Vertex AI: diff --git a/client.py b/client.py index 26a710f5..80185bb1 100644 --- a/client.py +++ b/client.py @@ -340,7 +340,7 @@ def create_client( } # Build environment overrides for API endpoint configuration # Uses get_effective_sdk_env() which reads provider settings from the database, - # ensuring UI-configured alternative providers (GLM, Ollama, Kimi, Custom) propagate + # ensuring UI-configured alternative providers (GLM, Ollama, Kimi, OrcaRouter, Custom) propagate # correctly to the Claude CLI subprocess from registry import get_effective_sdk_env, get_effort_setting sdk_env = get_effective_sdk_env() diff --git a/registry.py b/registry.py index 6255d431..8262ac23 100644 --- a/registry.py +++ b/registry.py @@ -827,6 +827,18 @@ def get_all_settings() -> dict[str, str]: ], "default_model": "qwen3-coder", }, + "orcarouter": { + "name": "OrcaRouter", + "base_url": "https://api.orcarouter.ai", + "requires_auth": True, + "auth_env_var": "ANTHROPIC_AUTH_TOKEN", + "models": [ + {"id": "anthropic/claude-opus-5", "name": "Claude Opus"}, + {"id": "anthropic/claude-sonnet-5", "name": "Claude Sonnet"}, + {"id": "anthropic/claude-haiku-4.5", "name": "Claude Haiku"}, + ], + "default_model": "anthropic/claude-sonnet-5", + }, "custom": { "name": "Custom Provider", "base_url": "", diff --git a/server/routers/settings.py b/server/routers/settings.py index 9ff04acd..0785a2f8 100644 --- a/server/routers/settings.py +++ b/server/routers/settings.py @@ -107,12 +107,14 @@ async def get_settings(): glm_mode = api_provider == "glm" ollama_mode = api_provider == "ollama" + orcarouter_mode = api_provider == "orcarouter" return SettingsResponse( yolo_mode=_parse_yolo_mode(all_settings.get("yolo_mode")), model=all_settings.get("model", DEFAULT_MODEL), glm_mode=glm_mode, ollama_mode=ollama_mode, + orcarouter_mode=orcarouter_mode, testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1), playwright_headless=True, # Always headless - embedded browser view replaces desktop windows batch_size=_parse_int(all_settings.get("batch_size"), 3), @@ -179,12 +181,14 @@ async def update_settings(update: SettingsUpdate): api_provider = all_settings.get("api_provider", "claude") glm_mode = api_provider == "glm" ollama_mode = api_provider == "ollama" + orcarouter_mode = api_provider == "orcarouter" return SettingsResponse( yolo_mode=_parse_yolo_mode(all_settings.get("yolo_mode")), model=all_settings.get("model", DEFAULT_MODEL), glm_mode=glm_mode, ollama_mode=ollama_mode, + orcarouter_mode=orcarouter_mode, testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1), playwright_headless=True, # Always headless - embedded browser view replaces desktop windows batch_size=_parse_int(all_settings.get("batch_size"), 3), diff --git a/server/schemas.py b/server/schemas.py index d54f00aa..9a41af58 100644 --- a/server/schemas.py +++ b/server/schemas.py @@ -480,6 +480,7 @@ class SettingsResponse(BaseModel): model: str = DEFAULT_MODEL glm_mode: bool = False # True when api_provider is "glm" ollama_mode: bool = False # True when api_provider is "ollama" + orcarouter_mode: bool = False # True when api_provider is "orcarouter" testing_agent_ratio: int = 1 # Regression testing agents (0-3) playwright_headless: bool = True batch_size: int = 3 # Features per coding agent batch (1-15) diff --git a/ui/src/components/SettingsModal.tsx b/ui/src/components/SettingsModal.tsx index 5605f4e6..3a1b083e 100644 --- a/ui/src/components/SettingsModal.tsx +++ b/ui/src/components/SettingsModal.tsx @@ -25,6 +25,7 @@ const PROVIDER_INFO_TEXT: Record = { kimi: 'Get an API key at kimi.com', glm: 'Get an API key at open.bigmodel.cn', ollama: 'Run models locally. Install from ollama.com', + orcarouter: 'Get an API key at https://www.orcarouter.ai', custom: 'Connect to any OpenAI-compatible API endpoint.', } diff --git a/ui/src/components/layout/ControlBar.tsx b/ui/src/components/layout/ControlBar.tsx index 48014368..a9f325c1 100644 --- a/ui/src/components/layout/ControlBar.tsx +++ b/ui/src/components/layout/ControlBar.tsx @@ -49,6 +49,12 @@ export function ControlBar() { )} + {settings?.orcarouter_mode && ( + + OrcaRouter + + )} +