@caeliq/llms 1.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1051 @@
1
+ ![](blog/images/claude-code-router-img.png)
2
+
3
+ [![](https://img.shields.io/github/license/musistudio/claude-code-router)](https://github.com/musistudio/claude-code-router/blob/main/LICENSE)
4
+
5
+ ## ✨ Features
6
+
7
+ - **Model Routing**: Route requests to different models based on your needs (e.g., background tasks, thinking, long context).
8
+ - **Multi-Provider Support**: Supports various model providers like OpenRouter, DeepSeek, Ollama, Gemini, Volcengine, SiliconFlow, Codex, Claude subscription, Qwen, Chrome On-Device, and Cursor (SDK).
9
+ - **Request/Response Transformation**: Customize requests and responses for different providers using transformers.
10
+ - **Dynamic Model Switching**: Switch models on-the-fly within Claude Code using the `/model` command.
11
+ - **CLI Model Management**: Manage models and providers directly from the terminal with `ccr model`.
12
+ - **GitHub Actions Integration**: Trigger Claude Code tasks in your GitHub workflows.
13
+ - **Plugin System**: Extend functionality with custom transformers.
14
+
15
+ ## 🛠 Improvements in this fork
16
+
17
+ This fork is based on [claude-code-router](https://github.com/musistudio/claude-code-router) and includes several enhancements:
18
+
19
+ - **Improved LLM Support**: Fixed streaming for Gemini/Gemma and enhanced OpenAI API handling.
20
+ - **Reasoning & Streaming Refactor**: Modularized streaming and reasoning logic into reusable utilities for better maintainability.
21
+ - **Mistral Integration**: Added specific handling for Mistral's reasoning parameters and decoupled transformation logic.
22
+ - **Build & Deployment**: Integrated the UI package into the Docker build process and added a Docker Compose configuration.
23
+ - **Code Quality**: Localized codebase (English comments), improved error handling, and addressed Copilot review feedback.
24
+ - **Gemini Stability & Tool Use Fixes**: Corrected `thoughtSignature` placement in Gemini request bodies (must be a standalone `thought: true` part, not attached to text/function-call parts); filtered synthetic `ccr_` placeholder signatures from outgoing Gemini requests to prevent Gemini 500 errors; fixed `tool_result` content-array serialization in the Anthropic transformer so models receive plain text instead of JSON-wrapped arrays (resolves "Error editing file" in Claude Code); fixed Fastify `onSend` hook to prevent `invalid type 'object'` unhandled rejections on error responses.
25
+ - **Codex (ChatGPT) Integration**: Added Codex transformer for the ChatGPT backend API (Responses API), supporting both OAuth-based authentication (`ccr codex-auth`) and PAT auth via `api_key: "at-..."`, plus SSE streaming, reasoning/thinking content, tool calls with web search, and image handling.
26
+ - **Cursor SDK Integration**: Added `cursor-sdk` transformer that runs Cursor models in-process via `@cursor/sdk`. Default **bridge** mode keeps Claude Code as the tool host (Cursor built-ins denied); supports `plan` / `agent` modes, `crsr_` / `CURSOR_API_KEY` auth, `ccr model get cursor` model discovery, and Docker runtime install of the SDK native packages.
27
+ - **Claude Subscription Integration**: Added `claude-auth` support for routing through a Claude Pro or Max subscription via OAuth (`ccr claude-auth`), using the `claude-auth` + `Anthropic` transformer chain.
28
+ - **Qwen Chat Integration**: Added `qwen-auth` transformer for the Qwen Chat backend (`qwen.aikit.club/v1/chat/completions`), supporting JWT-based authentication (`ccr qwen-auth`) where the user pastes a token copied from `chat.qwen.ai` localStorage, automatic token rotation, and stripping of the trailing `<details>...</details>` metadata block Qwen injects into responses.
29
+ - **DeepSeek Reasoning Replay**: Implemented mandatory reasoning replay for DeepSeek models (e.g., via OpenCode/ZenGo). DeepSeek requires previous assistant reasoning content to be included in subsequent requests — the `reasoning` transformer automatically replays reasoning output from prior turns.
30
+ - **Model Discovery**: Enabled non-interactive model discovery for arbitrary API providers. Using `ccr model get <provider>`, the tool automatically fetches remote models, parses custom JSON structures using configurable paths, and appends missing models to the local configuration while preserving existing settings.
31
+ - **Chrome On-Device Model**: Added `chrome-on-device` transformer for Chrome's built-in Gemini Nano (~4GB local model). Communicates via a bridge process (`ccr chrome-bridge`) that connects to Chrome's Prompt API over CDP. Uses `responseConstraint` for structured JSON output (tool calls + text), supports streaming and non-streaming, exposes an OpenAI-compatible `/v1/chat/completions` endpoint, and replaces Claude Code's system prompt with a minimal tool-focused one. Zero API cost, zero latency to external providers.
32
+ - **Stability & Prompting**: Implemented an "OPERATIONAL OVERRIDE" in the system prompt to prevent hallucinations and force adherence to user-provided paths.
33
+ - **Stall Recovery**: Added a tiered retry mechanism for whitespace-heavy content: if the model stalls (emits 1000+ whitespace chars), the bridge aborts and retries without constraints and with increased temperature (Dynamic Temperature Scaling).
34
+ - **Contextual awareness**: Added labels ("Tool Result:") to tool outputs and instructed the model to check for existing results before calling tools again.
35
+
36
+ ## 🚀 Getting Started
37
+
38
+ ### 1. Installation
39
+
40
+ #### Prerequisites
41
+
42
+ Before you begin, ensure you have the following installed on your system:
43
+ - **Docker & Docker Compose** (Recommended): The primary way to run the router. See [Docker Install Guide](https://docs.docker.com/get-docker/).
44
+ - **Node.js** (Optional): Required to run from source, publish packages, or use the **Chrome On-Device** bridge. This fork requires **Node.js ≥ 22.13.0** (needed by `@cursor/sdk`). See [Node.js Download](https://nodejs.org/).
45
+ - **Claude Code**: See the [official quickstart guide](https://code.claude.com/docs/en/quickstart) for installation instructions.
46
+
47
+ #### Quick Start with Docker
48
+
49
+ The fastest way to launch Claude Code Router is using Docker Compose:
50
+
51
+ ```shell
52
+ cd packages/server
53
+ docker compose up --build -d
54
+ ```
55
+
56
+ The Compose setup builds the server and UI into the `ccr` container, exposes the proxy on `http://localhost:3456`, and mounts configuration from `packages/server/ccr-config` to `/root/.claude-code-router` inside the container.
57
+
58
+ ### 2. Configuration
59
+
60
+ Create and configure your `~/.claude-code-router/config.json` file. For more details, you can refer to `config.example.json`.
61
+
62
+ The `config.json` file has several key sections:
63
+
64
+ - **`PROXY_URL`** (optional): You can set a proxy for API requests, for example: `"PROXY_URL": "http://127.0.0.1:7890"`.
65
+ - **`LOG`** (optional): You can enable logging by setting it to `true`. When set to `false`, no log files will be created. Default is `true`.
66
+ - **`LOG_LEVEL`** (optional): Set the logging level. Available options are: `"fatal"`, `"error"`, `"warn"`, `"info"`, `"debug"`, `"trace"`. Default is `"debug"`.
67
+ - **Logging Systems**: The Claude Code Router uses two separate logging systems:
68
+ - **Server-level logs**: HTTP requests, API calls, and server events are logged using pino in the `~/.claude-code-router/logs/` directory with filenames like `ccr-*.log`
69
+ - **Application-level logs**: Routing decisions and business logic events are logged in `~/.claude-code-router/claude-code-router.log`
70
+ - **`APIKEY`** (optional): You can set a secret key to authenticate requests. When set, clients must provide this key in the `Authorization` header (e.g., `Bearer your-secret-key`) or the `x-api-key` header. Example: `"APIKEY": "your-secret-key"`.
71
+ - **`HOST`** (optional): You can set the host address for the server. If `APIKEY` is not set, the host will be forced to `127.0.0.1` for security reasons to prevent unauthorized access. Example: `"HOST": "0.0.0.0"`.
72
+ - **`NON_INTERACTIVE_MODE`** (optional): When set to `true`, enables compatibility with non-interactive environments like GitHub Actions, Docker containers, or other CI/CD systems. This sets appropriate environment variables (`CI=true`, `FORCE_COLOR=0`, etc.) and configures stdin handling to prevent the process from hanging in automated environments. Example: `"NON_INTERACTIVE_MODE": true`.
73
+
74
+ - **`Providers`**: Used to configure different model providers.
75
+ - **`Router`**: Used to set up routing rules. `default` specifies the default model, which will be used for all requests if no other route is configured.
76
+ - **`API_TIMEOUT_MS`**: Specifies the timeout for API calls in milliseconds.
77
+
78
+ #### Environment Variable Interpolation
79
+
80
+ Claude Code Router supports environment variable interpolation for secure API key management. You can reference environment variables in your `config.json` using either `$VAR_NAME` or `${VAR_NAME}` syntax:
81
+
82
+ ```json
83
+ {
84
+ "OPENAI_API_KEY": "$OPENAI_API_KEY",
85
+ "GEMINI_API_KEY": "${GEMINI_API_KEY}",
86
+ "Providers": [
87
+ {
88
+ "name": "openai",
89
+ "api_base_url": "https://api.openai.com/v1/chat/completions",
90
+ "api_key": "$OPENAI_API_KEY",
91
+ "models": ["gpt-5", "gpt-5-mini"]
92
+ }
93
+ ]
94
+ }
95
+ ```
96
+
97
+ This allows you to keep sensitive API keys in environment variables instead of hardcoding them in configuration files. The interpolation works recursively through nested objects and arrays.
98
+
99
+ Here is a comprehensive example:
100
+
101
+ ```json
102
+ {
103
+ "APIKEY": "your-secret-key",
104
+ "PROXY_URL": "http://127.0.0.1:7890",
105
+ "LOG": true,
106
+ "API_TIMEOUT_MS": 600000,
107
+ "NON_INTERACTIVE_MODE": false,
108
+ "Providers": [
109
+ {
110
+ "name": "openrouter",
111
+ "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
112
+ "api_key": "sk-xxx",
113
+ "models": [
114
+ "google/gemini-2.5-pro-preview",
115
+ "anthropic/claude-sonnet-4",
116
+ "anthropic/claude-3.5-sonnet",
117
+ "anthropic/claude-3.7-sonnet:thinking"
118
+ ],
119
+ "transformer": {
120
+ "use": ["openrouter"]
121
+ }
122
+ },
123
+ {
124
+ "name": "deepseek",
125
+ "api_base_url": "https://api.deepseek.com/chat/completions",
126
+ "api_key": "sk-xxx",
127
+ "models": ["deepseek-chat", "deepseek-reasoner"],
128
+ "transformer": {
129
+ "use": ["deepseek"],
130
+ "deepseek-chat": {
131
+ "use": ["tooluse"]
132
+ }
133
+ }
134
+ },
135
+ {
136
+ "name": "ollama",
137
+ "api_base_url": "http://localhost:11434/v1/chat/completions",
138
+ "api_key": "ollama",
139
+ "models": ["qwen2.5-coder:latest"]
140
+ },
141
+ {
142
+ "name": "gemini",
143
+ "api_base_url": "https://generativelanguage.googleapis.com/v1beta/models/",
144
+ "api_key": "sk-xxx",
145
+ "models": ["gemini-2.5-flash", "gemini-2.5-pro", "gemma-4-31b-it"],
146
+ "transformer": {
147
+ "use": ["gemini"]
148
+ }
149
+ },
150
+ {
151
+ "name": "volcengine",
152
+ "api_base_url": "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
153
+ "api_key": "sk-xxx",
154
+ "models": ["deepseek-v3-250324", "deepseek-r1-250528"],
155
+ "transformer": {
156
+ "use": ["deepseek"]
157
+ }
158
+ },
159
+ {
160
+ "name": "modelscope",
161
+ "api_base_url": "https://api-inference.modelscope.cn/v1/chat/completions",
162
+ "api_key": "",
163
+ "models": ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507"],
164
+ "transformer": {
165
+ "use": [
166
+ [
167
+ "maxtoken",
168
+ {
169
+ "max_tokens": 65536
170
+ }
171
+ ],
172
+ "enhancetool"
173
+ ],
174
+ "Qwen/Qwen3-235B-A22B-Thinking-2507": {
175
+ "use": ["reasoning"]
176
+ }
177
+ }
178
+ },
179
+ {
180
+ "name": "dashscope",
181
+ "api_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
182
+ "api_key": "",
183
+ "models": ["qwen3-coder-plus"],
184
+ "transformer": {
185
+ "use": [
186
+ [
187
+ "maxtoken",
188
+ {
189
+ "max_tokens": 65536
190
+ }
191
+ ],
192
+ "enhancetool"
193
+ ]
194
+ }
195
+ },
196
+ {
197
+ "name": "aihubmix",
198
+ "api_base_url": "https://aihubmix.com/v1/chat/completions",
199
+ "api_key": "sk-",
200
+ "models": [
201
+ "glm-4.5",
202
+ "claude-opus-4-20250514",
203
+ "gemini-2.5-pro"
204
+ ]
205
+ }
206
+ ],
207
+ "Router": {
208
+ "default": "deepseek,deepseek-chat",
209
+ "background": "ollama,qwen2.5-coder:latest",
210
+ "think": "deepseek,deepseek-reasoner",
211
+ "longContext": "openrouter,google/gemini-2.5-pro-preview",
212
+ "longContextThreshold": 60000,
213
+ "webSearch": "gemini,gemini-2.5-flash"
214
+ }
215
+ }
216
+ ```
217
+
218
+ #### Adding a New Provider
219
+
220
+ If you want to add a new provider and automatically discover its models, follow these steps:
221
+
222
+ 1. **Add Minimal Config**: Add a new entry to the `Providers` array in `config.json` with just the basic details:
223
+ ```json
224
+ {
225
+ "name": "my-new-provider",
226
+ "api_base_url": "https://api.example.com/v1/chat/completions",
227
+ "api_key": "$MY_API_KEY",
228
+ "models": []
229
+ }
230
+ ```
231
+ 2. **Perform Model Discovery**: Run the discovery command to fetch available models:
232
+ ```shell
233
+ ccr model get my-new-provider
234
+ ```
235
+ 3. **Sync Models**: The command will list remote models and prompt you to append missing ones to your configuration.
236
+ 4. **Restart**: Restart the service to pick up the updated configuration:
237
+ ```shell
238
+ ccr restart
239
+ ```
240
+
241
+ > **Tip**: For a more comprehensive description of model discovery options, custom JSON response formats, and interactive model management, see the [CLI Model Management](#5-cli-model-management) section.
242
+
243
+ ### 3. Running Claude Code with the Router
244
+
245
+ #### Via `ccr code`
246
+
247
+ Start Claude Code using the router:
248
+
249
+ ```shell
250
+ ccr code
251
+ ```
252
+
253
+ #### Via Claude Code Settings (Alternative)
254
+
255
+ You can also configure Claude Code to always use the router by editing its `settings.json` file (typically at `~/.claude/settings.json`). Models are specified using the `<provider>,<model>` syntax:
256
+
257
+ ```json
258
+ {
259
+ "env": {
260
+ "ANTHROPIC_BASE_URL": "http://127.0.0.1:3456",
261
+ "ANTHROPIC_AUTH_TOKEN": "dummy",
262
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini,gemma-4-31b-it",
263
+ "ANTHROPIC_DEFAULT_SONNET_MODEL": "opencode,minimax-m2.7",
264
+ "ANTHROPIC_DEFAULT_OPUS_MODEL": "opencode,glm-5.1"
265
+ }
266
+ }
267
+ ```
268
+
269
+ | Variable | Purpose |
270
+ |---|---|
271
+ | `ANTHROPIC_BASE_URL` | Points Claude Code to the router's proxy address |
272
+ | `ANTHROPIC_AUTH_TOKEN` | Must match the `APIKEY` value set in the router's `config.json` |
273
+ | `ANTHROPIC_MODEL` | Default model (overrides per-tier defaults below) |
274
+ | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Fast / cost-effective model (Haiku equivalent) |
275
+ | `ANTHROPIC_DEFAULT_SONNET_MODEL` | Balanced performance model (Sonnet equivalent) |
276
+ | `ANTHROPIC_DEFAULT_OPUS_MODEL` | Maximum capability model (Opus equivalent) |
277
+
278
+ This approach lets you run `claude` directly without needing `ccr code`.
279
+
280
+ > **Note**: After modifying the configuration file, you need to restart the service for the changes to take effect:
281
+ >
282
+ > ```shell
283
+ > ccr restart
284
+ > ```
285
+
286
+ ### 4. UI Mode
287
+
288
+ For a more intuitive experience, you can use the UI mode to manage your configuration:
289
+
290
+ ```shell
291
+ ccr ui
292
+ ```
293
+
294
+ This will open a web-based interface where you can easily view and edit your `config.json` file.
295
+
296
+ ![UI](/blog/images/ui.png)
297
+
298
+ ### 5. CLI Model Management
299
+
300
+ For users who prefer terminal-based workflows, you can use the interactive CLI model selector:
301
+
302
+ ```shell
303
+ ccr model
304
+ ```
305
+ ![](blog/images/models.gif)
306
+
307
+ This command provides an interactive interface to:
308
+
309
+ - View current configuration:
310
+ - See all configured models (default, background, think, longContext, webSearch, image)
311
+ - Switch models: Quickly change which model is used for each router type
312
+ - Add new models: Add models to existing providers
313
+ - Create new providers: Set up complete provider configurations including:
314
+ - Provider name and API endpoint
315
+ - API key
316
+ - Available models
317
+ - Transformer configuration with support for:
318
+ - Multiple transformers (openrouter, deepseek, gemini, etc.)
319
+ - Transformer options (e.g., maxtoken with custom limits)
320
+ - Provider-specific routing (e.g., OpenRouter provider preferences)
321
+
322
+ The CLI tool validates all inputs and provides helpful prompts to guide you through the configuration process, making it easy to manage complex setups without editing JSON files manually.
323
+
324
+ For non-interactive model discovery, you can also test provider access and list remote models directly:
325
+
326
+ ```shell
327
+ ccr model get openai
328
+ ccr model get gemini
329
+ ```
330
+
331
+ This command:
332
+ - Calls the provider's model-list endpoint using the configured API key
333
+ - Prints the remote models returned by the provider
334
+ - Prompts to append only missing models to the configured `models` array
335
+
336
+ Built-in endpoint support is included for `openai` and `gemini`. For other providers, you can configure `models_api_url` and a custom `models_response_format` to handle different JSON response structures.
337
+
338
+ The `models_response_format` object supports:
339
+ - `listPath`: JSON path to the array of models (e.g., `"data"`, `"models"`, or `""` for root array)
340
+ - `idPath`: Field name within each model object to use as ID (e.g., `"id"`, `"name"`, `"slug"`)
341
+ - `stripPrefix`: Optional prefix to remove from model IDs (e.g., `"models/"`)
342
+
343
+ Example:
344
+
345
+ ```json
346
+ {
347
+ "name": "together.ai",
348
+ "api_base_url": "https://api.together.ai/v1/chat/completions",
349
+ "models_api_url": "https://api.together.ai/v1/models",
350
+ "api_key": "$TOGETHERAI_API_KEY",
351
+ "models": [],
352
+ "models_response_format": {
353
+ "listPath": "",
354
+ "idPath": "id"
355
+ }
356
+ }
357
+ ```
358
+
359
+ You can also override these settings via CLI flags for testing:
360
+ ```shell
361
+ ccr model get my-provider --list-path data --id-path id --strip-prefix "v1/"
362
+ ```
363
+
364
+ If the provider returns additional models, `ccr model get <provider>` can append only the missing entries while keeping existing configured models unchanged.
365
+
366
+ > **Note**: After syncing models into `config.json`, restart the service with `ccr restart` so the updated provider list is picked up by the running server.
367
+
368
+ #### Codex Provider Authentication
369
+
370
+ The Codex provider supports two authentication modes:
371
+
372
+ - **OAuth** via `ccr codex-auth`
373
+ - **PAT** via `api_key: "at-..."`
374
+
375
+ ##### OAuth mode
376
+
377
+ Before using Codex models with OAuth, authenticate with your OpenAI account:
378
+
379
+ ```shell
380
+ ccr codex-auth
381
+ ```
382
+
383
+ This command:
384
+ 1. Opens your browser to the OpenAI OAuth authorization page
385
+ 2. After you sign in, the OAuth callback is handled by the running CCR server
386
+ 3. Tokens are stored in `~/.claude-code-router/codex_auth.json`
387
+ 4. The Codex transformer automatically refreshes tokens when they expire
388
+
389
+ > **Note**: The server must be running for `ccr codex-auth` to work, as it hosts the OAuth callback endpoint.
390
+
391
+ **Running with Docker**:
392
+
393
+ The OAuth callback uses port `1455`, which is mapped to the CCR server port in `docker-compose.yml` (`"1455:3456"`). When running in Docker:
394
+
395
+ ```shell
396
+ docker exec -it claude-code-router ccr codex-auth
397
+ ```
398
+
399
+ The CLI prints a URL to open in your host browser. After signing in, the browser redirects to `http://localhost:1455/auth/callback`, which Docker forwards to the container. Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
400
+
401
+ ##### PAT mode
402
+
403
+ If your provider `api_key` starts with `at-`, CCR treats it as a Codex Personal Access Token and uses it directly. In PAT mode, you do **not** run `ccr codex-auth`.
404
+
405
+ ```json
406
+ {
407
+ "name": "codex",
408
+ "api_base_url": "https://chatgpt.com/backend-api/codex",
409
+ "api_key": "at-your-personal-access-token",
410
+ "models": ["gpt-5.4"],
411
+ "transformer": {
412
+ "use": ["codex"]
413
+ }
414
+ }
415
+ ```
416
+
417
+ On the first request, CCR resolves the required account headers from OpenAI and caches the result in memory. If `api_key` is not a PAT, CCR falls back to OAuth tokens from `~/.claude-code-router/codex_auth.json`.
418
+
419
+ > **See also**: Full Codex setup and troubleshooting are documented in `docs/docs/server/guides/codex.md`.
420
+
421
+ #### Cursor Provider Authentication
422
+
423
+ The Cursor provider uses the official `@cursor/sdk` (no browser OAuth CLI). Auth resolve order:
424
+
425
+ 1. Provider `api_key` starting with `crsr_` (Cursor dashboard API key)
426
+ 2. Otherwise `CURSOR_API_KEY` from the environment
427
+
428
+ Example provider:
429
+
430
+ ```json
431
+ {
432
+ "name": "cursor",
433
+ "api_base_url": "https://cursor.com",
434
+ "api_key": "$CURSOR_API_KEY",
435
+ "models": ["composer-2"],
436
+ "transformer": {
437
+ "use": [
438
+ [
439
+ "cursor-sdk",
440
+ {
441
+ "cursorMode": "bridge"
442
+ }
443
+ ]
444
+ ]
445
+ }
446
+ }
447
+ ```
448
+
449
+ - **bridge** (default): Claude Code hosts tools; Cursor built-ins are denied in an isolated workspace under `~/.claude-code-router/cursor-sdk-workspaces/`
450
+ - Discover models with `ccr model get cursor` (lists via `Cursor.models.list`, not REST `/models`)
451
+ - Docker Compose passes `CURSOR_API_KEY` into the container when set; local Cursor sandboxing is forced off in Docker
452
+
453
+ > **See also**: Full Cursor setup is documented in `docs/docs/server/guides/cursor.md`.
454
+
455
+ #### Claude Subscription Authentication
456
+
457
+ The Claude subscription provider uses OAuth to authenticate with Anthropic's API using your Claude Pro or Max subscription. Before using Claude models this way, you must authenticate:
458
+
459
+ ```shell
460
+ ccr claude-auth
461
+ ```
462
+
463
+ This command:
464
+ 1. Opens your browser to the Claude OAuth authorization page
465
+ 2. After you sign in, the OAuth callback is handled by the running CCR server on port `1455`
466
+ 3. Tokens are stored in `~/.claude-code-router/claude_auth.json`
467
+ 4. The `claude-auth` transformer automatically refreshes tokens when they expire
468
+
469
+ > **Note**: The server must be running for `ccr claude-auth` to work, as it hosts the OAuth callback endpoint on port 1455.
470
+
471
+ **Running with Docker**:
472
+
473
+ The OAuth callback uses port `1455`, which is mapped to the CCR server port in `docker-compose.yml` (`"1455:3456"`). When running in Docker:
474
+
475
+ ```shell
476
+ docker exec -it claude-code-router ccr claude-auth
477
+ ```
478
+
479
+ The CLI prints a URL to open in your host browser. After signing in, the browser redirects to `http://localhost:1455/callback`, which Docker forwards to the container. Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
480
+
481
+ A Claude subscription provider requires the `claude-auth` + `Anthropic` transformer chain:
482
+
483
+ ```json
484
+ {
485
+ "name": "claude-subscription",
486
+ "api_base_url": "https://api.anthropic.com",
487
+ "api_key": "no-key",
488
+ "models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
489
+ "transformer": {
490
+ "use": ["claude-auth", "Anthropic"]
491
+ }
492
+ }
493
+ ```
494
+
495
+ > **See also**: Full Claude subscription setup is documented in `docs/docs/server/guides/claude-auth.md`.
496
+
497
+ **Custom Transformers:**
498
+
499
+ #### Qwen Provider Authentication
500
+
501
+ The Qwen provider uses a single JWT to authenticate with the Qwen Chat backend. Before using Qwen models, you must save a token to your local CCR config:
502
+
503
+ ```shell
504
+ ccr qwen-auth
505
+ ```
506
+
507
+ This command:
508
+ 1. Prints a URL (`http://127.0.0.1:<port>/qwen/auth`) for the in-browser auth page
509
+ 2. The page offers two options:
510
+ - **Bookmarklet (recommended)**: drag the "Get Qwen Token" link to your bookmarks bar, then click it on the signed-in Qwen page. The token is sent back to CCR automatically.
511
+ - **Manual paste**: sign in at `chat.qwen.ai`, open dev tools (F12) → Console, run `copy(localStorage.getItem('token'))`, paste the JWT into the form and submit.
512
+ 3. The token is validated against `qwen.aikit.club/v1/validate` and saved to `~/.claude-code-router/qwen_auth.json` (mode 0600)
513
+ 4. The `qwen-auth` transformer automatically refreshes the token when it nears expiry (within 6 hours)
514
+
515
+ > **Note**: The server must be running for `ccr qwen-auth` to work, as it hosts the auth form at `/qwen/auth`. Unlike the Codex flow, no OAuth callback is required — the token is pasted directly into the form.
516
+
517
+ **Running with Docker**:
518
+
519
+ The Qwen auth page is served on the regular CCR port (no separate callback port). When running in Docker:
520
+
521
+ ```shell
522
+ docker exec -it claude-code-router ccr qwen-auth
523
+ ```
524
+
525
+ The CLI prints a URL to open in your host browser (`http://localhost:3456/qwen/auth`, which Docker forwards to the container). Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
526
+
527
+ **Custom host/port for the bookmarklet**: The bookmarklet's redirect target is hardcoded into the JS because it runs in the Qwen page's context (with no knowledge of CCR's address). By default it points to `http://127.0.0.1:3456`. If your CCR server is on a different host or port, set the `QWEN_AUTH_REDIRECT` env var before starting the server, e.g. `QWEN_AUTH_REDIRECT=http://192.168.1.10:8080` — the bookmarklet will then redirect to that address.
528
+
529
+ #### Chrome On-Device Bridge
530
+
531
+ The `chrome-on-device` transformer requires a bridge process running on the host to communicate with Chrome's Gemini Nano model:
532
+
533
+ ```bash
534
+ # Start the bridge (default: port 3457, CDP port 9222)
535
+ ccr chrome-bridge
536
+
537
+ # Custom ports
538
+ ccr chrome-bridge --port 3457 --cdp 9222
539
+ ```
540
+
541
+ The bridge:
542
+ 1. Checks if Chrome is running with remote debugging enabled (port 9222)
543
+ 2. If not, launches Chrome with the required flags (`--remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug-profile`)
544
+ 3. Connects to Chrome via Puppeteer/CDP with a 5-minute protocol timeout to handle slow model inference
545
+ 4. Loads a page that accesses the Prompt API (`window.LanguageModel`) and maintains a persistent `LanguageModel` session across all requests — conversation history is carried forward naturally within the session, not rebuilt per request
546
+ 5. Replaces Claude Code's system prompt with a minimal tool-focused one (5 core tools), using `responseConstraint` (JSON Schema) to force the model to emit structured JSON with `{text, tool_calls[]}` fields
547
+ 6. Exposes an OpenAI-compatible HTTP API on `0.0.0.0:3457`:
548
+ - `GET /v1/models` — returns available models with live context usage
549
+ - `GET /v1/models/{model_name}` — returns individual model info (display_name, max_input_tokens, capabilities)
550
+ - `POST /v1/chat/completions` — chat completions with streaming and non-streaming support
551
+ - `GET /health` — health check
552
+
553
+ **Prerequisites**: Chrome flags must be enabled (see Chrome On-Device Provider Configuration section). The model (~4GB) must be downloaded.
554
+
555
+ > **Note for Docker**: The bridge runs on the Docker **host**, not inside the container. Set the provider host to `http://host.docker.internal:3457` in your `config.json`.
556
+
557
+ ### 6. Presets Management
558
+
559
+ Presets allow you to save, share, and reuse configurations easily. You can export your current configuration as a preset and install presets from files or URLs.
560
+
561
+ ```shell
562
+ # Export current configuration as a preset
563
+ ccr preset export my-preset
564
+
565
+ # Export with metadata
566
+ ccr preset export my-preset --description "My OpenAI config" --author "Your Name" --tags "openai,production"
567
+
568
+ # Install a preset from local directory
569
+ ccr preset install /path/to/preset
570
+
571
+ # List all installed presets
572
+ ccr preset list
573
+
574
+ # Show preset information
575
+ ccr preset info my-preset
576
+
577
+ # Delete a preset
578
+ ccr preset delete my-preset
579
+ ```
580
+
581
+ **Preset Features:**
582
+ - **Export**: Save your current configuration as a preset directory (with manifest.json)
583
+ - **Install**: Install presets from local directories
584
+ - **Sensitive Data Handling**: API keys and other sensitive data are automatically sanitized during export (marked as `{{field}}` placeholders)
585
+ - **Dynamic Configuration**: Presets can include input schemas for collecting required information during installation
586
+ - **Version Control**: Each preset includes version metadata for tracking updates
587
+
588
+ **Preset File Structure:**
589
+ ```
590
+ ~/.claude-code-router/presets/
591
+ ├── my-preset/
592
+ │ └── manifest.json # Contains configuration and metadata
593
+ ```
594
+
595
+ ### 7. Activate Command (Environment Variables Setup)
596
+
597
+ The `activate` command allows you to set up environment variables globally in your shell, enabling you to use the `claude` command directly or integrate Claude Code Router with applications built using the Agent SDK.
598
+
599
+ To activate the environment variables, run:
600
+
601
+ ```shell
602
+ eval "$(ccr activate)"
603
+ ```
604
+
605
+ This command outputs the necessary environment variables in shell-friendly format, which are then set in your current shell session. After activation, you can:
606
+
607
+ - **Use `claude` command directly**: Run `claude` commands without needing to use `ccr code`. The `claude` command will automatically route requests through Claude Code Router.
608
+ - **Integrate with Agent SDK applications**: Applications built with the Anthropic Agent SDK will automatically use the configured router and models.
609
+
610
+ The `activate` command sets the following environment variables:
611
+
612
+ - `ANTHROPIC_AUTH_TOKEN`: API key from your configuration
613
+ - `ANTHROPIC_BASE_URL`: The local router endpoint (default: `http://127.0.0.1:3456`)
614
+ - `NO_PROXY`: Set to `127.0.0.1` to prevent proxy interference
615
+ - `DISABLE_TELEMETRY`: Disables telemetry
616
+ - `DISABLE_COST_WARNINGS`: Disables cost warnings
617
+ - `API_TIMEOUT_MS`: API timeout from your configuration
618
+
619
+ > **Note**: Make sure the Claude Code Router service is running (`ccr start`) before using the activated environment variables. The environment variables are only valid for the current shell session. To make them persistent, you can add `eval "$(ccr activate)"` to your shell configuration file (e.g., `~/.zshrc` or `~/.bashrc`).
620
+
621
+ #### Providers
622
+
623
+ The `Providers` array is where you define the different model providers you want to use. Each provider object requires:
624
+
625
+ - `name`: A unique name for the provider.
626
+ - `api_base_url`: The full API endpoint for chat completions.
627
+ - `api_key`: Your API key for the provider.
628
+ - `models`: A list of model names available from this provider.
629
+ - `transformer` (optional): Specifies transformers to process requests and responses.
630
+
631
+ #### Transformers
632
+
633
+ Transformers allow you to modify the request and response payloads to ensure compatibility with different provider APIs.
634
+
635
+ - **Global Transformer**: Apply a transformer to all models from a provider. In this example, the `openrouter` transformer is applied to all models under the `openrouter` provider.
636
+ ```json
637
+ {
638
+ "name": "openrouter",
639
+ "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
640
+ "api_key": "sk-xxx",
641
+ "models": [
642
+ "google/gemini-2.5-pro-preview",
643
+ "anthropic/claude-sonnet-4",
644
+ "anthropic/claude-3.5-sonnet"
645
+ ],
646
+ "transformer": { "use": ["openrouter"] }
647
+ }
648
+ ```
649
+ - **Model-Specific Transformer**: Apply a transformer to a specific model. In this example, the `deepseek` transformer is applied to all models, and an additional `tooluse` transformer is applied only to the `deepseek-chat` model.
650
+
651
+ ```json
652
+ {
653
+ "name": "deepseek",
654
+ "api_base_url": "https://api.deepseek.com/chat/completions",
655
+ "api_key": "sk-xxx",
656
+ "models": ["deepseek-chat", "deepseek-reasoner"],
657
+ "transformer": {
658
+ "use": ["deepseek"],
659
+ "deepseek-chat": { "use": ["tooluse"] }
660
+ }
661
+ }
662
+ ```
663
+
664
+ - **Passing Options to a Transformer**: Some transformers, like `maxtoken`, accept options. To pass options, use a nested array where the first element is the transformer name and the second is an options object.
665
+ ```json
666
+ {
667
+ "name": "siliconflow",
668
+ "api_base_url": "https://api.siliconflow.cn/v1/chat/completions",
669
+ "api_key": "sk-xxx",
670
+ "models": ["moonshotai/Kimi-K2-Instruct"],
671
+ "transformer": {
672
+ "use": [
673
+ [
674
+ "maxtoken",
675
+ {
676
+ "max_tokens": 16384
677
+ }
678
+ ]
679
+ ]
680
+ }
681
+ }
682
+ ```
683
+
684
+ **Available Built-in Transformers:**
685
+
686
+ - `Anthropic`:If you use only the `Anthropic` transformer, it will preserve the original request and response parameters(you can use it to connect directly to an Anthropic endpoint).
687
+ - `deepseek`: Adapts requests/responses for DeepSeek API.
688
+ - `gemini`: Adapts requests/responses for Gemini API.
689
+ - `mistral`: Adapts requests/responses for Mistral API.
690
+ - `openrouter`: Adapts requests/responses for OpenRouter API. It can also accept a `provider` routing parameter to specify which underlying providers OpenRouter should use. For more details, refer to the [OpenRouter documentation](https://openrouter.ai/docs/features/provider-routing). See an example below:
691
+ ```json
692
+ "transformer": {
693
+ "use": ["openrouter"],
694
+ "moonshotai/kimi-k2": {
695
+ "use": [
696
+ [
697
+ "openrouter",
698
+ {
699
+ "provider": {
700
+ "only": ["moonshotai/fp8"]
701
+ }
702
+ }
703
+ ]
704
+ ]
705
+ }
706
+ }
707
+ ```
708
+ - `groq`: Adapts requests/responses for groq API.
709
+ - `maxtoken`: Sets a specific `max_tokens` value.
710
+ - `tooluse`: Optimizes tool usage for certain models via `tool_choice`.
711
+ - `gemini-cli` (experimental): Unofficial support for Gemini via Gemini CLI [gemini-cli.js](https://gist.github.com/musistudio/1c13a65f35916a7ab690649d3df8d1cd).
712
+ - `reasoning`: Used to process the `reasoning_content` field.
713
+ - `sampling`: Used to process sampling information fields such as `temperature`, `top_p`, `top_k`, and `repetition_penalty`.
714
+ - `enhancetool`: Adds a layer of error tolerance to the tool call parameters returned by the LLM (this will cause the tool call information to no longer be streamed).
715
+ - `cleancache`: Clears the `cache_control` field from requests.
716
+ - `vertex-gemini`: Handles the Gemini API using Vertex authentication.
717
+ - `chutes-glm` Unofficial support for GLM 4.5 model via Chutes [chutes-glm-transformer.js](https://gist.github.com/vitobotta/2be3f33722e05e8d4f9d2b0138b8c863).
718
+ - `qwen-cli` (experimental): Unofficial support for qwen3-coder-plus model via Qwen CLI [qwen-cli.js](https://gist.github.com/musistudio/f5a67841ced39912fd99e42200d5ca8b).
719
+ - `rovo-cli` (experimental): Unofficial support for gpt-5 via Atlassian Rovo Dev CLI [rovo-cli.js](https://gist.github.com/SaseQ/c2a20a38b11276537ec5332d1f7a5e53).
720
+ - `codex`: Adapts requests/responses for the Codex (ChatGPT) backend API. Supports OAuth via `ccr codex-auth` or PAT auth when `api_key` starts with `at-`.
721
+ - `claude-auth`: Authenticates requests to Anthropic's API using your Claude Pro or Max subscription OAuth token. Converts Unified format to Anthropic format and handles SSE response conversion. Use it together with `Anthropic` in the provider chain, and authenticate via `ccr claude-auth`.
722
+ - `chrome-on-device`: Routes requests to Chrome's on-device Gemini Nano model via the Prompt API. Uses `responseConstraint` for structured JSON output. Requires a bridge process running on the host (`ccr chrome-bridge`).
723
+
724
+ **Chrome On-Device Provider Configuration:**
725
+
726
+ The `chrome-on-device` transformer routes requests to Chrome's built-in Gemini Nano model. This is a ~4GB on-device model that runs locally with no API costs. The model is accessed through Chrome's Prompt API (`window.LanguageModel`) via a bridge process.
727
+
728
+ **Prerequisites:**
729
+
730
+ 1. Google Chrome installed on your system (macOS, Windows, or Linux)
731
+ 2. Enable Chrome flags (one-time):
732
+ - `chrome://flags/#optimization-guide-on-device-model` → **Enabled**
733
+ - `chrome://flags/#prompt-api-for-gemini-nano-multimodal-input` → **Enabled**
734
+ 3. Restart Chrome after enabling flags and wait for the model to download (~4GB)
735
+ 4. Start the bridge process on the host: `ccr chrome-bridge`
736
+
737
+ **Provider Configuration:**
738
+
739
+ ```json
740
+ {
741
+ "name": "chrome-nano",
742
+ "api_base_url": "http://127.0.0.1:3457",
743
+ "api_key": "placeholder",
744
+ "models": ["gemini-nano"],
745
+ "transformer": {
746
+ "use": ["chrome-on-device", "tooluse"]
747
+ }
748
+ }
749
+ ```
750
+
751
+ > **Note**: The `tooluse` transformer is required alongside `chrome-on-device` to enable the a la carte tool-calling system (including the `ExitTool` for plain text responses) and to inject the necessary system reminders that help the model transition between thinking and acting.
752
+
753
+ **Starting the Bridge:**
754
+
755
+ The bridge is a standalone HTTP server that runs on the host and bridges HTTP requests to Chrome's Prompt API via CDP (Chrome DevTools Protocol):
756
+
757
+ ```bash
758
+ # Start the bridge (default: port 3457, CDP port 9222)
759
+ ccr chrome-bridge
760
+
761
+ # Custom ports
762
+ ccr chrome-bridge --port 3457 --cdp 9222
763
+ ```
764
+
765
+ The bridge automatically launches Chrome with the required flags if it's not already running (`--remote-debugging-port=9222 --user-data-dir=<temp_dir>`).
766
+
767
+ > **Note for Docker users**: The bridge must run on the Docker **host** (not inside the container), since it needs direct access to Chrome via CDP. When CCR runs in Docker, set the provider host to `http://host.docker.internal:3457`.
768
+
769
+ **How It Works:**
770
+
771
+ 1. The transformer replaces Claude Code's system prompt with a minimal tool-focused one listing 5 core tools (Bash, Read, Write, Edit, ExitTool)
772
+ 2. The bridge maintains persistent `LanguageModel` sessions — one per client fingerprint (`User-Agent + IP` hash). Conversation history is carried forward naturally within each session, not rebuilt per turn. It calls `session.promptStreaming()` with a `responseConstraint` (JSON Schema) that forces structured output: `{"tool_calls": [{"name": "...", "arguments": {...}}]}`. Text responses are handled by the model calling the `ExitTool`.
773
+ 3. The bridge transforms Claude Code's internal context blocks in user messages to conserve the limited context budget: `<system-reminder>` blocks containing tool calls or results are converted into structured `<tool_result>` tags, while other `<system-reminder>` blocks and `<command-*>` / `<local-command-*>` blocks for unsupported tools are stripped
774
+ 4. The bridge parses the structured JSON response into OpenAI-format SSE chunks (`chat.completion.chunk`) or a single non-streaming response (`chat.completion`)
775
+ 5. Tool calls are detected from the parsed JSON and converted to `tool_calls` in the response; `finish_reason` is set to `"tool_calls"` or `"stop"` accordingly
776
+ 6. Multi-turn tool use is supported — consecutive requests are processed within the same persistent session
777
+ 7. **Multi-session support**: Requests are fingerprinted by `User-Agent + IP` hash into separate sessions, allowing multiple concurrent Claude Code instances without context contamination. A built-in web dashboard (served on the bridge port) shows real-time stats for all sessions, including turn count, idle time, and context usage
778
+ 8. **Idle session eviction**: Sessions idle for more than 5 minutes are automatically destroyed to free resources. The `cli` session (dashboard default) is never evicted. Sessions can also be manually evicted via the dashboard's Evict button
779
+ 9. Auto-compaction triggers at 85% context usage, resetting the session while preserving the system prompt
780
+
781
+ **Limitations:**
782
+
783
+ - **Tool calling**: Uses `responseConstraint` (JSON Schema) for structured output rather than native function calling — this works reliably but depends on the model following the schema
784
+ - **Multi-turn consistency**: The small on-device model may occasionally loop on the same tool call or respond with text instead of calling a needed tool. A retry mechanism with corrected prompts mitigates this
785
+ - **No thinking/reasoning blocks**: The Prompt API doesn't separate thinking from visible output
786
+ - **Context window**: Limited to 9216 tokens; auto-compaction engages at 85% usage. Old interactions are evicted on context overflow
787
+ - **Output limit**: The model may stall on whitespace-heavy content (e.g., Python indentation). The bridge uses write-then-edit incremental file creation (3 lines per Write call) and whitespace stall detection with abort
788
+ - **Cross-platform support**: Compatible with macOS, Windows, and Linux (requires Chrome installation and manual flag enablement)
789
+
790
+ **Codex Provider Configuration:**
791
+
792
+ The Codex transformer connects to the ChatGPT backend API, providing access to GPT-5.x models. It supports either OAuth authentication or a PAT in `api_key`.
793
+
794
+ ```json
795
+ {
796
+ "name": "codex",
797
+ "api_base_url": "https://chatgpt.com/backend-api/codex",
798
+ "api_key": "oauth_dummy_key",
799
+ "models": ["gpt-5.4"],
800
+ "transformer": {
801
+ "use": ["codex"]
802
+ }
803
+ }
804
+ ```
805
+
806
+ > **OAuth mode**: Keep `api_key` as a placeholder and run `ccr codex-auth`. OAuth tokens are stored in `~/.claude-code-router/codex_auth.json`.
807
+
808
+ ```json
809
+ {
810
+ "name": "codex",
811
+ "api_base_url": "https://chatgpt.com/backend-api/codex",
812
+ "api_key": "at-your-personal-access-token",
813
+ "models": ["gpt-5.4"],
814
+ "transformer": {
815
+ "use": ["codex"]
816
+ }
817
+ }
818
+ ```
819
+
820
+ > **PAT mode**: If `api_key` starts with `at-`, CCR uses it directly and skips `ccr codex-auth`.
821
+
822
+ > **Note**: If `api_key` is not a PAT, CCR falls back to OAuth tokens from `~/.claude-code-router/codex_auth.json`.
823
+
824
+ **Qwen Provider Configuration:**
825
+
826
+ The Qwen provider uses the `qwen-auth` transformer (for the `Authorization: Bearer <jwt>` header and trailing `<details>` strip) paired with the existing `OpenAI` transformer (which registers the `POST /v1/chat/completions` endpoint).
827
+
828
+ ```json
829
+ {
830
+ "name": "qwen",
831
+ "api_base_url": "https://qwen.aikit.club/v1/chat/completions",
832
+ "api_key": "qwen-placeholder",
833
+ "models": ["qwen3-max", "qwen3-coder-plus"],
834
+ "transformer": {
835
+ "use": ["qwen-auth", "reasoning", "OpenAI"]
836
+ }
837
+ }
838
+ ```
839
+
840
+ Three transformers are required in the chain:
841
+
842
+ - `qwen-auth` — sets the `Authorization: Bearer <jwt>` header on every outbound request (loading/refreshing the JWT from `~/.claude-code-router/qwen_auth.json`) and strips the trailing `<details>...</details>` block Qwen injects into responses.
843
+ - `reasoning` — maps Claude Code's unified `reasoning` field onto the request so the Qwen endpoint's `enable_thinking` and `thinking_budget` parameters are populated.
844
+ - `OpenAI` — registers the `POST /v1/chat/completions` route. It is a thin endpoint stub with no body conversion, so it must remain last in the chain.
845
+
846
+ > **Note**: The `api_key` field is a placeholder — actual authentication is handled via the JWT stored in `~/.claude-code-router/qwen_auth.json`. Run `ccr qwen-auth` to authenticate before using the Qwen provider.
847
+
848
+ **Claude Subscription Provider Configuration:**
849
+
850
+ The `claude-auth` transformer routes requests to Anthropic's API using your Claude Pro or Max subscription OAuth token instead of a static API key.
851
+
852
+ ```json
853
+ {
854
+ "name": "claude-subscription",
855
+ "api_base_url": "https://api.anthropic.com",
856
+ "api_key": "no-key",
857
+ "models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
858
+ "transformer": {
859
+ "use": ["claude-auth", "Anthropic"]
860
+ }
861
+ }
862
+ ```
863
+
864
+ Two transformers are required in the chain:
865
+
866
+ - `claude-auth` — converts the request from Unified (OpenAI) format to Anthropic format, injects `Authorization: Bearer <token>` (loading/refreshing the token from `~/.claude-code-router/claude_auth.json`), and converts the Anthropic SSE response back to Unified format.
867
+ - `Anthropic` — registers the `POST /v1/messages` route. It has no body conversion in the provider chain, so it acts as a no-op endpoint stub.
868
+
869
+ > **Note**: The `api_key` field is a placeholder — actual authentication is handled via OAuth tokens stored in `~/.claude-code-router/claude_auth.json`. Run `ccr claude-auth` to authenticate before using this provider.
870
+
871
+ **DeepSeek via OpenCode (Mandatory Reasoning Replay):**
872
+
873
+ DeepSeek models require previous assistant reasoning content to be replayed in subsequent requests. When using DeepSeek models through providers like OpenCode, apply the `reasoning` transformer at the model level to handle this automatically:
874
+
875
+ ```json
876
+ {
877
+ "name": "opencode",
878
+ "api_base_url": "https://opencode.ai/zen/go/v1/chat/completions",
879
+ "api_key": "$OPENCODE_API_KEY",
880
+ "models": ["deepseek-v4-pro", "deepseek-v4-flash"],
881
+ "transformer": {
882
+ "use": ["OpenAI"],
883
+ "deepseek-v4-pro": {
884
+ "use": ["reasoning"]
885
+ },
886
+ "deepseek-v4-flash": {
887
+ "use": ["reasoning"]
888
+ }
889
+ }
890
+ }
891
+ ```
892
+
893
+ > **Note**: The `reasoning` transformer must be applied specifically to DeepSeek models (not provider-wide). It replays the assistant's reasoning output from previous turns as required by the DeepSeek API.
894
+
895
+ **Custom Transformers:**
896
+
897
+ You can also create your own transformers and load them via the `transformers` field in `config.json`.
898
+
899
+ ```json
900
+ {
901
+ "transformers": [
902
+ {
903
+ "path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js",
904
+ "options": {
905
+ "project": "xxx"
906
+ }
907
+ }
908
+ ]
909
+ }
910
+ ```
911
+
912
+ #### Router
913
+
914
+ The `Router` object defines which model to use for different scenarios:
915
+
916
+ - `default`: The default model for general tasks.
917
+ - `background`: A model for background tasks. This can be a smaller, local model to save costs.
918
+ - `think`: A model for reasoning-heavy tasks, like Plan Mode.
919
+ - `longContext`: A model for handling long contexts (e.g., > 60K tokens).
920
+ - `longContextThreshold` (optional): The token count threshold for triggering the long context model. Defaults to 60000 if not specified.
921
+ - `webSearch`: Used for handling web search tasks and this requires the model itself to support the feature. If you're using openrouter, you need to add the `:online` suffix after the model name.
922
+ - `image` (beta): Used for handling image-related tasks (supported by CCR’s built-in agent). If the model does not support tool calling, you need to set the `config.forceUseImageAgent` property to `true`.
923
+
924
+ - You can also switch models dynamically in Claude Code with the `/model` command:
925
+ `/model provider_name,model_name`
926
+ Example: `/model openrouter,anthropic/claude-3.5-sonnet`
927
+
928
+ #### Custom Router
929
+
930
+ For more advanced routing logic, you can specify a custom router script via the `CUSTOM_ROUTER_PATH` in your `config.json`. This allows you to implement complex routing rules beyond the default scenarios.
931
+
932
+ In your `config.json`:
933
+
934
+ ```json
935
+ {
936
+ "CUSTOM_ROUTER_PATH": "/User/xxx/.claude-code-router/custom-router.js"
937
+ }
938
+ ```
939
+
940
+ The custom router file must be a JavaScript module that exports an `async` function. This function receives the request object and the config object as arguments and should return the provider and model name as a string (e.g., `"provider_name,model_name"`), or `null` to fall back to the default router.
941
+
942
+ Here is an example of a `custom-router.js` based on `custom-router.example.js`:
943
+
944
+ ```javascript
945
+ // /User/xxx/.claude-code-router/custom-router.js
946
+
947
+ /**
948
+ * A custom router function to determine which model to use based on the request.
949
+ *
950
+ * @param {object} req - The request object from Claude Code, containing the request body.
951
+ * @param {object} config - The application's config object.
952
+ * @returns {Promise<string|null>} - A promise that resolves to the "provider,model_name" string, or null to use the default router.
953
+ */
954
+ module.exports = async function router(req, config) {
955
+ const userMessage = req.body.messages.find((m) => m.role === "user")?.content;
956
+
957
+ if (userMessage && userMessage.includes("explain this code")) {
958
+ // Use a powerful model for code explanation
959
+ return "openrouter,anthropic/claude-3.5-sonnet";
960
+ }
961
+
962
+ // Fallback to the default router configuration
963
+ return null;
964
+ };
965
+ ```
966
+
967
+ ##### Subagent Routing
968
+
969
+ For routing within subagents, you must specify a particular provider and model by including `<CCR-SUBAGENT-MODEL>provider,model</CCR-SUBAGENT-MODEL>` at the **beginning** of the subagent's prompt. This allows you to direct specific subagent tasks to designated models.
970
+
971
+ **Example:**
972
+
973
+ ```
974
+ <CCR-SUBAGENT-MODEL>openrouter,anthropic/claude-3.5-sonnet</CCR-SUBAGENT-MODEL>
975
+ Please help me analyze this code snippet for potential optimizations...
976
+ ```
977
+
978
+ ## Status Line (Beta)
979
+ To better monitor the status of claude-code-router at runtime, version v1.0.40 includes a built-in statusline tool, which you can enable in the UI.
980
+ ![statusline-config.png](/blog/images/statusline-config.png)
981
+
982
+ The effect is as follows:
983
+ ![statusline](/blog/images/statusline.png)
984
+
985
+ ## 🤖 GitHub Actions
986
+
987
+ Integrate Claude Code Router into your CI/CD pipeline. After setting up [Claude Code Actions](https://docs.anthropic.com/en/docs/claude-code/github-actions), modify your `.github/workflows/claude.yaml` to use the router:
988
+
989
+ ```yaml
990
+ name: Claude Code
991
+
992
+ on:
993
+ issue_comment:
994
+ types: [created]
995
+ # ... other triggers
996
+
997
+ jobs:
998
+ claude:
999
+ if: |
1000
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
1001
+ # ... other conditions
1002
+ runs-on: ubuntu-latest
1003
+ permissions:
1004
+ contents: read
1005
+ pull-requests: read
1006
+ issues: read
1007
+ id-token: write
1008
+ steps:
1009
+ - name: Checkout repository
1010
+ uses: actions/checkout@v4
1011
+ with:
1012
+ fetch-depth: 1
1013
+
1014
+ - name: Prepare Environment
1015
+ run: |
1016
+ curl -fsSL https://bun.sh/install | bash
1017
+ mkdir -p $HOME/.claude-code-router
1018
+ cat << 'EOF' > $HOME/.claude-code-router/config.json
1019
+ {
1020
+ "log": true,
1021
+ "NON_INTERACTIVE_MODE": true,
1022
+ "OPENAI_API_KEY": "${{ secrets.OPENAI_API_KEY }}",
1023
+ "OPENAI_BASE_URL": "https://api.deepseek.com",
1024
+ "OPENAI_MODEL": "deepseek-chat"
1025
+ }
1026
+ EOF
1027
+ shell: bash
1028
+
1029
+ - name: Start Claude Code Router
1030
+ run: |
1031
+ nohup ~/.bun/bin/bunx @caeliq/claude-code-router@1.0.8 start &
1032
+ shell: bash
1033
+
1034
+ - name: Run Claude Code
1035
+ id: claude
1036
+ uses: anthropics/claude-code-action@beta
1037
+ env:
1038
+ ANTHROPIC_BASE_URL: http://localhost:3456
1039
+ with:
1040
+ anthropic_api_key: "any-string-is-ok"
1041
+ ```
1042
+
1043
+ > **Note**: When running in GitHub Actions or other automation environments, make sure to set `"NON_INTERACTIVE_MODE": true` in your configuration to prevent the process from hanging due to stdin handling issues.
1044
+
1045
+ This setup allows for interesting automations, like running tasks during off-peak hours to reduce API costs.
1046
+
1047
+ ## 📝 Further Reading
1048
+
1049
+ - [Codex API](https://developers.openai.com/codex/sdk) — Developer docs for the ChatGPT backend API used by the `codex` transformer (OAuth PKCE, Responses API, streaming, tool calls)
1050
+ - [Chrome Prompt API](https://developer.chrome.com/docs/ai/prompt-api) — On-device Gemini Nano API used by the `chrome-on-device` transformer and bridge
1051
+ - [Provider Integration Lessons](tasks/lessons.md) — Hard-won knowledge for LLM provider integrations (DeepSeek, Mistral, Gemini, Codex, Gemini Nano)