release v1.0.24 to support custom router
This commit is contained in:
3
custom-router.example.js
Normal file
3
custom-router.example.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = async function router(req, config) {
|
||||||
|
return "deepseek,deepseek-chat";
|
||||||
|
};
|
||||||
34
package-lock.json
generated
34
package-lock.json
generated
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@musistudio/claude-code-router",
|
"name": "@musistudio/claude-code-router",
|
||||||
"version": "1.0.15",
|
"version": "1.0.23",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@musistudio/claude-code-router",
|
"name": "@musistudio/claude-code-router",
|
||||||
"version": "1.0.15",
|
"version": "1.0.23",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@musistudio/llms": "^1.0.4",
|
"@musistudio/llms": "^1.0.10",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"tiktoken": "^1.0.21",
|
"tiktoken": "^1.0.21",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
@@ -18,7 +18,9 @@
|
|||||||
"ccr": "dist/cli.js"
|
"ccr": "dist/cli.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.15",
|
||||||
"esbuild": "^0.25.1",
|
"esbuild": "^0.25.1",
|
||||||
|
"fastify": "^5.4.0",
|
||||||
"shx": "^0.4.0",
|
"shx": "^0.4.0",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
}
|
}
|
||||||
@@ -182,9 +184,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@musistudio/llms": {
|
"node_modules/@musistudio/llms": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@musistudio/llms/-/llms-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@musistudio/llms/-/llms-1.0.10.tgz",
|
||||||
"integrity": "sha512-z+Ge5NOaafIvgnGiZqySSz8b2sYIvRQRCVZHZH/IjotS2uQWXespcdIUu0h72toTRkLu7hVIxLuY5Poh+6PeTQ==",
|
"integrity": "sha512-s3FUykkR/IykIHb5a/5GXfwB3MSf3DjGbJlmK9injoKhSVhA9SgbP8nG2cj3AlC1Ve5bFyLS5OR4R7wxWB4oqQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/sdk": "^0.54.0",
|
"@anthropic-ai/sdk": "^0.54.0",
|
||||||
@@ -193,7 +195,8 @@
|
|||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"fastify": "^5.4.0",
|
"fastify": "^5.4.0",
|
||||||
"openai": "^5.6.0",
|
"openai": "^5.6.0",
|
||||||
"undici": "^7.10.0"
|
"undici": "^7.10.0",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
@@ -234,6 +237,16 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.0.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz",
|
||||||
|
"integrity": "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/abstract-logging": {
|
"node_modules/abstract-logging": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||||
@@ -1609,6 +1622,13 @@
|
|||||||
"node": ">=20.18.1"
|
"node": ">=20.18.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||||
|
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "11.1.0",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@musistudio/claude-code-router",
|
"name": "@musistudio/claude-code-router",
|
||||||
"version": "1.0.23",
|
"version": "1.0.24",
|
||||||
"description": "Use Claude Code without an Anthropics account and route it to another LLM provider",
|
"description": "Use Claude Code without an Anthropics account and route it to another LLM provider",
|
||||||
"bin": {
|
"bin": {
|
||||||
"ccr": "./dist/cli.js"
|
"ccr": "./dist/cli.js"
|
||||||
@@ -18,12 +18,13 @@
|
|||||||
"author": "musistudio",
|
"author": "musistudio",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@musistudio/llms": "^1.0.10",
|
"@musistudio/llms": "^1.0.11",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"tiktoken": "^1.0.21",
|
"tiktoken": "^1.0.21",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.15",
|
||||||
"esbuild": "^0.25.1",
|
"esbuild": "^0.25.1",
|
||||||
"fastify": "^5.4.0",
|
"fastify": "^5.4.0",
|
||||||
"shx": "^0.4.0",
|
"shx": "^0.4.0",
|
||||||
|
|||||||
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@@ -9,8 +9,8 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@musistudio/llms':
|
'@musistudio/llms':
|
||||||
specifier: ^1.0.10
|
specifier: ^1.0.11
|
||||||
version: 1.0.10(ws@8.18.3)(zod@3.25.67)
|
version: 1.0.11(ws@8.18.3)(zod@3.25.67)
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.4.7
|
specifier: ^16.4.7
|
||||||
version: 16.6.1
|
version: 16.6.1
|
||||||
@@ -21,6 +21,9 @@ importers:
|
|||||||
specifier: ^11.1.0
|
specifier: ^11.1.0
|
||||||
version: 11.1.0
|
version: 11.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^24.0.15
|
||||||
|
version: 24.0.15
|
||||||
esbuild:
|
esbuild:
|
||||||
specifier: ^0.25.1
|
specifier: ^0.25.1
|
||||||
version: 0.25.5
|
version: 0.25.5
|
||||||
@@ -220,8 +223,8 @@ packages:
|
|||||||
'@modelcontextprotocol/sdk':
|
'@modelcontextprotocol/sdk':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@musistudio/llms@1.0.10':
|
'@musistudio/llms@1.0.11':
|
||||||
resolution: {integrity: sha512-s3FUykkR/IykIHb5a/5GXfwB3MSf3DjGbJlmK9injoKhSVhA9SgbP8nG2cj3AlC1Ve5bFyLS5OR4R7wxWB4oqQ==}
|
resolution: {integrity: sha512-qydLNzZDeURK8fsYJFspM04x/4mlqmKAN2Ie7MLvWuYjYT+fOtDm5BaEzQKhNLOqA5pcB2bCU0L0VFRnoeOpBg==}
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
@@ -235,6 +238,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
'@types/node@24.0.15':
|
||||||
|
resolution: {integrity: sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==}
|
||||||
|
|
||||||
abstract-logging@2.0.1:
|
abstract-logging@2.0.1:
|
||||||
resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
|
resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
|
||||||
|
|
||||||
@@ -651,6 +657,9 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
undici-types@7.8.0:
|
||||||
|
resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
|
||||||
|
|
||||||
undici@7.11.0:
|
undici@7.11.0:
|
||||||
resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==}
|
resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==}
|
||||||
engines: {node: '>=20.18.1'}
|
engines: {node: '>=20.18.1'}
|
||||||
@@ -815,7 +824,7 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
'@musistudio/llms@1.0.10(ws@8.18.3)(zod@3.25.67)':
|
'@musistudio/llms@1.0.11(ws@8.18.3)(zod@3.25.67)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@anthropic-ai/sdk': 0.54.0
|
'@anthropic-ai/sdk': 0.54.0
|
||||||
'@fastify/cors': 11.0.1
|
'@fastify/cors': 11.0.1
|
||||||
@@ -846,6 +855,10 @@ snapshots:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.19.1
|
fastq: 1.19.1
|
||||||
|
|
||||||
|
'@types/node@24.0.15':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 7.8.0
|
||||||
|
|
||||||
abstract-logging@2.0.1: {}
|
abstract-logging@2.0.1: {}
|
||||||
|
|
||||||
agent-base@7.1.3: {}
|
agent-base@7.1.3: {}
|
||||||
@@ -1278,6 +1291,8 @@ snapshots:
|
|||||||
|
|
||||||
typescript@5.8.3: {}
|
typescript@5.8.3: {}
|
||||||
|
|
||||||
|
undici-types@7.8.0: {}
|
||||||
|
|
||||||
undici@7.11.0: {}
|
undici@7.11.0: {}
|
||||||
|
|
||||||
uuid@11.1.0: {}
|
uuid@11.1.0: {}
|
||||||
|
|||||||
@@ -1,9 +1,69 @@
|
|||||||
import { MessageCreateParamsBase } from "@anthropic-ai/sdk/resources/messages";
|
import {
|
||||||
|
MessageCreateParamsBase,
|
||||||
|
MessageParam,
|
||||||
|
Tool,
|
||||||
|
} from "@anthropic-ai/sdk/resources/messages";
|
||||||
import { get_encoding } from "tiktoken";
|
import { get_encoding } from "tiktoken";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
|
|
||||||
const enc = get_encoding("cl100k_base");
|
const enc = get_encoding("cl100k_base");
|
||||||
|
|
||||||
|
const calculateTokenCount = (
|
||||||
|
messages: MessageParam[],
|
||||||
|
system: any,
|
||||||
|
tools: Tool[]
|
||||||
|
) => {
|
||||||
|
let tokenCount = 0;
|
||||||
|
if (Array.isArray(messages)) {
|
||||||
|
messages.forEach((message) => {
|
||||||
|
if (typeof message.content === "string") {
|
||||||
|
tokenCount += enc.encode(message.content).length;
|
||||||
|
} else if (Array.isArray(message.content)) {
|
||||||
|
message.content.forEach((contentPart: any) => {
|
||||||
|
if (contentPart.type === "text") {
|
||||||
|
tokenCount += enc.encode(contentPart.text).length;
|
||||||
|
} else if (contentPart.type === "tool_use") {
|
||||||
|
tokenCount += enc.encode(
|
||||||
|
JSON.stringify(contentPart.input)
|
||||||
|
).length;
|
||||||
|
} else if (contentPart.type === "tool_result") {
|
||||||
|
tokenCount += enc.encode(
|
||||||
|
typeof contentPart.content === "string"
|
||||||
|
? contentPart.content
|
||||||
|
: JSON.stringify(contentPart.content)
|
||||||
|
).length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof system === "string") {
|
||||||
|
tokenCount += enc.encode(system).length;
|
||||||
|
} else if (Array.isArray(system)) {
|
||||||
|
system.forEach((item: any) => {
|
||||||
|
if (item.type !== "text") return;
|
||||||
|
if (typeof item.text === "string") {
|
||||||
|
tokenCount += enc.encode(item.text).length;
|
||||||
|
} else if (Array.isArray(item.text)) {
|
||||||
|
item.text.forEach((textPart: any) => {
|
||||||
|
tokenCount += enc.encode(textPart || "").length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tools) {
|
||||||
|
tools.forEach((tool: Tool) => {
|
||||||
|
if (tool.description) {
|
||||||
|
tokenCount += enc.encode(tool.name + tool.description).length;
|
||||||
|
}
|
||||||
|
if (tool.input_schema) {
|
||||||
|
tokenCount += enc.encode(JSON.stringify(tool.input_schema)).length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tokenCount;
|
||||||
|
};
|
||||||
|
|
||||||
const getUseModel = (req: any, tokenCount: number, config: any) => {
|
const getUseModel = (req: any, tokenCount: number, config: any) => {
|
||||||
if (req.body.model.includes(",")) {
|
if (req.body.model.includes(",")) {
|
||||||
return req.body.model;
|
return req.body.model;
|
||||||
@@ -26,64 +86,37 @@ const getUseModel = (req: any, tokenCount: number, config: any) => {
|
|||||||
log("Using think model for ", req.body.thinking);
|
log("Using think model for ", req.body.thinking);
|
||||||
return config.Router.think;
|
return config.Router.think;
|
||||||
}
|
}
|
||||||
if (Array.isArray(req.body.tools) && req.body.tools.some(tool => tool.type?.startsWith('web_search')) && config.Router.webSearch) {
|
if (
|
||||||
|
Array.isArray(req.body.tools) &&
|
||||||
|
req.body.tools.some((tool: any) => tool.type?.startsWith("web_search")) &&
|
||||||
|
config.Router.webSearch
|
||||||
|
) {
|
||||||
return config.Router.webSearch;
|
return config.Router.webSearch;
|
||||||
}
|
}
|
||||||
return config.Router!.default;
|
return config.Router!.default;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const router = async (req: any, res: any, config: any) => {
|
export const router = async (req: any, _res: any, config: any) => {
|
||||||
const { messages, system = [], tools }: MessageCreateParamsBase = req.body;
|
const { messages, system = [], tools }: MessageCreateParamsBase = req.body;
|
||||||
try {
|
try {
|
||||||
let tokenCount = 0;
|
const tokenCount = calculateTokenCount(
|
||||||
if (Array.isArray(messages)) {
|
messages as MessageParam[],
|
||||||
messages.forEach((message) => {
|
system,
|
||||||
if (typeof message.content === "string") {
|
tools as Tool[]
|
||||||
tokenCount += enc.encode(message.content).length;
|
);
|
||||||
} else if (Array.isArray(message.content)) {
|
|
||||||
message.content.forEach((contentPart) => {
|
let model;
|
||||||
if (contentPart.type === "text") {
|
if (config.CUSTOM_ROUTER_PATH) {
|
||||||
tokenCount += enc.encode(contentPart.text).length;
|
try {
|
||||||
} else if (contentPart.type === "tool_use") {
|
const customRouter = require(config.CUSTOM_ROUTER_PATH);
|
||||||
tokenCount += enc.encode(
|
model = await customRouter(req, config);
|
||||||
JSON.stringify(contentPart.input)
|
} catch (e: any) {
|
||||||
).length;
|
log("failed to load custom router", e.message);
|
||||||
} else if (contentPart.type === "tool_result") {
|
}
|
||||||
tokenCount += enc.encode(
|
|
||||||
typeof contentPart.content === "string"
|
|
||||||
? contentPart.content
|
|
||||||
: JSON.stringify(contentPart.content)
|
|
||||||
).length;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (typeof system === "string") {
|
if (!model) {
|
||||||
tokenCount += enc.encode(system).length;
|
model = getUseModel(req, tokenCount, config);
|
||||||
} else if (Array.isArray(system)) {
|
|
||||||
system.forEach((item) => {
|
|
||||||
if (item.type !== "text") return;
|
|
||||||
if (typeof item.text === "string") {
|
|
||||||
tokenCount += enc.encode(item.text).length;
|
|
||||||
} else if (Array.isArray(item.text)) {
|
|
||||||
item.text.forEach((textPart) => {
|
|
||||||
tokenCount += enc.encode(textPart || "").length;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (tools) {
|
|
||||||
tools.forEach((tool) => {
|
|
||||||
if (tool.description) {
|
|
||||||
tokenCount += enc.encode(tool.name + tool.description).length;
|
|
||||||
}
|
|
||||||
if (tool.input_schema) {
|
|
||||||
tokenCount += enc.encode(JSON.stringify(tool.input_schema)).length;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const model = getUseModel(req, tokenCount, config);
|
|
||||||
req.body.model = model;
|
req.body.model = model;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
log("Error in router middleware:", error.message);
|
log("Error in router middleware:", error.message);
|
||||||
|
|||||||
Reference in New Issue
Block a user