aicodeswitch 3.0.20 → 3.6.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.
@@ -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) {
@@ -158,6 +211,8 @@ class ProxyServer {
158
211
  }
159
212
  // 尝试每个规则,直到成功或全部失败
160
213
  let lastError = null;
214
+ let lastFailedRule = null;
215
+ let lastFailedService = null;
161
216
  for (let index = 0; index < allRules.length; index++) {
162
217
  const rule = allRules[index];
163
218
  const service = this.getServiceById(rule.targetServiceId);
@@ -181,6 +236,8 @@ class ProxyServer {
181
236
  catch (error) {
182
237
  console.error(`Service ${service.name} failed:`, error.message);
183
238
  lastError = error;
239
+ lastFailedRule = rule;
240
+ lastFailedService = service;
184
241
  // 检测是否是 timeout 错误
185
242
  const isTimeout = error.code === 'ECONNABORTED' ||
186
243
  ((_b = error.message) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes('timeout')) ||
@@ -206,6 +263,21 @@ class ProxyServer {
206
263
  }
207
264
  // 所有服务都失败了
208
265
  console.error('All services failed');
266
+ // 如果有失败的服务但都在黑名单中,尝试使用最后一个失败的服务(作为 fallback)
267
+ if (lastFailedRule && lastFailedService) {
268
+ console.log(`All services in blacklist, attempting fallback to last failed service: ${lastFailedService.name}`);
269
+ try {
270
+ yield this.proxyRequest(req, res, route, lastFailedRule, lastFailedService, {
271
+ failoverEnabled: false, // Fallback 模式不启用故障切换
272
+ forwardedToServiceName: undefined,
273
+ });
274
+ return;
275
+ }
276
+ catch (fallbackError) {
277
+ console.error(`Fallback to service ${lastFailedService.name} also failed:`, fallbackError.message);
278
+ lastError = fallbackError;
279
+ }
280
+ }
209
281
  // 记录日志
210
282
  if (((_d = this.config) === null || _d === void 0 ? void 0 : _d.enableLogging) !== false && SUPPORTED_TARGETS.some(target => req.path.startsWith(`/${target}/`))) {
211
283
  yield this.dbManager.addLog({
@@ -322,6 +394,58 @@ class ProxyServer {
322
394
  if (!route) {
323
395
  return res.status(404).json({ error: `No active route found for target type: ${targetType}` });
324
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
+ }
325
449
  // 检查是否启用故障切换
326
450
  const enableFailover = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.enableFailover) !== false; // 默认为 true
327
451
  if (!enableFailover) {
@@ -344,6 +468,8 @@ class ProxyServer {
344
468
  }
345
469
  // 尝试每个规则,直到成功或全部失败
346
470
  let lastError = null;
471
+ let lastFailedRule = null;
472
+ let lastFailedService = null;
347
473
  for (let index = 0; index < allRules.length; index++) {
348
474
  const rule = allRules[index];
349
475
  const service = this.getServiceById(rule.targetServiceId);
@@ -367,6 +493,8 @@ class ProxyServer {
367
493
  catch (error) {
368
494
  console.error(`Service ${service.name} failed:`, error.message);
369
495
  lastError = error;
496
+ lastFailedRule = rule;
497
+ lastFailedService = service;
370
498
  // 检测是否是 timeout 错误
371
499
  const isTimeout = error.code === 'ECONNABORTED' ||
372
500
  ((_b = error.message) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes('timeout')) ||
@@ -392,6 +520,21 @@ class ProxyServer {
392
520
  }
393
521
  // 所有服务都失败了
394
522
  console.error('All services failed');
523
+ // 如果有失败的服务但都在黑名单中,尝试使用最后一个失败的服务(作为 fallback)
524
+ if (lastFailedRule && lastFailedService) {
525
+ console.log(`All services in blacklist, attempting fallback to last failed service: ${lastFailedService.name}`);
526
+ try {
527
+ yield this.proxyRequest(req, res, route, lastFailedRule, lastFailedService, {
528
+ failoverEnabled: false, // Fallback 模式不启用故障切换
529
+ forwardedToServiceName: undefined,
530
+ });
531
+ return;
532
+ }
533
+ catch (fallbackError) {
534
+ console.error(`Fallback to service ${lastFailedService.name} also failed:`, fallbackError.message);
535
+ lastError = fallbackError;
536
+ }
537
+ }
395
538
  // 记录日志
396
539
  if (((_d = this.config) === null || _d === void 0 ? void 0 : _d.enableLogging) !== false && SUPPORTED_TARGETS.some(target => req.path.startsWith(`/${target}/`))) {
397
540
  yield this.dbManager.addLog({
@@ -733,6 +876,24 @@ class ProxyServer {
733
876
  return undefined;
734
877
  const body = req.body;
735
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
+ }
736
897
  // 1. 首先查找 model-mapping 类型的规则,按 sortOrder 降序匹配
737
898
  if (requestModel) {
738
899
  const modelMappingRules = rules.filter(rule => rule.contentType === 'model-mapping' &&
@@ -985,6 +1146,10 @@ class ProxyServer {
985
1146
  type: 'thinking',
986
1147
  match: (_req, body) => this.hasThinkingSignal(body),
987
1148
  },
1149
+ {
1150
+ type: 'high-iq',
1151
+ match: (_req, body) => this.hasHighIqSignal(body),
1152
+ },
988
1153
  {
989
1154
  type: 'long-context',
990
1155
  match: (_req, body) => this.hasLongContextSignal(body),
@@ -1049,6 +1214,10 @@ class ProxyServer {
1049
1214
  bg: 'background',
1050
1215
  thinking: 'thinking',
1051
1216
  reasoning: 'thinking',
1217
+ 'high-iq': 'high-iq',
1218
+ high_iq: 'high-iq',
1219
+ highiq: 'high-iq',
1220
+ smart: 'high-iq',
1052
1221
  'long-context': 'long-context',
1053
1222
  long_context: 'long-context',
1054
1223
  long: 'long-context',
@@ -1090,6 +1259,157 @@ class ProxyServer {
1090
1259
  ((_a = body === null || body === void 0 ? void 0 : body.reasoning) === null || _a === void 0 ? void 0 : _a.effort) ||
1091
1260
  ((_b = body === null || body === void 0 ? void 0 : body.reasoning) === null || _b === void 0 ? void 0 : _b.enabled));
1092
1261
  }
1262
+ 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')
1269
+ 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;
1275
+ }
1276
+ }
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
+ }
1285
+ }
1286
+ }
1287
+ }
1288
+ return false;
1289
+ }
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;
1298
+ }
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';
1309
+ }
1310
+ if (trimmed.startsWith('!!')) {
1311
+ return 'on';
1312
+ }
1313
+ }
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';
1320
+ }
1321
+ if (trimmed.startsWith('!!')) {
1322
+ return 'on';
1323
+ }
1324
+ }
1325
+ }
1326
+ }
1327
+ break; // 只检查最后一条用户消息
1328
+ }
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;
1412
+ }
1093
1413
  hasBackgroundSignal(body) {
1094
1414
  var _a, _b, _c;
1095
1415
  // 检测 count tokens 请求:messages 只有一条,role 为 "user",content 为 "count"
@@ -1211,15 +1531,23 @@ class ProxyServer {
1211
1531
  }
1212
1532
  /** 判断是否为 Claude 相关类型(使用 x-api-key 认证) */
1213
1533
  isClaudeSource(sourceType) {
1214
- return sourceType === 'claude-chat' || sourceType === 'claude-code';
1534
+ // 向下兼容:支持旧类型 'claude-code'
1535
+ const normalized = sourceType === 'claude-code' ? 'claude' : sourceType;
1536
+ return normalized === 'claude-chat' || normalized === 'claude';
1215
1537
  }
1216
1538
  isOpenAIChatSource(sourceType) {
1217
- return sourceType === 'openai-chat' || sourceType === 'openai-responses' || sourceType === 'deepseek-reasoning-chat';
1539
+ // 向下兼容:支持旧类型 'openai-responses'
1540
+ const normalized = sourceType === 'openai-responses' ? 'openai' : sourceType;
1541
+ return normalized === 'openai-chat' || normalized === 'openai' || normalized === 'deepseek-reasoning-chat';
1218
1542
  }
1219
1543
  /** 判断是否为 Gemini 类型 */
1220
1544
  isGeminiSource(sourceType) {
1221
1545
  return sourceType === 'gemini';
1222
1546
  }
1547
+ /** 判断是否为 Gemini Chat 类型 */
1548
+ isGeminiChatSource(sourceType) {
1549
+ return sourceType === 'gemini-chat';
1550
+ }
1223
1551
  isChatType(sourceType) {
1224
1552
  return sourceType.endsWith('-chat') || sourceType === 'gemini';
1225
1553
  }
@@ -1341,8 +1669,8 @@ class ProxyServer {
1341
1669
  // 向下兼容:检测旧数据的 'auto' 值
1342
1670
  // TODO: 删除
1343
1671
  const isAuto = authType === 'auto';
1344
- // 使用 x-goog-api-key 认证(适用于 Google Gemini API)
1345
- if (authType === types_1.AuthType.G_API_KEY || (isAuto && this.isGeminiSource(sourceType))) {
1672
+ // 使用 x-goog-api-key 认证(适用于 Google Gemini API 和 Gemini Chat
1673
+ if (authType === types_1.AuthType.G_API_KEY || (isAuto && (this.isGeminiSource(sourceType) || this.isGeminiChatSource(sourceType)))) {
1346
1674
  headers['x-goog-api-key'] = service.apiKey;
1347
1675
  }
1348
1676
  // 使用 x-api-key 认证(适用于 claude-chat, claude-code 及某些需要 x-api-key 的 openai-chat 兼容 API)
@@ -1604,7 +1932,9 @@ class ProxyServer {
1604
1932
  var _a, _b, _c, _d, _e;
1605
1933
  res.locals.skipLog = true;
1606
1934
  const startTime = Date.now();
1607
- const sourceType = (service.sourceType || 'openai-chat');
1935
+ const rawSourceType = service.sourceType || 'openai-chat';
1936
+ // 标准化 sourceType,将旧类型转换为新类型(向下兼容)
1937
+ const sourceType = (0, type_migration_1.normalizeSourceType)(rawSourceType);
1608
1938
  const targetType = route.targetType;
1609
1939
  const failoverEnabled = (options === null || options === void 0 ? void 0 : options.failoverEnabled) === true;
1610
1940
  const forwardedToServiceName = options === null || options === void 0 ? void 0 : options.forwardedToServiceName;
@@ -1907,7 +2237,7 @@ class ProxyServer {
1907
2237
  else if (this.isOpenAIChatSource(sourceType)) {
1908
2238
  requestBody = (0, claude_openai_1.transformClaudeRequestToOpenAIChat)(requestBody, rule.targetModel);
1909
2239
  }
1910
- else if (this.isGeminiSource(sourceType)) {
2240
+ else if (this.isGeminiSource(sourceType) || this.isGeminiChatSource(sourceType)) {
1911
2241
  requestBody = (0, gemini_1.transformClaudeRequestToGemini)(requestBody);
1912
2242
  }
1913
2243
  else {
@@ -1923,7 +2253,7 @@ class ProxyServer {
1923
2253
  else if (this.isClaudeSource(sourceType)) {
1924
2254
  requestBody = (0, claude_openai_1.transformClaudeRequestToOpenAIChat)(requestBody, rule.targetModel);
1925
2255
  }
1926
- else if (this.isGeminiSource(sourceType)) {
2256
+ else if (this.isGeminiSource(sourceType) || this.isGeminiChatSource(sourceType)) {
1927
2257
  requestBody = (0, gemini_1.transformOpenAIChatRequestToGemini)(requestBody);
1928
2258
  }
1929
2259
  else {
@@ -1952,7 +2282,8 @@ class ProxyServer {
1952
2282
  const model = requestBody.model || rule.targetModel || 'gemini-pro';
1953
2283
  upstreamUrl = this.buildGeminiUrl(service.apiUrl, model, streamRequested);
1954
2284
  }
1955
- else if (this.isChatType(sourceType)) {
2285
+ else if (this.isChatType(sourceType) || this.isGeminiChatSource(sourceType)) {
2286
+ // Chat 类型(包括 gemini-chat)直接使用用户配置的完整 URL
1956
2287
  upstreamUrl = service.apiUrl;
1957
2288
  }
1958
2289
  else {
@@ -2235,8 +2566,8 @@ class ProxyServer {
2235
2566
  }));
2236
2567
  return;
2237
2568
  }
2238
- // Gemini -> Claude Code 流式转换
2239
- if (targetType === 'claude-code' && this.isGeminiSource(sourceType)) {
2569
+ // Gemini / Gemini Chat -> Claude Code 流式转换
2570
+ if (targetType === 'claude-code' && (this.isGeminiSource(sourceType) || this.isGeminiChatSource(sourceType))) {
2240
2571
  res.setHeader('Content-Type', 'text/event-stream');
2241
2572
  res.setHeader('Cache-Control', 'no-cache');
2242
2573
  res.setHeader('Connection', 'keep-alive');
@@ -2331,8 +2662,8 @@ class ProxyServer {
2331
2662
  }));
2332
2663
  return;
2333
2664
  }
2334
- // Gemini -> Codex 流式转换
2335
- if (targetType === 'codex' && this.isGeminiSource(sourceType)) {
2665
+ // Gemini / Gemini Chat -> Codex 流式转换
2666
+ if (targetType === 'codex' && (this.isGeminiSource(sourceType) || this.isGeminiChatSource(sourceType))) {
2336
2667
  res.setHeader('Content-Type', 'text/event-stream');
2337
2668
  res.setHeader('Cache-Control', 'no-cache');
2338
2669
  res.setHeader('Connection', 'keep-alive');
@@ -2499,7 +2830,7 @@ class ProxyServer {
2499
2830
  responseBodyForLog = JSON.stringify(converted);
2500
2831
  res.status(response.status).json(converted);
2501
2832
  }
2502
- else if (targetType === 'claude-code' && this.isGeminiSource(sourceType)) {
2833
+ else if (targetType === 'claude-code' && (this.isGeminiSource(sourceType) || this.isGeminiChatSource(sourceType))) {
2503
2834
  const converted = (0, gemini_1.transformGeminiResponseToClaude)(responseData, rule.targetModel);
2504
2835
  usageForLog = (0, gemini_1.extractTokenUsageFromGeminiUsage)(responseData === null || responseData === void 0 ? void 0 : responseData.usageMetadata);
2505
2836
  responseBodyForLog = JSON.stringify(converted);
@@ -2511,7 +2842,7 @@ class ProxyServer {
2511
2842
  responseBodyForLog = JSON.stringify(converted);
2512
2843
  res.status(response.status).json(converted);
2513
2844
  }
2514
- else if (targetType === 'codex' && this.isGeminiSource(sourceType)) {
2845
+ else if (targetType === 'codex' && (this.isGeminiSource(sourceType) || this.isGeminiChatSource(sourceType))) {
2515
2846
  const converted = (0, gemini_1.transformGeminiResponseToOpenAIChat)(responseData, rule.targetModel);
2516
2847
  usageForLog = (0, gemini_1.extractTokenUsageFromGeminiUsage)(responseData === null || responseData === void 0 ? void 0 : responseData.usageMetadata);
2517
2848
  responseBodyForLog = JSON.stringify(converted);
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrateSourceType = migrateSourceType;
4
+ exports.downgradeSourceType = downgradeSourceType;
5
+ exports.isLegacySourceType = isLegacySourceType;
6
+ exports.normalizeSourceType = normalizeSourceType;
7
+ /**
8
+ * 旧类型 → 新类型映射表
9
+ */
10
+ const SOURCE_TYPE_MIGRATION_MAP = {
11
+ 'openai-chat': 'openai-chat',
12
+ 'openai-responses': 'openai', // 重命名
13
+ 'claude-chat': 'claude-chat',
14
+ 'claude-code': 'claude', // 重命名
15
+ 'deepseek-reasoning-chat': 'deepseek-reasoning-chat',
16
+ 'gemini': 'gemini',
17
+ 'gemini-chat': 'gemini-chat',
18
+ };
19
+ /**
20
+ * 新类型 → 旧类型映射表
21
+ * 用于向下兼容导出
22
+ */
23
+ const SOURCE_TYPE_REVERSE_MAP = {
24
+ 'openai-chat': 'openai-chat',
25
+ 'openai': 'openai-responses',
26
+ 'claude-chat': 'claude-chat',
27
+ 'claude': 'claude-code',
28
+ 'deepseek-reasoning-chat': 'deepseek-reasoning-chat',
29
+ 'gemini': 'gemini',
30
+ 'gemini-chat': 'gemini-chat',
31
+ };
32
+ /**
33
+ * 将旧类型转换为新类型
34
+ * @param legacyType 旧的数据源类型
35
+ * @returns 新的数据源类型
36
+ */
37
+ function migrateSourceType(legacyType) {
38
+ return SOURCE_TYPE_MIGRATION_MAP[legacyType];
39
+ }
40
+ /**
41
+ * 将新类型转换为旧类型
42
+ * 用于向下兼容导出
43
+ * @param newType 新的数据源类型
44
+ * @returns 旧的数据源类型
45
+ */
46
+ function downgradeSourceType(newType) {
47
+ return SOURCE_TYPE_REVERSE_MAP[newType];
48
+ }
49
+ /**
50
+ * 检查是否为旧类型
51
+ * @param type 类型字符串
52
+ * @returns 是否为旧类型
53
+ */
54
+ function isLegacySourceType(type) {
55
+ return type === 'openai-responses' || type === 'claude-code';
56
+ }
57
+ /**
58
+ * 标准化类型
59
+ * 自动处理新旧类型,将旧类型转换为新类型,新类型保持不变
60
+ * @param type 类型字符串(可能是旧类型或新类型)
61
+ * @returns 标准化后的新类型
62
+ */
63
+ function normalizeSourceType(type) {
64
+ if (isLegacySourceType(type)) {
65
+ return migrateSourceType(type);
66
+ }
67
+ return type;
68
+ }