aicodeswitch 3.6.1 → 3.6.3
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/bin/upgrade.js +29 -5
- package/dist/server/main.js +67 -18
- package/dist/server/proxy-server.js +259 -23
- package/dist/server/transformers/claude-openai.js +2 -2
- package/dist/ui/assets/index-Y_qNeXCt.js +511 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/index-B_Vn-9wd.js +0 -511
package/bin/upgrade.js
CHANGED
|
@@ -231,8 +231,7 @@ const upgrade = async () => {
|
|
|
231
231
|
chalk.yellow.bold('⚠️ Sudo privileges required\n\n') +
|
|
232
232
|
chalk.white('This operation requires ') + chalk.yellow.bold('sudo') + chalk.white(' privileges.\n\n') +
|
|
233
233
|
chalk.white('Please run the following command to upgrade:\n\n') +
|
|
234
|
-
chalk.cyan.bold(' sudo npm install -g ' + PACKAGE_NAME + '@latest
|
|
235
|
-
chalk.gray('After upgrading, run ') + chalk.cyan('aicos restart') + chalk.gray(' to restart the server.'),
|
|
234
|
+
chalk.cyan.bold(' sudo npm install -g ' + PACKAGE_NAME + '@latest'),
|
|
236
235
|
{
|
|
237
236
|
padding: 1,
|
|
238
237
|
margin: 1,
|
|
@@ -241,6 +240,19 @@ const upgrade = async () => {
|
|
|
241
240
|
}
|
|
242
241
|
));
|
|
243
242
|
console.log('');
|
|
243
|
+
console.log(boxen(
|
|
244
|
+
chalk.red.bold('⚠️ 重启服务以使更改生效\n\n') +
|
|
245
|
+
chalk.white('升级完成后,必须重启服务才能使用新版本!\n\n') +
|
|
246
|
+
chalk.white('执行命令: ') + chalk.cyan.bold('aicos restart'),
|
|
247
|
+
{
|
|
248
|
+
padding: 1,
|
|
249
|
+
margin: 1,
|
|
250
|
+
borderStyle: 'round',
|
|
251
|
+
borderColor: 'red',
|
|
252
|
+
backgroundColor: 'red'
|
|
253
|
+
}
|
|
254
|
+
));
|
|
255
|
+
console.log('');
|
|
244
256
|
process.exit(0);
|
|
245
257
|
}
|
|
246
258
|
|
|
@@ -274,9 +286,21 @@ const upgrade = async () => {
|
|
|
274
286
|
}
|
|
275
287
|
));
|
|
276
288
|
console.log('');
|
|
277
|
-
console.log(
|
|
278
|
-
|
|
279
|
-
|
|
289
|
+
console.log(boxen(
|
|
290
|
+
chalk.red.bold('⚠️ 重启服务以使更改生效\n\n') +
|
|
291
|
+
chalk.white('升级完成后,必须重启服务才能使用新版本!\n\n') +
|
|
292
|
+
chalk.white('执行命令: ') + chalk.cyan.bold('aicos restart'),
|
|
293
|
+
{
|
|
294
|
+
padding: 1,
|
|
295
|
+
margin: 1,
|
|
296
|
+
borderStyle: 'round',
|
|
297
|
+
borderColor: 'red',
|
|
298
|
+
backgroundColor: 'red'
|
|
299
|
+
}
|
|
300
|
+
));
|
|
301
|
+
console.log('');
|
|
302
|
+
console.log(chalk.cyan('💡 其他命令:\n'));
|
|
303
|
+
console.log(chalk.white(' • 查看版本: ') + chalk.cyan('aicos version'));
|
|
280
304
|
console.log('\n');
|
|
281
305
|
};
|
|
282
306
|
|
package/dist/server/main.js
CHANGED
|
@@ -243,10 +243,29 @@ const updateClaudeAgentTeamsConfig = (enableAgentTeams) => __awaiter(void 0, voi
|
|
|
243
243
|
return false;
|
|
244
244
|
}
|
|
245
245
|
});
|
|
246
|
-
const
|
|
246
|
+
const VALID_CODEX_REASONING_EFFORTS = ['low', 'medium', 'high'];
|
|
247
|
+
const DEFAULT_CODEX_REASONING_EFFORT = 'high';
|
|
248
|
+
const isCodexReasoningEffort = (value) => {
|
|
249
|
+
return typeof value === 'string' && VALID_CODEX_REASONING_EFFORTS.includes(value);
|
|
250
|
+
};
|
|
251
|
+
const buildCodexConfigToml = (modelReasoningEffort) => {
|
|
252
|
+
const localPort = process.env.PORT ? parseInt(process.env.PORT, 10) : 4567;
|
|
253
|
+
return `model_provider = "aicodeswitch"
|
|
254
|
+
model = "gpt-5.1-codex"
|
|
255
|
+
model_reasoning_effort = "${modelReasoningEffort}"
|
|
256
|
+
disable_response_storage = true
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
[model_providers.aicodeswitch]
|
|
260
|
+
name = "aicodeswitch"
|
|
261
|
+
base_url = "http://${host}:${localPort}/codex"
|
|
262
|
+
wire_api = "responses"
|
|
263
|
+
requires_openai_auth = true
|
|
264
|
+
`;
|
|
265
|
+
};
|
|
266
|
+
const writeCodexConfig = (dbManager_1, ...args_1) => __awaiter(void 0, [dbManager_1, ...args_1], void 0, function* (dbManager, modelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT) {
|
|
247
267
|
try {
|
|
248
268
|
const homeDir = os_1.default.homedir();
|
|
249
|
-
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 4567;
|
|
250
269
|
const config = dbManager.getConfig();
|
|
251
270
|
// Codex config.toml
|
|
252
271
|
const codexDir = path_1.default.join(homeDir, '.codex');
|
|
@@ -277,19 +296,7 @@ const writeCodexConfig = (dbManager) => __awaiter(void 0, void 0, void 0, functi
|
|
|
277
296
|
if (!fs_1.default.existsSync(codexDir)) {
|
|
278
297
|
fs_1.default.mkdirSync(codexDir, { recursive: true });
|
|
279
298
|
}
|
|
280
|
-
|
|
281
|
-
model = "gpt-5.1-codex"
|
|
282
|
-
model_reasoning_effort = "high"
|
|
283
|
-
disable_response_storage = true
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
[model_providers.aicodeswitch]
|
|
287
|
-
name = "aicodeswitch"
|
|
288
|
-
base_url = "http://${host}:${port}/codex"
|
|
289
|
-
wire_api = "responses"
|
|
290
|
-
requires_openai_auth = true
|
|
291
|
-
`;
|
|
292
|
-
fs_1.default.writeFileSync(codexConfigPath, codexConfig);
|
|
299
|
+
fs_1.default.writeFileSync(codexConfigPath, buildCodexConfigToml(modelReasoningEffort));
|
|
293
300
|
// Codex auth.json
|
|
294
301
|
const codexAuthPath = path_1.default.join(codexDir, 'auth.json');
|
|
295
302
|
// 同样处理 auth.json 的备份
|
|
@@ -308,7 +315,7 @@ requires_openai_auth = true
|
|
|
308
315
|
configType: 'codex',
|
|
309
316
|
timestamp: Date.now(),
|
|
310
317
|
originalHash: originalConfigHash,
|
|
311
|
-
proxyMarker: `http://${host}:${
|
|
318
|
+
proxyMarker: `http://${host}:${process.env.PORT ? parseInt(process.env.PORT, 10) : 4567}/codex`,
|
|
312
319
|
files: [
|
|
313
320
|
{
|
|
314
321
|
originalPath: codexConfigPath,
|
|
@@ -329,6 +336,35 @@ requires_openai_auth = true
|
|
|
329
336
|
return false;
|
|
330
337
|
}
|
|
331
338
|
});
|
|
339
|
+
const updateCodexReasoningEffortConfig = (modelReasoningEffort) => __awaiter(void 0, void 0, void 0, function* () {
|
|
340
|
+
try {
|
|
341
|
+
const homeDir = os_1.default.homedir();
|
|
342
|
+
const codexConfigPath = path_1.default.join(homeDir, '.codex/config.toml');
|
|
343
|
+
if (!fs_1.default.existsSync(codexConfigPath)) {
|
|
344
|
+
console.error('Codex config.toml does not exist');
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
const configStatus = (0, config_metadata_1.checkCodexConfigStatus)();
|
|
348
|
+
if (!configStatus.isOverwritten) {
|
|
349
|
+
console.error('Codex config is not overwritten by proxy. Please activate a route first.');
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
fs_1.default.writeFileSync(codexConfigPath, buildCodexConfigToml(modelReasoningEffort));
|
|
353
|
+
const metadata = (0, config_metadata_1.loadMetadata)('codex');
|
|
354
|
+
if (metadata && metadata.files[0]) {
|
|
355
|
+
metadata.files[0].currentHash = (0, crypto_1.createHash)('sha256')
|
|
356
|
+
.update(fs_1.default.readFileSync(codexConfigPath, 'utf-8'))
|
|
357
|
+
.digest('hex');
|
|
358
|
+
metadata.timestamp = Date.now();
|
|
359
|
+
(0, config_metadata_1.saveMetadata)(metadata);
|
|
360
|
+
}
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
console.error('Failed to update Codex reasoning effort config:', error);
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
});
|
|
332
368
|
const restoreClaudeConfig = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
333
369
|
try {
|
|
334
370
|
const homeDir = os_1.default.homedir();
|
|
@@ -1384,8 +1420,12 @@ ${instruction}
|
|
|
1384
1420
|
const result = yield writeClaudeConfig(dbManager, enableAgentTeams);
|
|
1385
1421
|
res.json(result);
|
|
1386
1422
|
})));
|
|
1387
|
-
app.post('/api/write-config/codex', asyncHandler((
|
|
1388
|
-
const
|
|
1423
|
+
app.post('/api/write-config/codex', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1424
|
+
const requestedEffort = req.body.modelReasoningEffort;
|
|
1425
|
+
const modelReasoningEffort = isCodexReasoningEffort(requestedEffort)
|
|
1426
|
+
? requestedEffort
|
|
1427
|
+
: DEFAULT_CODEX_REASONING_EFFORT;
|
|
1428
|
+
const result = yield writeCodexConfig(dbManager, modelReasoningEffort);
|
|
1389
1429
|
res.json(result);
|
|
1390
1430
|
})));
|
|
1391
1431
|
// 更新Claude Code配置中的Agent Teams设置(当路由已激活时)
|
|
@@ -1394,6 +1434,15 @@ ${instruction}
|
|
|
1394
1434
|
const result = yield updateClaudeAgentTeamsConfig(enableAgentTeams);
|
|
1395
1435
|
res.json(result);
|
|
1396
1436
|
})));
|
|
1437
|
+
app.post('/api/update-codex-reasoning-effort', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1438
|
+
const requestedEffort = req.body.modelReasoningEffort;
|
|
1439
|
+
if (!isCodexReasoningEffort(requestedEffort)) {
|
|
1440
|
+
res.status(400).json({ error: 'Invalid modelReasoningEffort' });
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const result = yield updateCodexReasoningEffortConfig(requestedEffort);
|
|
1444
|
+
res.json(result);
|
|
1445
|
+
})));
|
|
1397
1446
|
app.post('/api/restore-config/claude', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1398
1447
|
const result = yield restoreClaudeConfig();
|
|
1399
1448
|
res.json(result);
|
|
@@ -56,6 +56,7 @@ const claude_openai_1 = require("./transformers/claude-openai");
|
|
|
56
56
|
const gemini_1 = require("./transformers/gemini");
|
|
57
57
|
const types_1 = require("../types");
|
|
58
58
|
const mcp_image_handler_1 = require("./mcp-image-handler");
|
|
59
|
+
const type_migration_1 = require("./type-migration");
|
|
59
60
|
const SUPPORTED_TARGETS = ['claude-code', 'codex'];
|
|
60
61
|
class ProxyServer {
|
|
61
62
|
constructor(dbManager, app) {
|
|
@@ -136,6 +137,58 @@ class ProxyServer {
|
|
|
136
137
|
if (!route) {
|
|
137
138
|
return res.status(404).json({ error: 'No matching route found' });
|
|
138
139
|
}
|
|
140
|
+
// 高智商命令检测和会话状态管理
|
|
141
|
+
const highIqCommand = this.detectHighIqCommand(req.body);
|
|
142
|
+
const sessionId = this.defaultExtractSessionId(req, route.targetType);
|
|
143
|
+
if (highIqCommand === 'on') {
|
|
144
|
+
// 检查是否有可用的高智商规则
|
|
145
|
+
const highIqRule = yield this.findHighIqRule(route.id);
|
|
146
|
+
if (highIqRule) {
|
|
147
|
+
console.log('[HIGH-IQ] Command detected: ON');
|
|
148
|
+
// 更新会话状态
|
|
149
|
+
if (sessionId) {
|
|
150
|
+
const session = this.dbManager.getSession(sessionId);
|
|
151
|
+
this.dbManager.upsertSession({
|
|
152
|
+
id: sessionId,
|
|
153
|
+
targetType: route.targetType,
|
|
154
|
+
title: session === null || session === void 0 ? void 0 : session.title,
|
|
155
|
+
firstRequestAt: (session === null || session === void 0 ? void 0 : session.firstRequestAt) || Date.now(),
|
|
156
|
+
lastRequestAt: Date.now(),
|
|
157
|
+
vendorId: session === null || session === void 0 ? void 0 : session.vendorId,
|
|
158
|
+
vendorName: session === null || session === void 0 ? void 0 : session.vendorName,
|
|
159
|
+
serviceId: session === null || session === void 0 ? void 0 : session.serviceId,
|
|
160
|
+
serviceName: session === null || session === void 0 ? void 0 : session.serviceName,
|
|
161
|
+
model: session === null || session === void 0 ? void 0 : session.model,
|
|
162
|
+
totalTokens: (session === null || session === void 0 ? void 0 : session.totalTokens) || 0,
|
|
163
|
+
requestCount: ((session === null || session === void 0 ? void 0 : session.requestCount) || 0) + 1,
|
|
164
|
+
// 新增字段
|
|
165
|
+
highIqMode: true,
|
|
166
|
+
highIqRuleId: highIqRule.id,
|
|
167
|
+
highIqEnabledAt: Date.now()
|
|
168
|
+
});
|
|
169
|
+
console.log(`[HIGH-IQ] Session ${sessionId} enabled with rule ${highIqRule.id}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log('[HIGH-IQ] No available high-iq rule found');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if (highIqCommand === 'off') {
|
|
177
|
+
console.log('[HIGH-IQ] Command detected: OFF');
|
|
178
|
+
// 关闭会话的高智商模式
|
|
179
|
+
if (sessionId) {
|
|
180
|
+
const session = this.dbManager.getSession(sessionId);
|
|
181
|
+
if (session === null || session === void 0 ? void 0 : session.highIqMode) {
|
|
182
|
+
this.dbManager.upsertSession(Object.assign(Object.assign({}, session), { highIqMode: false, lastRequestAt: Date.now(), requestCount: (session.requestCount || 0) + 1 }));
|
|
183
|
+
console.log(`[HIGH-IQ] Session ${sessionId} disabled`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// 移除命令前缀(在发送给上游API之前)
|
|
188
|
+
if (highIqCommand) {
|
|
189
|
+
req.body = this.removeHighIqCommand(req.body);
|
|
190
|
+
console.log(`[HIGH-IQ] Removed command prefix`);
|
|
191
|
+
}
|
|
139
192
|
// 检查是否启用故障切换
|
|
140
193
|
const enableFailover = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.enableFailover) !== false; // 默认为 true
|
|
141
194
|
if (!enableFailover) {
|
|
@@ -341,6 +394,58 @@ class ProxyServer {
|
|
|
341
394
|
if (!route) {
|
|
342
395
|
return res.status(404).json({ error: `No active route found for target type: ${targetType}` });
|
|
343
396
|
}
|
|
397
|
+
// 高智商命令检测和会话状态管理
|
|
398
|
+
const highIqCommand = this.detectHighIqCommand(req.body);
|
|
399
|
+
const sessionId = this.defaultExtractSessionId(req, targetType);
|
|
400
|
+
if (highIqCommand === 'on') {
|
|
401
|
+
// 检查是否有可用的高智商规则
|
|
402
|
+
const highIqRule = yield this.findHighIqRule(route.id);
|
|
403
|
+
if (highIqRule) {
|
|
404
|
+
console.log('[HIGH-IQ] Command detected: ON');
|
|
405
|
+
// 更新会话状态
|
|
406
|
+
if (sessionId) {
|
|
407
|
+
const session = this.dbManager.getSession(sessionId);
|
|
408
|
+
this.dbManager.upsertSession({
|
|
409
|
+
id: sessionId,
|
|
410
|
+
targetType: route.targetType,
|
|
411
|
+
title: session === null || session === void 0 ? void 0 : session.title,
|
|
412
|
+
firstRequestAt: (session === null || session === void 0 ? void 0 : session.firstRequestAt) || Date.now(),
|
|
413
|
+
lastRequestAt: Date.now(),
|
|
414
|
+
vendorId: session === null || session === void 0 ? void 0 : session.vendorId,
|
|
415
|
+
vendorName: session === null || session === void 0 ? void 0 : session.vendorName,
|
|
416
|
+
serviceId: session === null || session === void 0 ? void 0 : session.serviceId,
|
|
417
|
+
serviceName: session === null || session === void 0 ? void 0 : session.serviceName,
|
|
418
|
+
model: session === null || session === void 0 ? void 0 : session.model,
|
|
419
|
+
totalTokens: (session === null || session === void 0 ? void 0 : session.totalTokens) || 0,
|
|
420
|
+
requestCount: ((session === null || session === void 0 ? void 0 : session.requestCount) || 0) + 1,
|
|
421
|
+
// 新增字段
|
|
422
|
+
highIqMode: true,
|
|
423
|
+
highIqRuleId: highIqRule.id,
|
|
424
|
+
highIqEnabledAt: Date.now()
|
|
425
|
+
});
|
|
426
|
+
console.log(`[HIGH-IQ] Session ${sessionId} enabled with rule ${highIqRule.id}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
console.log('[HIGH-IQ] No available high-iq rule found');
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else if (highIqCommand === 'off') {
|
|
434
|
+
console.log('[HIGH-IQ] Command detected: OFF');
|
|
435
|
+
// 关闭会话的高智商模式
|
|
436
|
+
if (sessionId) {
|
|
437
|
+
const session = this.dbManager.getSession(sessionId);
|
|
438
|
+
if (session === null || session === void 0 ? void 0 : session.highIqMode) {
|
|
439
|
+
this.dbManager.upsertSession(Object.assign(Object.assign({}, session), { highIqMode: false, lastRequestAt: Date.now(), requestCount: (session.requestCount || 0) + 1 }));
|
|
440
|
+
console.log(`[HIGH-IQ] Session ${sessionId} disabled`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// 移除命令前缀(在发送给上游API之前)
|
|
445
|
+
if (highIqCommand) {
|
|
446
|
+
req.body = this.removeHighIqCommand(req.body);
|
|
447
|
+
console.log(`[HIGH-IQ] Removed command prefix`);
|
|
448
|
+
}
|
|
344
449
|
// 检查是否启用故障切换
|
|
345
450
|
const enableFailover = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.enableFailover) !== false; // 默认为 true
|
|
346
451
|
if (!enableFailover) {
|
|
@@ -771,6 +876,24 @@ class ProxyServer {
|
|
|
771
876
|
return undefined;
|
|
772
877
|
const body = req.body;
|
|
773
878
|
const requestModel = body === null || body === void 0 ? void 0 : body.model;
|
|
879
|
+
// 新增:检查会话是否在高智商模式中
|
|
880
|
+
const targetType = this.getRouteTargetType(routeId);
|
|
881
|
+
if (targetType) {
|
|
882
|
+
const sessionId = this.defaultExtractSessionId(req, targetType);
|
|
883
|
+
if (sessionId) {
|
|
884
|
+
const session = this.dbManager.getSession(sessionId);
|
|
885
|
+
if ((session === null || session === void 0 ? void 0 : session.highIqMode) && session.highIqRuleId) {
|
|
886
|
+
// 验证规则是否仍然可用
|
|
887
|
+
const highIqRule = yield this.findHighIqRule(routeId);
|
|
888
|
+
if (highIqRule && highIqRule.id === session.highIqRuleId) {
|
|
889
|
+
console.log(`[HIGH-IQ] Session ${sessionId} using high-iq rule ${highIqRule.id}`);
|
|
890
|
+
return highIqRule;
|
|
891
|
+
}
|
|
892
|
+
// 规则不可用,自动关闭高智商模式
|
|
893
|
+
console.log(`[HIGH-IQ] Rule ${session.highIqRuleId} no longer available, auto-disable`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
774
897
|
// 1. 首先查找 model-mapping 类型的规则,按 sortOrder 降序匹配
|
|
775
898
|
if (requestModel) {
|
|
776
899
|
const modelMappingRules = rules.filter(rule => rule.contentType === 'model-mapping' &&
|
|
@@ -1164,36 +1287,128 @@ class ProxyServer {
|
|
|
1164
1287
|
}
|
|
1165
1288
|
return false;
|
|
1166
1289
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1290
|
+
/**
|
|
1291
|
+
* 检测用户消息中的高智商命令
|
|
1292
|
+
* @returns 'on' - 开启命令 (!!), 'off' - 关闭命令 (!x), null - 无命令
|
|
1293
|
+
*/
|
|
1294
|
+
detectHighIqCommand(body) {
|
|
1295
|
+
const messages = body === null || body === void 0 ? void 0 : body.messages;
|
|
1296
|
+
if (!Array.isArray(messages)) {
|
|
1297
|
+
return null;
|
|
1170
1298
|
}
|
|
1171
|
-
//
|
|
1172
|
-
|
|
1173
|
-
|
|
1299
|
+
// 只检查最后一条用户消息
|
|
1300
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1301
|
+
const message = messages[i];
|
|
1174
1302
|
if ((message === null || message === void 0 ? void 0 : message.role) !== 'user')
|
|
1175
1303
|
continue;
|
|
1176
1304
|
const content = message === null || message === void 0 ? void 0 : message.content;
|
|
1177
|
-
// 处理字符串类型的 content
|
|
1178
1305
|
if (typeof content === 'string') {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1306
|
+
const trimmed = content.trim();
|
|
1307
|
+
if (trimmed.startsWith('!x')) {
|
|
1308
|
+
return 'off';
|
|
1309
|
+
}
|
|
1310
|
+
if (trimmed.startsWith('!!')) {
|
|
1311
|
+
return 'on';
|
|
1182
1312
|
}
|
|
1183
1313
|
}
|
|
1184
|
-
// 处理数组类型的 content
|
|
1185
1314
|
else if (Array.isArray(content)) {
|
|
1186
1315
|
for (const block of content) {
|
|
1187
1316
|
if ((block === null || block === void 0 ? void 0 : block.type) === 'text' && typeof block.text === 'string') {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1317
|
+
const trimmed = block.text.trim();
|
|
1318
|
+
if (trimmed.startsWith('!x')) {
|
|
1319
|
+
return 'off';
|
|
1320
|
+
}
|
|
1321
|
+
if (trimmed.startsWith('!!')) {
|
|
1322
|
+
return 'on';
|
|
1191
1323
|
}
|
|
1192
1324
|
}
|
|
1193
1325
|
}
|
|
1194
1326
|
}
|
|
1327
|
+
break; // 只检查最后一条用户消息
|
|
1195
1328
|
}
|
|
1196
|
-
return
|
|
1329
|
+
return null;
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* 移除用户消息中的高智商命令前缀
|
|
1333
|
+
*/
|
|
1334
|
+
removeHighIqCommand(body) {
|
|
1335
|
+
if (!(body === null || body === void 0 ? void 0 : body.messages))
|
|
1336
|
+
return body;
|
|
1337
|
+
const processed = JSON.parse(JSON.stringify(body));
|
|
1338
|
+
for (let i = processed.messages.length - 1; i >= 0; i--) {
|
|
1339
|
+
const message = processed.messages[i];
|
|
1340
|
+
if ((message === null || message === void 0 ? void 0 : message.role) !== 'user')
|
|
1341
|
+
continue;
|
|
1342
|
+
const content = message === null || message === void 0 ? void 0 : message.content;
|
|
1343
|
+
if (typeof content === 'string') {
|
|
1344
|
+
const trimmed = content.trim();
|
|
1345
|
+
if (trimmed.startsWith('!x')) {
|
|
1346
|
+
message.content = trimmed.replace(/^!x\s*/, '').trim();
|
|
1347
|
+
}
|
|
1348
|
+
else if (trimmed.startsWith('!!')) {
|
|
1349
|
+
message.content = trimmed.replace(/^!!\s*/, '').trim();
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
else if (Array.isArray(content)) {
|
|
1353
|
+
for (const block of content) {
|
|
1354
|
+
if ((block === null || block === void 0 ? void 0 : block.type) === 'text' && typeof block.text === 'string') {
|
|
1355
|
+
const trimmed = block.text.trim();
|
|
1356
|
+
if (trimmed.startsWith('!x')) {
|
|
1357
|
+
block.text = trimmed.replace(/^!x\s*/, '').trim();
|
|
1358
|
+
}
|
|
1359
|
+
else if (trimmed.startsWith('!!')) {
|
|
1360
|
+
block.text = trimmed.replace(/^!!\s*/, '').trim();
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
break; // 只处理最后一条用户消息
|
|
1366
|
+
}
|
|
1367
|
+
return processed;
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* 查找可用的高智商规则
|
|
1371
|
+
*/
|
|
1372
|
+
findHighIqRule(routeId) {
|
|
1373
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1374
|
+
const rules = this.getRulesByRouteId(routeId);
|
|
1375
|
+
if (!rules || rules.length === 0)
|
|
1376
|
+
return undefined;
|
|
1377
|
+
const highIqRules = rules.filter(rule => rule.contentType === 'high-iq' && !rule.isDisabled);
|
|
1378
|
+
// 过滤黑名单和限制
|
|
1379
|
+
for (const rule of highIqRules) {
|
|
1380
|
+
const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, 'high-iq');
|
|
1381
|
+
if (isBlacklisted)
|
|
1382
|
+
continue;
|
|
1383
|
+
// 检查并重置到期的规则
|
|
1384
|
+
this.dbManager.checkAndResetRuleIfNeeded(rule.id);
|
|
1385
|
+
this.dbManager.checkAndResetRequestCountIfNeeded(rule.id);
|
|
1386
|
+
// 检查token限制
|
|
1387
|
+
if (rule.tokenLimit && rule.totalTokensUsed !== undefined &&
|
|
1388
|
+
rule.totalTokensUsed >= rule.tokenLimit * 1000) {
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
// 检查请求次数限制
|
|
1392
|
+
if (rule.requestCountLimit && rule.totalRequestsUsed !== undefined &&
|
|
1393
|
+
rule.totalRequestsUsed >= rule.requestCountLimit) {
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1396
|
+
// 检查频率限制
|
|
1397
|
+
if (this.isFrequencyLimitExceeded(rule)) {
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
return rule;
|
|
1401
|
+
}
|
|
1402
|
+
return undefined;
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* 获取路由的目标类型
|
|
1407
|
+
*/
|
|
1408
|
+
getRouteTargetType(routeId) {
|
|
1409
|
+
const routes = this.dbManager.getRoutes();
|
|
1410
|
+
const route = routes.find(r => r.id === routeId);
|
|
1411
|
+
return (route === null || route === void 0 ? void 0 : route.targetType) || null;
|
|
1197
1412
|
}
|
|
1198
1413
|
hasBackgroundSignal(body) {
|
|
1199
1414
|
var _a, _b, _c;
|
|
@@ -1316,10 +1531,14 @@ class ProxyServer {
|
|
|
1316
1531
|
}
|
|
1317
1532
|
/** 判断是否为 Claude 相关类型(使用 x-api-key 认证) */
|
|
1318
1533
|
isClaudeSource(sourceType) {
|
|
1319
|
-
|
|
1534
|
+
// 向下兼容:支持旧类型 'claude-code'
|
|
1535
|
+
const normalized = sourceType === 'claude-code' ? 'claude' : sourceType;
|
|
1536
|
+
return normalized === 'claude-chat' || normalized === 'claude';
|
|
1320
1537
|
}
|
|
1321
1538
|
isOpenAIChatSource(sourceType) {
|
|
1322
|
-
|
|
1539
|
+
// 向下兼容:支持旧类型 'openai-responses'
|
|
1540
|
+
const normalized = sourceType === 'openai-responses' ? 'openai' : sourceType;
|
|
1541
|
+
return normalized === 'openai-chat' || normalized === 'openai' || normalized === 'deepseek-reasoning-chat';
|
|
1323
1542
|
}
|
|
1324
1543
|
/** 判断是否为 Gemini 类型 */
|
|
1325
1544
|
isGeminiSource(sourceType) {
|
|
@@ -1332,6 +1551,22 @@ class ProxyServer {
|
|
|
1332
1551
|
isChatType(sourceType) {
|
|
1333
1552
|
return sourceType.endsWith('-chat') || sourceType === 'gemini';
|
|
1334
1553
|
}
|
|
1554
|
+
/**
|
|
1555
|
+
* 构建 OpenAI Responses 类型的完整 URL
|
|
1556
|
+
* - baseUrl 以 /v{number} 结尾时,直接拼接请求路径
|
|
1557
|
+
* - baseUrl 不带版本时,自动补 /v1 再拼接请求路径
|
|
1558
|
+
* - 兼容请求路径本身已携带版本前缀(如 /v1/responses)场景
|
|
1559
|
+
*/
|
|
1560
|
+
buildOpenAIResponsesUrl(baseUrl, mappedPath) {
|
|
1561
|
+
const trimmedBase = baseUrl.trim().replace(/\/+$/, '');
|
|
1562
|
+
const normalizedPath = mappedPath.startsWith('/') || mappedPath === '' ? mappedPath : `/${mappedPath}`;
|
|
1563
|
+
const baseHasVersionSuffix = /\/v\d+$/i.test(trimmedBase);
|
|
1564
|
+
const pathHasVersionPrefix = /^\/v\d+(?:\/|$)/i.test(normalizedPath);
|
|
1565
|
+
if (baseHasVersionSuffix || pathHasVersionPrefix) {
|
|
1566
|
+
return `${trimmedBase}${normalizedPath}`;
|
|
1567
|
+
}
|
|
1568
|
+
return `${trimmedBase}/v1${normalizedPath}`;
|
|
1569
|
+
}
|
|
1335
1570
|
/**
|
|
1336
1571
|
* 构建 Gemini API 的完整 URL
|
|
1337
1572
|
* 用户只填写 base 地址(如 https://generativelanguage.googleapis.com)
|
|
@@ -1713,7 +1948,9 @@ class ProxyServer {
|
|
|
1713
1948
|
var _a, _b, _c, _d, _e;
|
|
1714
1949
|
res.locals.skipLog = true;
|
|
1715
1950
|
const startTime = Date.now();
|
|
1716
|
-
const
|
|
1951
|
+
const rawSourceType = service.sourceType || 'openai-chat';
|
|
1952
|
+
// 标准化 sourceType,将旧类型转换为新类型(向下兼容)
|
|
1953
|
+
const sourceType = (0, type_migration_1.normalizeSourceType)(rawSourceType);
|
|
1717
1954
|
const targetType = route.targetType;
|
|
1718
1955
|
const failoverEnabled = (options === null || options === void 0 ? void 0 : options.failoverEnabled) === true;
|
|
1719
1956
|
const forwardedToServiceName = options === null || options === void 0 ? void 0 : options.forwardedToServiceName;
|
|
@@ -1837,11 +2074,6 @@ class ProxyServer {
|
|
|
1837
2074
|
useMCPProcessing = false;
|
|
1838
2075
|
}
|
|
1839
2076
|
}
|
|
1840
|
-
// 高智商请求处理:移除 !! 前缀
|
|
1841
|
-
if (rule.contentType === 'high-iq' && requestBody.messages) {
|
|
1842
|
-
requestBody = this.removeHighIqPrefix(requestBody);
|
|
1843
|
-
console.log('[HIGH-IQ] Removed !! prefix from user messages');
|
|
1844
|
-
}
|
|
1845
2077
|
// 用于收集响应数据的变量
|
|
1846
2078
|
let responseHeadersForLog;
|
|
1847
2079
|
let responseBodyForLog;
|
|
@@ -2066,6 +2298,10 @@ class ProxyServer {
|
|
|
2066
2298
|
const model = requestBody.model || rule.targetModel || 'gemini-pro';
|
|
2067
2299
|
upstreamUrl = this.buildGeminiUrl(service.apiUrl, model, streamRequested);
|
|
2068
2300
|
}
|
|
2301
|
+
else if (sourceType === 'openai') {
|
|
2302
|
+
// OpenAI Responses 兼容模式:自动处理 baseUrl 是否包含 /v{number}
|
|
2303
|
+
upstreamUrl = this.buildOpenAIResponsesUrl(service.apiUrl, mappedPath);
|
|
2304
|
+
}
|
|
2069
2305
|
else if (this.isChatType(sourceType) || this.isGeminiChatSource(sourceType)) {
|
|
2070
2306
|
// Chat 类型(包括 gemini-chat)直接使用用户配置的完整 URL
|
|
2071
2307
|
upstreamUrl = service.apiUrl;
|
|
@@ -446,11 +446,11 @@ const transformClaudeRequestToOpenAIChat = (body, targetModel) => {
|
|
|
446
446
|
openaiBody.thinking = { type: claudeThinking.type };
|
|
447
447
|
}
|
|
448
448
|
// 为 OpenAI Responses API 添加 reasoning 配置
|
|
449
|
-
// 映射关系:enabled->medium, disabled->
|
|
449
|
+
// 映射关系:enabled->medium, disabled->low, auto->low
|
|
450
450
|
if (claudeThinking.type) {
|
|
451
451
|
const effortMap = {
|
|
452
452
|
'enabled': 'medium',
|
|
453
|
-
'disabled': '
|
|
453
|
+
'disabled': 'low',
|
|
454
454
|
'auto': 'low'
|
|
455
455
|
};
|
|
456
456
|
openaiBody.reasoning = {
|