aicodeswitch 3.6.3 → 3.9.2

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.
@@ -57,6 +57,7 @@ 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
59
  const type_migration_1 = require("./type-migration");
60
+ const original_config_reader_1 = require("./original-config-reader");
60
61
  const SUPPORTED_TARGETS = ['claude-code', 'codex'];
61
62
  class ProxyServer {
62
63
  constructor(dbManager, app) {
@@ -135,65 +136,21 @@ class ProxyServer {
135
136
  try {
136
137
  const route = this.findMatchingRoute(req);
137
138
  if (!route) {
138
- return res.status(404).json({ error: 'No matching route found' });
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');
139
+ // 没有找到激活的路由,尝试使用原始配置
140
+ const fallbackResult = yield this.handleFallbackToOriginalConfig(req, res);
141
+ if (fallbackResult) {
142
+ return; // 成功使用原始配置处理请求
174
143
  }
144
+ // 如果原始配置也不可用,返回错误
145
+ return res.status(404).json({ error: 'No matching route found and no original config available' });
175
146
  }
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
- }
147
+ // 高智商请求判定:从消息结构推断是否启用,不再使用 !x 显式关闭语法
148
+ const forcedContentType = yield this.prepareHighIqRouting(req, route, route.targetType);
192
149
  // 检查是否启用故障切换
193
150
  const enableFailover = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.enableFailover) !== false; // 默认为 true
194
151
  if (!enableFailover) {
195
152
  // 故障切换已禁用,使用传统的单一规则匹配
196
- const rule = yield this.findMatchingRule(route.id, req);
153
+ const rule = yield this.findMatchingRule(route.id, req, forcedContentType);
197
154
  if (!rule) {
198
155
  return res.status(404).json({ error: 'No matching rule found' });
199
156
  }
@@ -205,7 +162,7 @@ class ProxyServer {
205
162
  return;
206
163
  }
207
164
  // 启用故障切换:获取所有候选规则
208
- const allRules = this.getAllMatchingRules(route.id, req);
165
+ const allRules = this.getAllMatchingRules(route.id, req, forcedContentType);
209
166
  if (allRules.length === 0) {
210
167
  return res.status(404).json({ error: 'No matching rule found' });
211
168
  }
@@ -394,63 +351,13 @@ class ProxyServer {
394
351
  if (!route) {
395
352
  return res.status(404).json({ error: `No active route found for target type: ${targetType}` });
396
353
  }
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
- }
354
+ // 高智商请求判定:从消息结构推断是否启用,不再使用 !x 显式关闭语法
355
+ const forcedContentType = yield this.prepareHighIqRouting(req, route, targetType);
449
356
  // 检查是否启用故障切换
450
357
  const enableFailover = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.enableFailover) !== false; // 默认为 true
451
358
  if (!enableFailover) {
452
359
  // 故障切换已禁用,使用传统的单一规则匹配
453
- const rule = yield this.findMatchingRule(route.id, req);
360
+ const rule = yield this.findMatchingRule(route.id, req, forcedContentType);
454
361
  if (!rule) {
455
362
  return res.status(404).json({ error: 'No matching rule found' });
456
363
  }
@@ -462,7 +369,7 @@ class ProxyServer {
462
369
  return;
463
370
  }
464
371
  // 启用故障切换:获取所有候选规则
465
- const allRules = this.getAllMatchingRules(route.id, req);
372
+ const allRules = this.getAllMatchingRules(route.id, req, forcedContentType);
466
373
  if (allRules.length === 0) {
467
374
  return res.status(404).json({ error: 'No matching rule found' });
468
375
  }
@@ -646,6 +553,110 @@ class ProxyServer {
646
553
  const activeRoutes = this.getActiveRoutes();
647
554
  return activeRoutes.find(route => route.targetType === targetType && route.isActive);
648
555
  }
556
+ /**
557
+ * 当没有激活的路由时,fallback 到原始配置
558
+ * @returns true 表示成功处理,false 表示无法处理
559
+ */
560
+ handleFallbackToOriginalConfig(req, res) {
561
+ return __awaiter(this, void 0, void 0, function* () {
562
+ // 确定目标类型
563
+ let targetType;
564
+ if (req.path.startsWith('/claude-code/')) {
565
+ targetType = 'claude-code';
566
+ }
567
+ else if (req.path.startsWith('/codex/')) {
568
+ targetType = 'codex';
569
+ }
570
+ if (!targetType) {
571
+ return false;
572
+ }
573
+ // 读取原始配置
574
+ const originalConfig = (0, original_config_reader_1.readOriginalConfig)(targetType);
575
+ if (!originalConfig) {
576
+ console.log(`[FALLBACK] No original config available for ${targetType}`);
577
+ return false;
578
+ }
579
+ // 检查原始配置的 API URL 是否指向本系统(避免死循环)
580
+ if (this.isLocalProxyUrl(originalConfig.apiUrl, targetType)) {
581
+ console.error(`[FALLBACK] Original config points to local proxy, rejecting to avoid loop: ${originalConfig.apiUrl}`);
582
+ return false;
583
+ }
584
+ console.log(`[FALLBACK] Using original config for ${targetType}: ${originalConfig.apiUrl}`);
585
+ try {
586
+ // 创建临时的路由对象(用于传递给 proxyRequest)
587
+ const tempRoute = {
588
+ id: 'fallback-route',
589
+ name: 'Fallback to Original Config',
590
+ targetType: targetType,
591
+ isActive: true,
592
+ createdAt: Date.now(),
593
+ updatedAt: Date.now(),
594
+ };
595
+ // 创建临时的规则对象
596
+ const tempRule = {
597
+ id: 'fallback-rule',
598
+ routeId: 'fallback-route',
599
+ contentType: 'default',
600
+ targetServiceId: 'fallback-service',
601
+ createdAt: Date.now(),
602
+ updatedAt: Date.now(),
603
+ };
604
+ // 创建临时的服务对象
605
+ const tempService = {
606
+ id: 'fallback-service',
607
+ name: 'Original Config',
608
+ apiUrl: originalConfig.apiUrl,
609
+ apiKey: originalConfig.apiKey,
610
+ authType: originalConfig.authType,
611
+ sourceType: originalConfig.sourceType || (targetType === 'claude-code' ? 'claude' : 'openai'),
612
+ createdAt: Date.now(),
613
+ updatedAt: Date.now(),
614
+ };
615
+ // 调用 proxyRequest 处理请求,并标记使用原始配置
616
+ yield this.proxyRequest(req, res, tempRoute, tempRule, tempService, {
617
+ useOriginalConfig: true,
618
+ });
619
+ return true;
620
+ }
621
+ catch (error) {
622
+ console.error('[FALLBACK] Failed to use original config:', error);
623
+ return false;
624
+ }
625
+ });
626
+ }
627
+ /**
628
+ * 检查 API URL 是否指向本系统的代理服务
629
+ * 用于避免 fallback 时的死循环
630
+ */
631
+ isLocalProxyUrl(apiUrl, targetType) {
632
+ try {
633
+ const url = new URL(apiUrl);
634
+ // 检查是否是 localhost 或 127.0.0.1
635
+ const isLocalhost = url.hostname === 'localhost' ||
636
+ url.hostname === '127.0.0.1' ||
637
+ url.hostname === '::1' ||
638
+ url.hostname === '0.0.0.0';
639
+ if (!isLocalhost) {
640
+ return false;
641
+ }
642
+ // 检查端口是否是本系统的端口(默认 4567)
643
+ const serverPort = process.env.PORT ? parseInt(process.env.PORT, 10) : 4567;
644
+ const urlPort = url.port ? parseInt(url.port, 10) : (url.protocol === 'https:' ? 443 : 80);
645
+ const isSamePort = urlPort === serverPort;
646
+ if (!isSamePort) {
647
+ return false;
648
+ }
649
+ // 检查路径是否包含本系统的代理路径
650
+ const proxyPath = `/${targetType}`;
651
+ const hasProxyPath = url.pathname.startsWith(proxyPath) ||
652
+ url.pathname === proxyPath;
653
+ return hasProxyPath;
654
+ }
655
+ catch (error) {
656
+ // URL 解析失败,认为不是本地代理 URL
657
+ return false;
658
+ }
659
+ }
649
660
  findRouteByTargetType(targetType) {
650
661
  const activeRoutes = this.getActiveRoutes();
651
662
  return activeRoutes.find(route => route.targetType === targetType && route.isActive);
@@ -865,7 +876,7 @@ class ProxyServer {
865
876
  }
866
877
  return service;
867
878
  }
868
- findMatchingRule(routeId, req) {
879
+ findMatchingRule(routeId, req, forcedContentType) {
869
880
  return __awaiter(this, void 0, void 0, function* () {
870
881
  const rules = this.getRulesByRouteId(routeId);
871
882
  if (!rules || rules.length === 0)
@@ -876,27 +887,32 @@ class ProxyServer {
876
887
  return undefined;
877
888
  const body = req.body;
878
889
  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`);
890
+ const contentType = forcedContentType || this.determineContentType(req);
891
+ // 高智商规则优先于 model-mapping,确保 !!/推断命中时不会被模型映射覆盖
892
+ if (contentType === 'high-iq') {
893
+ const highIqRules = enabledRules.filter(rule => rule.contentType === 'high-iq');
894
+ for (const rule of highIqRules) {
895
+ const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, rule.contentType);
896
+ if (isBlacklisted) {
897
+ continue;
894
898
  }
899
+ this.dbManager.checkAndResetRuleIfNeeded(rule.id);
900
+ this.dbManager.checkAndResetRequestCountIfNeeded(rule.id);
901
+ if (rule.tokenLimit && rule.totalTokensUsed !== undefined && rule.totalTokensUsed >= rule.tokenLimit * 1000) {
902
+ continue;
903
+ }
904
+ if (rule.requestCountLimit && rule.totalRequestsUsed !== undefined && rule.totalRequestsUsed >= rule.requestCountLimit) {
905
+ continue;
906
+ }
907
+ if (this.isFrequencyLimitExceeded(rule)) {
908
+ continue;
909
+ }
910
+ return rule;
895
911
  }
896
912
  }
897
913
  // 1. 首先查找 model-mapping 类型的规则,按 sortOrder 降序匹配
898
914
  if (requestModel) {
899
- const modelMappingRules = rules.filter(rule => rule.contentType === 'model-mapping' &&
915
+ const modelMappingRules = enabledRules.filter(rule => rule.contentType === 'model-mapping' &&
900
916
  rule.replacedModel &&
901
917
  requestModel.includes(rule.replacedModel));
902
918
  // 过滤黑名单和token限制
@@ -924,8 +940,7 @@ class ProxyServer {
924
940
  }
925
941
  }
926
942
  // 2. 查找其他内容类型的规则
927
- const contentType = this.determineContentType(req);
928
- const contentTypeRules = rules.filter(rule => rule.contentType === contentType);
943
+ const contentTypeRules = enabledRules.filter(rule => rule.contentType === contentType);
929
944
  // 过滤黑名单和token限制
930
945
  for (const rule of contentTypeRules) {
931
946
  const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, contentType);
@@ -950,7 +965,7 @@ class ProxyServer {
950
965
  return rule;
951
966
  }
952
967
  // 3. 最后返回 default 规则
953
- const defaultRules = rules.filter(rule => rule.contentType === 'default');
968
+ const defaultRules = enabledRules.filter(rule => rule.contentType === 'default');
954
969
  // 过滤黑名单和token限制
955
970
  for (const rule of defaultRules) {
956
971
  const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, 'default');
@@ -977,7 +992,7 @@ class ProxyServer {
977
992
  return undefined;
978
993
  });
979
994
  }
980
- getAllMatchingRules(routeId, req) {
995
+ getAllMatchingRules(routeId, req, forcedContentType) {
981
996
  const rules = this.getRulesByRouteId(routeId);
982
997
  if (!rules || rules.length === 0)
983
998
  return [];
@@ -988,20 +1003,21 @@ class ProxyServer {
988
1003
  const body = req.body;
989
1004
  const requestModel = body === null || body === void 0 ? void 0 : body.model;
990
1005
  const candidates = [];
991
- // 1. Model mapping rules
992
- if (requestModel) {
993
- const modelMappingRules = enabledRules.filter(rule => rule.contentType === 'model-mapping' &&
1006
+ const contentType = forcedContentType || this.determineContentType(req);
1007
+ const prioritizeContentType = contentType === 'high-iq';
1008
+ const modelMappingRules = requestModel
1009
+ ? enabledRules.filter(rule => rule.contentType === 'model-mapping' &&
994
1010
  rule.replacedModel &&
995
- requestModel.includes(rule.replacedModel));
996
- candidates.push(...modelMappingRules);
997
- }
998
- // 2. Content type specific rules
999
- const contentType = this.determineContentType(req);
1011
+ requestModel.includes(rule.replacedModel))
1012
+ : [];
1000
1013
  const contentTypeRules = enabledRules.filter(rule => rule.contentType === contentType);
1001
- candidates.push(...contentTypeRules);
1002
- // 3. Default rules
1003
1014
  const defaultRules = enabledRules.filter(rule => rule.contentType === 'default');
1004
- candidates.push(...defaultRules);
1015
+ if (prioritizeContentType) {
1016
+ candidates.push(...contentTypeRules, ...modelMappingRules, ...defaultRules);
1017
+ }
1018
+ else {
1019
+ candidates.push(...modelMappingRules, ...contentTypeRules, ...defaultRules);
1020
+ }
1005
1021
  // 4. 检查并重置到期的规则
1006
1022
  candidates.forEach(rule => {
1007
1023
  this.dbManager.checkAndResetRuleIfNeeded(rule.id);
@@ -1260,112 +1276,245 @@ class ProxyServer {
1260
1276
  ((_b = body === null || body === void 0 ? void 0 : body.reasoning) === null || _b === void 0 ? void 0 : _b.enabled));
1261
1277
  }
1262
1278
  hasHighIqSignal(body) {
1263
- const messages = body === null || body === void 0 ? void 0 : body.messages;
1264
- if (!Array.isArray(messages)) {
1265
- return false;
1266
- }
1267
- for (const message of messages) {
1268
- if ((message === null || message === void 0 ? void 0 : message.role) !== 'user')
1279
+ return this.inferHighIqRouting(body, false).shouldUseHighIq;
1280
+ }
1281
+ inferHighIqRouting(body, previousMode) {
1282
+ const messages = this.extractConversationMessages(body);
1283
+ for (let i = messages.length - 1; i >= 0; i--) {
1284
+ const message = messages[i];
1285
+ if ((message === null || message === void 0 ? void 0 : message.role) !== 'user') {
1269
1286
  continue;
1270
- const content = message === null || message === void 0 ? void 0 : message.content;
1271
- // 处理字符串类型的 content
1272
- if (typeof content === 'string') {
1273
- if (content.trim().startsWith('!!')) {
1274
- return true;
1287
+ }
1288
+ const signal = this.analyzeUserMessageForHighIq(message);
1289
+ if (!signal.hasHumanText) {
1290
+ continue;
1291
+ }
1292
+ if (signal.hasHighIqPrefix) {
1293
+ return {
1294
+ shouldUseHighIq: true,
1295
+ shouldStripPrefix: true,
1296
+ decisionSource: 'human',
1297
+ };
1298
+ }
1299
+ return {
1300
+ shouldUseHighIq: false,
1301
+ shouldStripPrefix: false,
1302
+ decisionSource: 'human',
1303
+ };
1304
+ }
1305
+ if (previousMode) {
1306
+ return {
1307
+ shouldUseHighIq: true,
1308
+ shouldStripPrefix: false,
1309
+ decisionSource: 'fallback',
1310
+ };
1311
+ }
1312
+ return {
1313
+ shouldUseHighIq: false,
1314
+ shouldStripPrefix: false,
1315
+ decisionSource: 'none',
1316
+ };
1317
+ }
1318
+ prepareHighIqRouting(req, route, targetType) {
1319
+ return __awaiter(this, void 0, void 0, function* () {
1320
+ const sessionId = this.defaultExtractSessionId(req, targetType);
1321
+ const session = sessionId ? this.dbManager.getSession(sessionId) : null;
1322
+ const previousMode = (session === null || session === void 0 ? void 0 : session.highIqMode) === true;
1323
+ const inference = this.inferHighIqRouting(req.body, previousMode);
1324
+ if (inference.shouldStripPrefix) {
1325
+ req.body = this.removeHighIqPrefix(req.body);
1326
+ console.log('[HIGH-IQ] Removed "!!" prefix from user message');
1327
+ }
1328
+ if (!inference.shouldUseHighIq) {
1329
+ if (sessionId && (session === null || session === void 0 ? void 0 : session.highIqMode) && inference.decisionSource === 'human') {
1330
+ yield this.dbManager.updateSession(sessionId, {
1331
+ highIqMode: false,
1332
+ highIqRuleId: undefined,
1333
+ lastRequestAt: Date.now(),
1334
+ });
1335
+ console.log(`[HIGH-IQ] Session ${sessionId} auto-disabled by latest human message`);
1275
1336
  }
1337
+ return undefined;
1276
1338
  }
1277
- // 处理数组类型的 content
1278
- else if (Array.isArray(content)) {
1279
- for (const block of content) {
1280
- if ((block === null || block === void 0 ? void 0 : block.type) === 'text' && typeof block.text === 'string') {
1281
- if (block.text.trim().startsWith('!!')) {
1282
- return true;
1283
- }
1284
- }
1339
+ const highIqRule = yield this.findHighIqRule(route.id);
1340
+ if (!highIqRule) {
1341
+ if (sessionId && (session === null || session === void 0 ? void 0 : session.highIqMode)) {
1342
+ yield this.dbManager.updateSession(sessionId, {
1343
+ highIqMode: false,
1344
+ highIqRuleId: undefined,
1345
+ lastRequestAt: Date.now(),
1346
+ });
1285
1347
  }
1348
+ console.log('[HIGH-IQ] Inferred high-iq request but no available high-iq rule found');
1349
+ return undefined;
1286
1350
  }
1287
- }
1288
- return false;
1351
+ if (sessionId && (!(session === null || session === void 0 ? void 0 : session.highIqMode) || session.highIqRuleId !== highIqRule.id)) {
1352
+ yield this.dbManager.updateSession(sessionId, {
1353
+ highIqMode: true,
1354
+ highIqRuleId: highIqRule.id,
1355
+ highIqEnabledAt: (session === null || session === void 0 ? void 0 : session.highIqEnabledAt) || Date.now(),
1356
+ lastRequestAt: Date.now(),
1357
+ });
1358
+ console.log(`[HIGH-IQ] Session ${sessionId} inferred ON with rule ${highIqRule.id}`);
1359
+ }
1360
+ return 'high-iq';
1361
+ });
1289
1362
  }
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;
1363
+ extractConversationMessages(body) {
1364
+ if (!body || typeof body !== 'object') {
1365
+ return [];
1298
1366
  }
1299
- // 只检查最后一条用户消息
1300
- for (let i = messages.length - 1; i >= 0; i--) {
1301
- const message = messages[i];
1302
- if ((message === null || message === void 0 ? void 0 : message.role) !== 'user')
1303
- continue;
1304
- const content = message === null || message === void 0 ? void 0 : message.content;
1305
- if (typeof content === 'string') {
1306
- const trimmed = content.trim();
1307
- if (trimmed.startsWith('!x')) {
1308
- return 'off';
1367
+ if (Array.isArray(body.messages)) {
1368
+ return body.messages;
1369
+ }
1370
+ if (typeof body.input === 'string') {
1371
+ return [{ role: 'user', content: body.input }];
1372
+ }
1373
+ if (Array.isArray(body.input)) {
1374
+ const normalized = [];
1375
+ for (const item of body.input) {
1376
+ if (item && typeof item === 'object' && typeof item.role === 'string') {
1377
+ normalized.push(item);
1309
1378
  }
1310
- if (trimmed.startsWith('!!')) {
1311
- return 'on';
1379
+ else if (typeof item === 'string') {
1380
+ normalized.push({ role: 'user', content: item });
1312
1381
  }
1313
1382
  }
1314
- else if (Array.isArray(content)) {
1315
- for (const block of content) {
1316
- if ((block === null || block === void 0 ? void 0 : block.type) === 'text' && typeof block.text === 'string') {
1317
- const trimmed = block.text.trim();
1318
- if (trimmed.startsWith('!x')) {
1319
- return 'off';
1383
+ return normalized;
1384
+ }
1385
+ if (body.input && typeof body.input === 'object' && typeof body.input.role === 'string') {
1386
+ return [body.input];
1387
+ }
1388
+ return [];
1389
+ }
1390
+ analyzeUserMessageForHighIq(message) {
1391
+ let hasHumanText = false;
1392
+ let hasHighIqPrefix = false;
1393
+ const scanText = (text, treatAsHuman) => {
1394
+ const trimmed = text.trim();
1395
+ if (!trimmed) {
1396
+ return;
1397
+ }
1398
+ if (treatAsHuman) {
1399
+ hasHumanText = true;
1400
+ }
1401
+ if (treatAsHuman && trimmed.startsWith('!!')) {
1402
+ hasHighIqPrefix = true;
1403
+ }
1404
+ };
1405
+ const content = message === null || message === void 0 ? void 0 : message.content;
1406
+ if (typeof content === 'string') {
1407
+ scanText(content, true);
1408
+ return { hasHumanText, hasHighIqPrefix };
1409
+ }
1410
+ const blocks = Array.isArray(content) ? content : [content];
1411
+ for (const block of blocks) {
1412
+ if (typeof block === 'string') {
1413
+ scanText(block, true);
1414
+ continue;
1415
+ }
1416
+ if (!block || typeof block !== 'object') {
1417
+ continue;
1418
+ }
1419
+ const type = typeof block.type === 'string' ? block.type : '';
1420
+ const toolGenerated = type === 'tool_result' || type === 'tool' || Boolean(block.tool_use_id || block.tool_call_id);
1421
+ if (typeof block.text === 'string') {
1422
+ scanText(block.text, !toolGenerated);
1423
+ }
1424
+ if (typeof block.content === 'string') {
1425
+ scanText(block.content, !toolGenerated);
1426
+ }
1427
+ if (Array.isArray(block.content)) {
1428
+ for (const nested of block.content) {
1429
+ if (typeof nested === 'string') {
1430
+ scanText(nested, !toolGenerated);
1431
+ continue;
1432
+ }
1433
+ if (nested && typeof nested === 'object') {
1434
+ if (typeof nested.text === 'string') {
1435
+ scanText(nested.text, !toolGenerated);
1320
1436
  }
1321
- if (trimmed.startsWith('!!')) {
1322
- return 'on';
1437
+ if (typeof nested.content === 'string') {
1438
+ scanText(nested.content, !toolGenerated);
1323
1439
  }
1324
1440
  }
1325
1441
  }
1326
1442
  }
1327
- break; // 只检查最后一条用户消息
1328
1443
  }
1329
- return null;
1444
+ return { hasHumanText, hasHighIqPrefix };
1330
1445
  }
1331
- /**
1332
- * 移除用户消息中的高智商命令前缀
1333
- */
1334
- removeHighIqCommand(body) {
1335
- if (!(body === null || body === void 0 ? void 0 : body.messages))
1446
+ removeHighIqPrefix(body) {
1447
+ if (!body || typeof body !== 'object') {
1336
1448
  return body;
1449
+ }
1337
1450
  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();
1451
+ if (typeof processed.input === 'string') {
1452
+ processed.input = processed.input.replace(/^\s*!!\s*/, '');
1453
+ return processed;
1454
+ }
1455
+ if (Array.isArray(processed.input)) {
1456
+ for (let i = processed.input.length - 1; i >= 0; i--) {
1457
+ if (typeof processed.input[i] !== 'string') {
1458
+ continue;
1347
1459
  }
1348
- else if (trimmed.startsWith('!!')) {
1349
- message.content = trimmed.replace(/^!!\s*/, '').trim();
1460
+ const next = processed.input[i].replace(/^\s*!!\s*/, '');
1461
+ if (next !== processed.input[i]) {
1462
+ processed.input[i] = next;
1463
+ return processed;
1350
1464
  }
1351
1465
  }
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
- }
1466
+ }
1467
+ const messages = this.extractConversationMessages(processed);
1468
+ for (let i = messages.length - 1; i >= 0; i--) {
1469
+ const message = messages[i];
1470
+ if ((message === null || message === void 0 ? void 0 : message.role) !== 'user') {
1471
+ continue;
1472
+ }
1473
+ if (this.stripHighIqPrefixInMessage(message)) {
1474
+ break;
1364
1475
  }
1365
- break; // 只处理最后一条用户消息
1366
1476
  }
1367
1477
  return processed;
1368
1478
  }
1479
+ stripHighIqPrefixInMessage(message) {
1480
+ const content = message === null || message === void 0 ? void 0 : message.content;
1481
+ if (typeof content === 'string') {
1482
+ const next = content.replace(/^\s*!!\s*/, '');
1483
+ if (next !== content) {
1484
+ message.content = next;
1485
+ return true;
1486
+ }
1487
+ return false;
1488
+ }
1489
+ if (!Array.isArray(content)) {
1490
+ return false;
1491
+ }
1492
+ for (const block of content) {
1493
+ if (!block || typeof block !== 'object') {
1494
+ continue;
1495
+ }
1496
+ const type = typeof block.type === 'string' ? block.type : '';
1497
+ const toolGenerated = type === 'tool_result' || type === 'tool' || Boolean(block.tool_use_id || block.tool_call_id);
1498
+ if (toolGenerated) {
1499
+ continue;
1500
+ }
1501
+ if (typeof block.text === 'string') {
1502
+ const next = block.text.replace(/^\s*!!\s*/, '');
1503
+ if (next !== block.text) {
1504
+ block.text = next;
1505
+ return true;
1506
+ }
1507
+ }
1508
+ if (typeof block.content === 'string') {
1509
+ const next = block.content.replace(/^\s*!!\s*/, '');
1510
+ if (next !== block.content) {
1511
+ block.content = next;
1512
+ return true;
1513
+ }
1514
+ }
1515
+ }
1516
+ return false;
1517
+ }
1369
1518
  /**
1370
1519
  * 查找可用的高智商规则
1371
1520
  */
@@ -1402,14 +1551,6 @@ class ProxyServer {
1402
1551
  return undefined;
1403
1552
  });
1404
1553
  }
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;
1412
- }
1413
1554
  hasBackgroundSignal(body) {
1414
1555
  var _a, _b, _c;
1415
1556
  // 检测 count tokens 请求:messages 只有一条,role 为 "user",content 为 "count"
@@ -1954,6 +2095,7 @@ class ProxyServer {
1954
2095
  const targetType = route.targetType;
1955
2096
  const failoverEnabled = (options === null || options === void 0 ? void 0 : options.failoverEnabled) === true;
1956
2097
  const forwardedToServiceName = options === null || options === void 0 ? void 0 : options.forwardedToServiceName;
2098
+ const useOriginalConfig = (options === null || options === void 0 ? void 0 : options.useOriginalConfig) === true;
1957
2099
  let requestBody = req.body || {};
1958
2100
  let usageForLog;
1959
2101
  let logged = false;
@@ -2122,6 +2264,7 @@ class ProxyServer {
2122
2264
  vendorId: service.vendorId,
2123
2265
  vendorName: vendor === null || vendor === void 0 ? void 0 : vendor.name,
2124
2266
  requestModel,
2267
+ tags: useOriginalConfig ? ['使用原始配置'] : undefined,
2125
2268
  responseHeaders: responseHeadersForLog,
2126
2269
  responseBody: responseBodyForLog,
2127
2270
  streamChunks: streamChunksForLog,
@@ -2133,6 +2276,7 @@ class ProxyServer {
2133
2276
  const totalTokens = ((usageForLog === null || usageForLog === void 0 ? void 0 : usageForLog.inputTokens) || 0) + ((usageForLog === null || usageForLog === void 0 ? void 0 : usageForLog.outputTokens) || 0) +
2134
2277
  ((usageForLog === null || usageForLog === void 0 ? void 0 : usageForLog.totalTokens) || 0);
2135
2278
  const sessionTitle = this.defaultExtractSessionTitle(req, sessionId);
2279
+ const existingSession = this.dbManager.getSession(sessionId);
2136
2280
  this.dbManager.upsertSession({
2137
2281
  id: sessionId,
2138
2282
  targetType,
@@ -2145,6 +2289,11 @@ class ProxyServer {
2145
2289
  serviceName: service.name,
2146
2290
  model: requestModel || rule.targetModel,
2147
2291
  totalTokens,
2292
+ highIqMode: rule.contentType === 'high-iq' ? true : existingSession === null || existingSession === void 0 ? void 0 : existingSession.highIqMode,
2293
+ highIqRuleId: rule.contentType === 'high-iq' ? rule.id : existingSession === null || existingSession === void 0 ? void 0 : existingSession.highIqRuleId,
2294
+ highIqEnabledAt: rule.contentType === 'high-iq'
2295
+ ? ((existingSession === null || existingSession === void 0 ? void 0 : existingSession.highIqEnabledAt) || Date.now())
2296
+ : existingSession === null || existingSession === void 0 ? void 0 : existingSession.highIqEnabledAt,
2148
2297
  });
2149
2298
  }
2150
2299
  // 更新规则的token使用量(只在成功请求时更新)