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",
|
||||
"version": "1.0.15",
|
||||
"version": "1.0.23",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@musistudio/claude-code-router",
|
||||
"version": "1.0.15",
|
||||
"version": "1.0.23",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@musistudio/llms": "^1.0.4",
|
||||
"@musistudio/llms": "^1.0.10",
|
||||
"dotenv": "^16.4.7",
|
||||
"tiktoken": "^1.0.21",
|
||||
"uuid": "^11.1.0"
|
||||
@@ -18,7 +18,9 @@
|
||||
"ccr": "dist/cli.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.15",
|
||||
"esbuild": "^0.25.1",
|
||||
"fastify": "^5.4.0",
|
||||
"shx": "^0.4.0",
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
@@ -182,9 +184,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@musistudio/llms": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@musistudio/llms/-/llms-1.0.4.tgz",
|
||||
"integrity": "sha512-z+Ge5NOaafIvgnGiZqySSz8b2sYIvRQRCVZHZH/IjotS2uQWXespcdIUu0h72toTRkLu7hVIxLuY5Poh+6PeTQ==",
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@musistudio/llms/-/llms-1.0.10.tgz",
|
||||
"integrity": "sha512-s3FUykkR/IykIHb5a/5GXfwB3MSf3DjGbJlmK9injoKhSVhA9SgbP8nG2cj3AlC1Ve5bFyLS5OR4R7wxWB4oqQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.54.0",
|
||||
@@ -193,7 +195,8 @@
|
||||
"dotenv": "^16.5.0",
|
||||
"fastify": "^5.4.0",
|
||||
"openai": "^5.6.0",
|
||||
"undici": "^7.10.0"
|
||||
"undici": "^7.10.0",
|
||||
"uuid": "^11.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
@@ -234,6 +237,16 @@
|
||||
"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": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||
@@ -1609,6 +1622,13 @@
|
||||
"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": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"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",
|
||||
"bin": {
|
||||
"ccr": "./dist/cli.js"
|
||||
@@ -18,12 +18,13 @@
|
||||
"author": "musistudio",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@musistudio/llms": "^1.0.10",
|
||||
"@musistudio/llms": "^1.0.11",
|
||||
"dotenv": "^16.4.7",
|
||||
"tiktoken": "^1.0.21",
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.15",
|
||||
"esbuild": "^0.25.1",
|
||||
"fastify": "^5.4.0",
|
||||
"shx": "^0.4.0",
|
||||
|
||||
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@@ -9,8 +9,8 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@musistudio/llms':
|
||||
specifier: ^1.0.10
|
||||
version: 1.0.10(ws@8.18.3)(zod@3.25.67)
|
||||
specifier: ^1.0.11
|
||||
version: 1.0.11(ws@8.18.3)(zod@3.25.67)
|
||||
dotenv:
|
||||
specifier: ^16.4.7
|
||||
version: 16.6.1
|
||||
@@ -21,6 +21,9 @@ importers:
|
||||
specifier: ^11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^24.0.15
|
||||
version: 24.0.15
|
||||
esbuild:
|
||||
specifier: ^0.25.1
|
||||
version: 0.25.5
|
||||
@@ -220,8 +223,8 @@ packages:
|
||||
'@modelcontextprotocol/sdk':
|
||||
optional: true
|
||||
|
||||
'@musistudio/llms@1.0.10':
|
||||
resolution: {integrity: sha512-s3FUykkR/IykIHb5a/5GXfwB3MSf3DjGbJlmK9injoKhSVhA9SgbP8nG2cj3AlC1Ve5bFyLS5OR4R7wxWB4oqQ==}
|
||||
'@musistudio/llms@1.0.11':
|
||||
resolution: {integrity: sha512-qydLNzZDeURK8fsYJFspM04x/4mlqmKAN2Ie7MLvWuYjYT+fOtDm5BaEzQKhNLOqA5pcB2bCU0L0VFRnoeOpBg==}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
@@ -235,6 +238,9 @@ packages:
|
||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@types/node@24.0.15':
|
||||
resolution: {integrity: sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==}
|
||||
|
||||
abstract-logging@2.0.1:
|
||||
resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
|
||||
|
||||
@@ -651,6 +657,9 @@ packages:
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@7.8.0:
|
||||
resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
|
||||
|
||||
undici@7.11.0:
|
||||
resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
@@ -815,7 +824,7 @@ snapshots:
|
||||
- supports-color
|
||||
- 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:
|
||||
'@anthropic-ai/sdk': 0.54.0
|
||||
'@fastify/cors': 11.0.1
|
||||
@@ -846,6 +855,10 @@ snapshots:
|
||||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.19.1
|
||||
|
||||
'@types/node@24.0.15':
|
||||
dependencies:
|
||||
undici-types: 7.8.0
|
||||
|
||||
abstract-logging@2.0.1: {}
|
||||
|
||||
agent-base@7.1.3: {}
|
||||
@@ -1278,6 +1291,8 @@ snapshots:
|
||||
|
||||
typescript@5.8.3: {}
|
||||
|
||||
undici-types@7.8.0: {}
|
||||
|
||||
undici@7.11.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 { log } from "./log";
|
||||
|
||||
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) => {
|
||||
if (req.body.model.includes(",")) {
|
||||
return req.body.model;
|
||||
@@ -26,64 +86,37 @@ const getUseModel = (req: any, tokenCount: number, config: any) => {
|
||||
log("Using think model for ", req.body.thinking);
|
||||
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!.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;
|
||||
try {
|
||||
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) => {
|
||||
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;
|
||||
const tokenCount = calculateTokenCount(
|
||||
messages as MessageParam[],
|
||||
system,
|
||||
tools as Tool[]
|
||||
);
|
||||
|
||||
let model;
|
||||
if (config.CUSTOM_ROUTER_PATH) {
|
||||
try {
|
||||
const customRouter = require(config.CUSTOM_ROUTER_PATH);
|
||||
model = await customRouter(req, config);
|
||||
} catch (e: any) {
|
||||
log("failed to load custom router", e.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!model) {
|
||||
model = getUseModel(req, tokenCount, config);
|
||||
}
|
||||
if (typeof system === "string") {
|
||||
tokenCount += enc.encode(system).length;
|
||||
} 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;
|
||||
} catch (error: any) {
|
||||
log("Error in router middleware:", error.message);
|
||||
|
||||
Reference in New Issue
Block a user