Provider Router
A proxy system that routes Claude Code's API requests to external LLM providers (OpenAI, Gemini, GLM, Ollama).
Overview
Claude Code ─── ANTHROPIC_BASE_URL ──→ jikime router (localhost:8787/{provider})
│
┌───────┴───────┐
▼ ▼
OpenAI-Compatible Gemini API
(OpenAI/GLM/Ollama)It intercepts Claude Code's API requests via the ANTHROPIC_BASE_URL environment variable and transforms them into the format of the provider specified in the URL path.
URL-Based Routing
The router identifies the provider from the URL path:
http://localhost:8787/{provider}/v1/messages
↑
Provider name (openai, gemini, ollama, etc.)This approach allows multiple Claude Code sessions to use different providers simultaneously. A single router instance handles all provider requests.
Supported Providers
| Provider | Connection Method | Description |
|---|---|---|
| OpenAI | Via proxy | chat/completions format conversion |
| Gemini | Via proxy | generateContent format conversion |
| GLM | Direct connection | Uses Z.ai Anthropic-compatible endpoint |
| Ollama | Via proxy | Uses OpenAI-compatible mode |
Configuration
Configuration File
~/.jikime/router.yaml:
router:
port: 8787
host: "127.0.0.1"
providers:
openai:
model: gpt-5.1
base_url: https://api.openai.com/v1
gemini:
model: gemini-2.5-flash
base_url: https://generativelanguage.googleapis.com
glm:
model: glm-4.7
base_url: https://api.z.ai/api/paas/v4
anthropic_url: https://api.z.ai/api/anthropic
region: international
ollama:
model: llama3.1
base_url: http://localhost:11434
# Scenario-based routing (optional)
scenarios:
default: openai/gpt-5.1
background: ollama/llama3.1
think: openai/o1
long_context: gemini/gemini-2.5-flash
long_context_threshold: 60000API keys are not stored in router.yaml. They are automatically read from environment variables.
API Key Configuration
Set API keys as environment variables:
# Add to ~/.zshrc or ~/.bashrc
export OPENAI_API_KEY="sk-..."
export GEMINI_API_KEY="AI..."
export GLM_API_KEY="..."Ollama runs locally, so no API key is required.
Usage
Switching Providers
# Switch to GLM (no proxy needed, direct connection)
jikime router switch glm
# Switch to OpenAI (proxy starts automatically)
jikime router switch openai
# Switch to Gemini (proxy starts automatically)
jikime router switch gemini
# Switch to Ollama (proxy starts automatically)
jikime router switch ollama
# Restore to native Claude
jikime router switch claudeThe switch command automatically updates the current project's .claude/settings.local.json. It must be run inside a project directory (where .git or .claude exists). Claude Code restart is required after changing.
Different providers can be used for different projects:
# Project A: Cost savings (GLM)
cd ~/projects/project-a && jikime router switch glm
# Project B: High quality (Native Claude)
cd ~/projects/project-b && jikime router switch claudeModel Selection
You can specify a particular model using the provider/model format:
# Default model (uses the model value from router.yaml)
jikime router switch openai # → gpt-5.1 (config default)
# Specify a particular model
jikime router switch openai/gpt-4o-mini
jikime router switch openai/o1
jikime router switch gemini/gemini-2.5-pro
jikime router switch glm/glm-4.7
jikime router switch ollama/deepseek-r1
jikime router switch ollama/llama3.1:70bWhen a model is specified, it will be used instead of the default model in router.yaml.
Router Management
# Start router (foreground)
jikime router start
# Start router (background daemon)
jikime router start -d
# Specify port
jikime router start -d -p 9090
# Check status
jikime router status
# Send test request (provider specification required)
jikime router test openai
jikime router test gemini
# Stop router
jikime router stopHow It Works
Proxy Mode (OpenAI, Gemini, Ollama)
- The
switchcommand automatically starts the router (does not restart if already running) - Sets
ANTHROPIC_BASE_URL=http://localhost:8787/{provider}in.claude/settings.local.json - Claude Code sends requests to the local router (provider included in URL path)
- Router identifies the provider from the URL path and converts Anthropic format → provider format
- Converts provider response back to Anthropic format and returns it
Multi-session support: Since one router handles all providers, multiple Claude Code sessions can use different providers simultaneously.
Direct Mode (GLM)
- The
switchcommand sets directly in.claude/settings.local.json:ANTHROPIC_BASE_URL: Z.ai's Anthropic-compatible endpointANTHROPIC_API_KEY: GLM API keyANTHROPIC_DEFAULT_*_MODEL: GLM model name
- Claude Code sends requests directly to Z.ai (no proxy needed)
Streaming
SSE (Server-Sent Events) streaming is supported for all providers:
- OpenAI:
chat/completionsSSE → Anthropic SSE - Gemini:
streamGenerateContent?alt=sse→ Anthropic SSE - Ollama: OpenAI-compatible SSE → Anthropic SSE
Tool Use
Supports Claude Code's tool_use (file read/write, code execution, etc.):
- OpenAI/Ollama:
tool_calls↔tool_usecontent block conversion - Gemini:
functionCall/functionResponse↔tool_use/tool_resultconversion
API Parameter Conversion
Automatically handles API compatibility for each provider:
OpenAI
- max_tokens handling: Latest models (gpt-5.x, o1, o3, o4 series) require the
max_completion_tokensparameter, but these models return 400 errors when the limit is too low, so output token limits are not sent and model defaults are used. Legacy models (gpt-4o, gpt-4, etc.) sendmax_tokensnormally. - tool_choice conversion:
any→required,tool→functionformat
Gemini
- JSON Schema cleanup: Recursively removes schema fields not supported by Gemini (
exclusiveMinimum,additionalProperties,propertyNames,$schema,exclusiveMaximum). Handles nestedproperties,items, andallOf/anyOf/oneOfcontents. - system_instruction: Converts system messages to Gemini's
system_instructionformat
Statusline Integration
Router status is automatically reflected in Claude Code's statusline.
Display Format
# When router is active
🤖 openai/gpt-5.1
# When model is specified
🤖 gemini/gemini-2.5-pro
🤖 glm/glm-4.7
🤖 ollama/deepseek-r1
# When using native Claude
🤖 Claude Opus 4.5How It Works
The switch command records the current state in ~/.jikime/router-state.json:
{
"provider": "openai",
"model": "gpt-5.1",
"mode": "proxy",
"active": true
}The statusline reads this file and displays in provider/model format. When switch claude is executed, the state file is deleted and the native model name is displayed.
File Structure
cmd/routercmd/
├── router.go # Parent command
├── start.go # jikime router start
├── stop.go # jikime router stop
├── status.go # jikime router status
├── switch.go # jikime router switch
└── test.go # jikime router test
internal/router/
├── config.go # Config loader
├── server.go # HTTP proxy server
├── handler.go # /v1/messages handler
├── stream.go # SSE utilities
├── types/
│ └── types.go # Anthropic API types
└── provider/
├── provider.go # Provider interface
├── openai.go # OpenAI provider
├── gemini.go # Gemini provider
├── glm.go # GLM provider (OpenAI wrapper)
└── ollama.go # Ollama provider (OpenAI wrapper)Debugging
To check router requests/responses, run in foreground mode:
# Stop existing router
jikime router stop
# Run in foreground (logs output to terminal)
jikime router startLog example:
[router] 2026/01/25 01:30:00 Starting on 127.0.0.1:8787 (providers: openai, gemini, glm, ollama)
[router] 2026/01/25 01:30:05 -> openai/gpt-5.1 (stream=true)
[router] 2026/01/25 01:30:10 <- Provider error (400): {...}Troubleshooting
API Key Error
Error: API key environment variable not set for 'openai'Check if the environment variable for the provider is set:
echo $OPENAI_API_KEYRouter Connection Failed
Error: router is not runningStart the router first or use switch (starts automatically):
jikime router switch openaiNot Applied to Claude Code
After switch, you must restart Claude Code. .claude/settings.local.json is only read at session start.
Gemini 400 Error (Schema Related)
Unsupported value for 'properties.exclusiveMinimum'This occurs when there's a tool using JSON Schema fields not supported by Gemini. The router automatically removes these, so update to the latest binary:
jikime-adk updateOllama Connection Error
Check if Ollama is running locally:
ollama list
# or
curl http://localhost:11434/api/tagsGLM Region Configuration
When using in China, change the region in router.yaml:
glm:
region: china # base_url switches to open.bigmodel.cnTechnical Details
API Key Management
API keys are never stored in router.yaml (yaml:"-" tag applied). They are read only from environment variables and used only in memory:
| Provider | Environment Variable |
|---|---|
| OpenAI | OPENAI_API_KEY |
| Gemini | GEMINI_API_KEY |
| GLM | GLM_API_KEY |
| Ollama | Not required (local) |
Token Handling by Supported Models
| Model Group | max_tokens Handling | Reason |
|---|---|---|
| gpt-4o, gpt-4, gpt-3.5 | Sends max_tokens | Legacy API compatibility |
| gpt-5.x | Not sent (model default) | Returns 400 error when limit is low |
| o1, o3, o4 series | Not sent (model default) | max_completion_tokens required but no limit needed |
URL-Based Routing Behavior
When the switch command is executed:
- If the router is not running, it starts in the background
- Sets
ANTHROPIC_BASE_URL=http://localhost:8787/{provider}in.claude/settings.local.json
Multi-session support: Since the router identifies the provider from the URL path, a single started router handles all provider requests. There's no need to restart the router when switching providers.
Example:
- Session A:
ANTHROPIC_BASE_URL=http://localhost:8787/openai→ Uses OpenAI - Session B:
ANTHROPIC_BASE_URL=http://localhost:8787/gemini→ Uses Gemini - The same router instance handles both sessions