@dedenlabs/claude-code-router-cli 2.0.0

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.
@@ -0,0 +1,246 @@
1
+ {
2
+ "LOG": false,
3
+ "LOG_LEVEL": "info",
4
+ "CLAUDE_PATH": "",
5
+ "HOST": "127.0.0.1",
6
+ "PORT": 3456,
7
+ "APIKEY": "sk-anything",
8
+ "API_TIMEOUT_MS": "600000",
9
+ "PROXY_URL": "",
10
+ "transformers": [
11
+ {
12
+ "name": "",
13
+ "path": "./transformers/glm-thinking.js",
14
+ "options": {
15
+ "enabled": "true",
16
+ "debug": "true"
17
+ }
18
+ }
19
+ ],
20
+ "Providers": [
21
+ {
22
+ "name": "haiku-minimax",
23
+ "api_base_url": "https://api.minimaxi.com/anthropic/v1/messages",
24
+ "api_key": "YOUR_MINIMAX_API_KEY",
25
+ "models": [
26
+ "MiniMax-M2"
27
+ ],
28
+ "transformer": {
29
+ "use": [
30
+ "Anthropic"
31
+ ]
32
+ }
33
+ },
34
+ {
35
+ "name": "sonnet-minimax",
36
+ "api_base_url": "https://api.minimaxi.com/anthropic/v1/messages",
37
+ "api_key": "YOUR_MINIMAX_API_KEY",
38
+ "models": [
39
+ "MiniMax-M2"
40
+ ],
41
+ "transformer": {
42
+ "use": [
43
+ "Anthropic"
44
+ ]
45
+ }
46
+ },
47
+ {
48
+ "name": "opus-minimax",
49
+ "api_base_url": "https://api.minimaxi.com/anthropic/v1/messages",
50
+ "api_key": "YOUR_MINIMAX_API_KEY",
51
+ "models": [
52
+ "MiniMax-M2"
53
+ ],
54
+ "transformer": {
55
+ "use": [
56
+ "Anthropic"
57
+ ]
58
+ }
59
+ },
60
+ {
61
+ "name": "haiku-glm",
62
+ "api_base_url": "https://open.bigmodel.cn/api/anthropic/v1/messages",
63
+ "api_key": "YOUR_GLM_API_KEY",
64
+ "models": [
65
+ "glm-4.6"
66
+ ],
67
+ "transformer": {
68
+ "use": [
69
+ "Anthropic"
70
+ ]
71
+ }
72
+ },
73
+ {
74
+ "name": "sonnet-glm",
75
+ "api_base_url": "https://open.bigmodel.cn/api/anthropic/v1/messages",
76
+ "api_key": "YOUR_GLM_API_KEY",
77
+ "models": [
78
+ "glm-4.6"
79
+ ],
80
+ "transformer": {
81
+ "use": [
82
+ "Anthropic"
83
+ ]
84
+ }
85
+ },
86
+ {
87
+ "name": "opus-glm",
88
+ "api_base_url": "https://api.z.ai/api/coding/paas/v4/chat/completions",
89
+ "api_key": "YOUR_GLM_API_KEY",
90
+ "models": [
91
+ "glm-4.6"
92
+ ],
93
+ "transformer": {
94
+ "use": [
95
+ "reasoning",
96
+ "glm-thinking"
97
+ ]
98
+ }
99
+ },
100
+ {
101
+ "name": "opus-openrouter",
102
+ "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
103
+ "api_key": "api-key",
104
+ "models": [
105
+ "anthropic/claude-4.5-sonnet:thinking",
106
+ "google/gemini-2.5-pro-preview"
107
+ ],
108
+ "transformer": {
109
+ "use": [
110
+ "openrouter"
111
+ ]
112
+ }
113
+ }
114
+ ],
115
+ "Router": {
116
+ "engine": "unified",
117
+ "defaultRoute": "sonnet",
118
+ "rules": [
119
+ {
120
+ "name": "用户指定规则",
121
+ "condition": {
122
+ "type": "custom",
123
+ "customFunction": "modelContainsComma"
124
+ },
125
+ "action": {
126
+ "route": "${userModel}",
127
+ "description": "用户指定路由:用户在请求中直接指定provider,model格式"
128
+ },
129
+ "priority": 200,
130
+ "enabled": true
131
+ },
132
+ {
133
+ "name": "提供商映射规则",
134
+ "condition": {
135
+ "type": "custom",
136
+ "customFunction": "directModelMapping"
137
+ },
138
+ "action": {
139
+ "route": "${mappedModel}",
140
+ "description": "提供商映射:将用户指定的模型名映射到对应的provider格式"
141
+ },
142
+ "priority": 190,
143
+ "enabled": true
144
+ },
145
+ {
146
+ "name": "长上下文规则",
147
+ "condition": {
148
+ "type": "tokenThreshold",
149
+ "value": 180000,
150
+ "operator": "gt"
151
+ },
152
+ "action": {
153
+ "route": "sonnet",
154
+ "description": "长上下文路由:基于token阈值选择模型"
155
+ },
156
+ "priority": 100,
157
+ "enabled": true
158
+ },
159
+ {
160
+ "name": "子代理规则",
161
+ "condition": {
162
+ "type": "fieldExists",
163
+ "field": "system.1.text",
164
+ "value": "<CCR-SUBAGENT-MODEL>",
165
+ "operator": "contains"
166
+ },
167
+ "action": {
168
+ "route": "${subagent}",
169
+ "description": "子代理路由:通过特殊标记选择模型"
170
+ },
171
+ "priority": 90,
172
+ "enabled": true
173
+ },
174
+ {
175
+ "name": "后台规则",
176
+ "condition": {
177
+ "type": "modelContains",
178
+ "value": "haiku",
179
+ "operator": "contains"
180
+ },
181
+ "action": {
182
+ "route": "haiku",
183
+ "description": "后台路由:Haiku模型自动使用轻量级模型"
184
+ },
185
+ "priority": 80,
186
+ "enabled": true
187
+ },
188
+ {
189
+ "name": "网络搜索规则",
190
+ "condition": {
191
+ "type": "toolExists",
192
+ "value": "web_search",
193
+ "operator": "exists"
194
+ },
195
+ "action": {
196
+ "route": "sonnet",
197
+ "description": "网络搜索路由:检测到web_search工具时使用特定模型"
198
+ },
199
+ "priority": 70,
200
+ "enabled": true
201
+ },
202
+ {
203
+ "name": "思考规则",
204
+ "condition": {
205
+ "type": "fieldExists",
206
+ "field": "thinking",
207
+ "value": true,
208
+ "operator": "eq"
209
+ },
210
+ "action": {
211
+ "route": "opus",
212
+ "description": "思考路由:启用thinking参数时使用特定模型"
213
+ },
214
+ "priority": 60,
215
+ "enabled": true
216
+ }
217
+ ],
218
+ "contextThreshold": {
219
+ "default": 60000,
220
+ "longContext": 180000
221
+ },
222
+ "cache": {
223
+ "enabled": true,
224
+ "ttl": 300000,
225
+ "maxSize": 1000
226
+ },
227
+ "debug": {
228
+ "enabled": true,
229
+ "logLevel": "info"
230
+ }
231
+ },
232
+ "StatusLine": {
233
+ "enabled": false,
234
+ "currentStyle": "default",
235
+ "default": {
236
+ "modules": []
237
+ },
238
+ "powerline": {
239
+ "modules": []
240
+ }
241
+ },
242
+ "CUSTOM_ROUTER_PATH": "",
243
+ "OPENAI_API_KEY": "",
244
+ "OPENAI_BASE_URL": "",
245
+ "OPENAI_MODEL": ""
246
+ }
@@ -0,0 +1,171 @@
1
+ {
2
+ "LOG": true,
3
+ "HOST": "127.0.0.1",
4
+ "PORT": 8080,
5
+ "APIKEY": "1",
6
+ "API_TIMEOUT_MS": 600000,
7
+ "Providers": [
8
+ {
9
+ "name": "openrouter",
10
+ "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
11
+ "api_key": "sk-or-v1-",
12
+ "models": [
13
+ "anthropic/claude-3.5-sonnet",
14
+ "anthropic/claude-3.7-sonnet:thinking",
15
+ "deepseek/deepseek-chat-v3-0324",
16
+ "@preset/kimi"
17
+ ],
18
+ "transformer": {
19
+ "use": ["openrouter"],
20
+ "deepseek/deepseek-chat-v3-0324": {
21
+ "use": ["tooluse"]
22
+ }
23
+ }
24
+ },
25
+ {
26
+ "name": "deepseek",
27
+ "api_base_url": "https://api.deepseek.com/chat/completions",
28
+ "api_key": "sk-",
29
+ "models": [
30
+ "deepseek-chat",
31
+ "deepseek-reasoner"
32
+ ],
33
+ "transformer": {
34
+ "use": ["deepseek"],
35
+ "deepseek-chat": {
36
+ "use": ["tooluse"]
37
+ }
38
+ }
39
+ },
40
+ {
41
+ "name": "gemini",
42
+ "api_base_url": "https://generativelanguage.googleapis.com/v1beta/models/",
43
+ "api_key": "",
44
+ "models": [
45
+ "gemini-2.5-flash",
46
+ "gemini-2.5-pro"
47
+ ],
48
+ "transformer": {
49
+ "use": ["gemini"]
50
+ }
51
+ }
52
+ ],
53
+ "Router": {
54
+ "engine": "unified",
55
+ "defaultRoute": "openrouter,anthropic/claude-3.5-sonnet",
56
+ "rules": [
57
+ {
58
+ "name": "longContext",
59
+ "priority": 100,
60
+ "enabled": true,
61
+ "condition": {
62
+ "type": "tokenThreshold",
63
+ "value": 60000,
64
+ "operator": "gt"
65
+ },
66
+ "action": {
67
+ "route": "gemini,gemini-2.5-pro",
68
+ "transformers": []
69
+ }
70
+ },
71
+ {
72
+ "name": "subagent",
73
+ "priority": 90,
74
+ "enabled": true,
75
+ "condition": {
76
+ "type": "fieldExists",
77
+ "field": "system.1.text",
78
+ "operator": "exists"
79
+ },
80
+ "action": {
81
+ "route": "${subagent}",
82
+ "transformers": []
83
+ }
84
+ },
85
+ {
86
+ "name": "background",
87
+ "priority": 80,
88
+ "enabled": true,
89
+ "condition": {
90
+ "type": "modelContains",
91
+ "value": "haiku",
92
+ "operator": "contains"
93
+ },
94
+ "action": {
95
+ "route": "openrouter,@preset/kimi",
96
+ "transformers": []
97
+ }
98
+ },
99
+ {
100
+ "name": "webSearch",
101
+ "priority": 70,
102
+ "enabled": true,
103
+ "condition": {
104
+ "type": "toolExists",
105
+ "value": "web_search",
106
+ "operator": "exists"
107
+ },
108
+ "action": {
109
+ "route": "gemini,gemini-2.5-flash",
110
+ "transformers": []
111
+ }
112
+ },
113
+ {
114
+ "name": "thinking",
115
+ "priority": 60,
116
+ "enabled": true,
117
+ "condition": {
118
+ "type": "fieldExists",
119
+ "field": "thinking",
120
+ "operator": "exists"
121
+ },
122
+ "action": {
123
+ "route": "deepseek,deepseek-reasoner",
124
+ "transformers": []
125
+ }
126
+ },
127
+ {
128
+ "name": "directMapping",
129
+ "priority": 50,
130
+ "enabled": true,
131
+ "condition": {
132
+ "type": "custom",
133
+ "customFunction": "directModelMapping"
134
+ },
135
+ "action": {
136
+ "route": "${mappedModel}",
137
+ "transformers": []
138
+ }
139
+ },
140
+ {
141
+ "name": "userSpecified",
142
+ "priority": 40,
143
+ "enabled": true,
144
+ "condition": {
145
+ "type": "custom",
146
+ "customFunction": "modelContainsComma"
147
+ },
148
+ "action": {
149
+ "route": "${userModel}",
150
+ "transformers": []
151
+ }
152
+ }
153
+ ],
154
+ "cache": {
155
+ "enabled": true,
156
+ "maxSize": 1000,
157
+ "ttl": 300000
158
+ },
159
+ "debug": {
160
+ "enabled": false,
161
+ "logLevel": "info",
162
+ "logToFile": true,
163
+ "logToConsole": true
164
+ },
165
+ "contextThreshold": {
166
+ "default": 1000,
167
+ "longContext": 60000
168
+ }
169
+ },
170
+ "NON_INTERACTIVE_MODE": false
171
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * 复杂路由规则示例
3
+ * 演示如何组合多个条件进行路由决策
4
+ */
5
+
6
+ /**
7
+ * 评估复杂的路由条件
8
+ * @param {RouteContext} context - 路由上下文
9
+ * @returns {boolean} - 路由决策结果
10
+ */
11
+ function evaluateComplexCondition(context) {
12
+ // 获取请求信息
13
+ const request = context.req;
14
+ const messages = context.messages || [];
15
+ const tools = context.tools || [];
16
+
17
+ // 条件1:检查是否为复杂任务
18
+ const isComplexTask = checkComplexity(messages);
19
+
20
+ // 条件2:检查是否使用了特定工具
21
+ const hasCodeTools = checkCodeTools(tools);
22
+
23
+ // 条件3:检查token数量
24
+ const hasLongContext = context.tokenCount > 10000;
25
+
26
+ // 条件4:检查是否有系统指令
27
+ const hasSystemInstruction = checkSystemInstruction(context);
28
+
29
+ // 组合决策逻辑
30
+ const score = calculateRouteScore({
31
+ complexity: isComplexTask,
32
+ hasCode: hasCodeTools,
33
+ longContext: hasLongContext,
34
+ hasSystem: hasSystemInstruction
35
+ });
36
+
37
+ console.log(`复杂路由评估:`, {
38
+ complexity: isComplexTask,
39
+ hasCode: hasCodeTools,
40
+ longContext: hasLongContext,
41
+ hasSystem: hasSystemInstruction,
42
+ score
43
+ });
44
+
45
+ // 分数超过3分使用高级模型
46
+ return score >= 3;
47
+ }
48
+
49
+ /**
50
+ * 检查任务复杂度
51
+ */
52
+ function checkComplexity(messages) {
53
+ const userMessages = messages.filter(m => m.role === 'user');
54
+
55
+ if (userMessages.length === 0) return false;
56
+
57
+ const lastMessage = userMessages[userMessages.length - 1];
58
+ const content = typeof lastMessage.content === 'string'
59
+ ? lastMessage.content
60
+ : lastMessage.content.map(item => item.text).join('');
61
+
62
+ // 简单的复杂度指标
63
+ const indicators = [
64
+ content.includes('分析') || content.includes('设计'),
65
+ content.includes('实现') || content.includes('开发'),
66
+ content.includes('优化') || content.includes('重构'),
67
+ content.length > 500, // 长文本
68
+ (content.match(/[??]/g) || []).length > 3 // 多个问题
69
+ ];
70
+
71
+ return indicators.filter(Boolean).length >= 2;
72
+ }
73
+
74
+ /**
75
+ * 检查是否包含代码相关工具
76
+ */
77
+ function checkCodeTools(tools) {
78
+ const codeToolNames = ['code_interpreter', 'run_code', 'execute', 'bash', 'terminal'];
79
+ return tools.some(tool => {
80
+ const toolName = tool.type || tool.function?.name || '';
81
+ return codeToolNames.includes(toolName);
82
+ });
83
+ }
84
+
85
+ /**
86
+ * 检查是否有系统指令
87
+ */
88
+ function checkSystemInstruction(context) {
89
+ if (!context.system || context.system.length === 0) return false;
90
+
91
+ const systemText = typeof context.system[0] === 'string'
92
+ ? context.system[0]
93
+ : context.system[0]?.text || '';
94
+
95
+ const instructionKeywords = ['请', '必须', '要求', '确保', '严格'];
96
+ return instructionKeywords.some(keyword => systemText.includes(keyword));
97
+ }
98
+
99
+ /**
100
+ * 计算路由分数
101
+ */
102
+ function calculateRouteScore(factors) {
103
+ let score = 0;
104
+
105
+ if (factors.complexity) score += 1;
106
+ if (factors.hasCode) score += 1;
107
+ if (factors.longContext) score += 1;
108
+ if (factors.hasSystem) score += 1;
109
+
110
+ return score;
111
+ }
112
+
113
+ // 导出函数
114
+ module.exports = {
115
+ evaluateComplexCondition
116
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 基于时间的路由规则
3
+ * 根据当前时间决定使用不同的模型
4
+ */
5
+
6
+ /**
7
+ * 检查是否为工作时间
8
+ * @param {RouteContext} context - 路由上下文
9
+ * @returns {boolean} - 是否为工作时间
10
+ */
11
+ function isBusinessHours(context) {
12
+ const now = new Date();
13
+ const hour = now.getHours();
14
+ const dayOfWeek = now.getDay(); // 0=周日, 1=周一, ..., 6=周六
15
+
16
+ // 工作时间:周一到周五,9:00-18:00
17
+ const isWeekday = dayOfWeek >= 1 && dayOfWeek <= 5;
18
+ const isWorkHour = hour >= 9 && hour < 18;
19
+
20
+ const inBusinessHours = isWeekday && isWorkHour;
21
+
22
+ console.log(`时间检查: ${now.toISOString()}, 小时: ${hour}, 星期${dayOfWeek}, 工作时间: ${inBusinessHours}`);
23
+
24
+ return inBusinessHours;
25
+ }
26
+
27
+ /**
28
+ * 检查是否为高峰时段
29
+ * @param {RouteContext} context - 路由上下文
30
+ * @returns {boolean} - 是否为高峰时段
31
+ */
32
+ function isPeakHours(context) {
33
+ const now = new Date();
34
+ const hour = now.getHours();
35
+
36
+ // 高峰时段:9:00-11:00 和 14:00-17:00
37
+ const isMorningPeak = hour >= 9 && hour < 11;
38
+ const isAfternoonPeak = hour >= 14 && hour < 17;
39
+
40
+ const isPeakHours = isMorningPeak || isAfternoonPeak;
41
+
42
+ console.log(`高峰时段检查: 当前${hour}点,高峰: ${isPeakHours}`);
43
+
44
+ return isPeakHours;
45
+ }
46
+
47
+ // 导出多个函数
48
+ module.exports = {
49
+ isBusinessHours,
50
+ isPeakHours
51
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * 用户偏好路由规则
3
+ * 根据用户的系统消息中保存的偏好设置进行路由
4
+ */
5
+
6
+ // 模拟用户偏好数据库
7
+ const userPreferences = {
8
+ 'user@example.com': {
9
+ preferredModel: 'gpt-4',
10
+ provider: 'openai'
11
+ },
12
+ 'admin@example.com': {
13
+ preferredModel: 'claude-3-opus',
14
+ provider: 'anthropic'
15
+ }
16
+ };
17
+
18
+ /**
19
+ * 检查用户偏好
20
+ * @param {RouteContext} context - 路由上下文
21
+ * @returns {boolean} - 是否匹配用户偏好
22
+ */
23
+ function checkUserPreference(context) {
24
+ // 从系统消息或会话信息中提取用户标识
25
+ const userEmail = extractUserEmail(context);
26
+
27
+ if (!userEmail) {
28
+ console.log(`无法识别用户邮箱,使用默认路由`);
29
+ return false;
30
+ }
31
+
32
+ const preference = userPreferences[userEmail];
33
+ if (!preference) {
34
+ console.log(`用户 ${userEmail} 没有设置偏好,使用默认路由`);
35
+ return false;
36
+ }
37
+
38
+ // 记录匹配结果
39
+ console.log(`用户偏好匹配: ${userEmail} -> ${preference.provider}/${preference.preferredModel}`);
40
+ return true;
41
+ }
42
+
43
+ /**
44
+ * 从上下文中提取用户邮箱
45
+ */
46
+ function extractUserEmail(context) {
47
+ // 尝试从多个可能的来源获取用户邮箱
48
+ if (context.req?.headers?.['x-user-email']) {
49
+ return context.req.headers['x-user-email'];
50
+ }
51
+
52
+ if (context.sessionId && context.sessionId.includes('@')) {
53
+ return context.sessionId;
54
+ }
55
+
56
+ // 可以从系统消息中解析
57
+ if (context.system && context.system[0]?.text) {
58
+ const emailMatch = context.system[0].text.match(/user:\s*([^\s]+)/);
59
+ if (emailMatch) {
60
+ return emailMatch[1];
61
+ }
62
+ }
63
+
64
+ return null;
65
+ }
66
+
67
+ // 导出函数
68
+ module.exports = {
69
+ checkUserPreference
70
+ };