aicodeswitch 1.5.0 → 1.6.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.
@@ -702,6 +702,7 @@ class ProxyServer {
702
702
  return sourceType === 'openai-responses';
703
703
  }
704
704
  applyModelOverride(body, rule) {
705
+ // 如果 targetModel 为空或不存在,保留原始 model(透传)
705
706
  if (!rule.targetModel)
706
707
  return body;
707
708
  if (body && typeof body === 'object') {
@@ -813,6 +814,49 @@ class ProxyServer {
813
814
  }
814
815
  return undefined;
815
816
  }
817
+ /**
818
+ * 根据源工具类型和目标API类型,映射请求路径
819
+ * @param sourceTool 源工具类型 (claude-code 或 codex)
820
+ * @param targetSourceType 目标API的数据格式类型
821
+ * @param originalPath 原始请求路径(已去除工具前缀)
822
+ * @returns 映射后的目标路径
823
+ */
824
+ mapRequestPath(sourceTool, targetSourceType, originalPath) {
825
+ // Claude Code 发起的请求
826
+ if (sourceTool === 'claude-code') {
827
+ // Claude Code 默认使用 Claude API 格式
828
+ if (this.isClaudeSource(targetSourceType)) {
829
+ // Claude → Claude: 直接透传路径
830
+ return originalPath;
831
+ }
832
+ else if (this.isOpenAIChatSource(targetSourceType)) {
833
+ // Claude → OpenAI Chat: /v1/messages → /v1/chat/completions
834
+ return originalPath.replace(/\/v1\/messages\b/, '/v1/chat/completions');
835
+ }
836
+ else if (this.isOpenAIResponsesSource(targetSourceType)) {
837
+ // Claude → OpenAI Responses: /v1/messages → /v1/responses/completions
838
+ return originalPath.replace(/\/v1\/messages\b/, '/v1/responses/completions');
839
+ }
840
+ }
841
+ // Codex 发起的请求
842
+ if (sourceTool === 'codex') {
843
+ // Codex 默认使用 OpenAI Responses API 格式
844
+ if (this.isOpenAIResponsesSource(targetSourceType)) {
845
+ // OpenAI Responses → OpenAI Responses: 直接透传路径
846
+ return originalPath;
847
+ }
848
+ else if (this.isOpenAIChatSource(targetSourceType)) {
849
+ // OpenAI Responses → OpenAI Chat: /v1/responses/completions → /v1/chat/completions
850
+ return originalPath.replace(/\/v1\/responses\/completions\b/, '/v1/chat/completions');
851
+ }
852
+ else if (this.isClaudeSource(targetSourceType)) {
853
+ // OpenAI Responses → Claude: /v1/responses/completions → /v1/messages
854
+ return originalPath.replace(/\/v1\/responses\/completions\b/, '/v1/messages');
855
+ }
856
+ }
857
+ // 默认:直接返回原始路径
858
+ return originalPath;
859
+ }
816
860
  proxyRequest(req, res, route, rule, service) {
817
861
  return __awaiter(this, void 0, void 0, function* () {
818
862
  var _a;
@@ -827,6 +871,7 @@ class ProxyServer {
827
871
  let responseHeadersForLog;
828
872
  let responseBodyForLog;
829
873
  let streamChunksForLog;
874
+ let upstreamRequestForLog;
830
875
  const finalizeLog = (statusCode, error) => __awaiter(this, void 0, void 0, function* () {
831
876
  var _a, _b;
832
877
  if (logged || !((_a = this.config) === null || _a === void 0 ? void 0 : _a.enableLogging))
@@ -859,6 +904,7 @@ class ProxyServer {
859
904
  responseHeaders: responseHeadersForLog,
860
905
  responseBody: responseBodyForLog,
861
906
  streamChunks: streamChunksForLog,
907
+ upstreamRequest: upstreamRequestForLog,
862
908
  });
863
909
  });
864
910
  try {
@@ -903,9 +949,11 @@ class ProxyServer {
903
949
  else if (route.targetType === 'codex' && req.path.startsWith('/codex')) {
904
950
  pathToAppend = req.path.slice('/codex'.length);
905
951
  }
952
+ // 根据源工具类型和目标API类型,映射请求路径
953
+ const mappedPath = this.mapRequestPath(route.targetType, sourceType, pathToAppend);
906
954
  const config = {
907
955
  method: req.method,
908
- url: `${service.apiUrl}${pathToAppend}`,
956
+ url: `${service.apiUrl}${mappedPath}`,
909
957
  headers: this.buildUpstreamHeaders(req, service, sourceType, streamRequested),
910
958
  timeout: service.timeout || 30000,
911
959
  validateStatus: () => true,
@@ -917,6 +965,11 @@ class ProxyServer {
917
965
  if (['POST', 'PUT', 'PATCH'].includes(req.method.toUpperCase())) {
918
966
  config.data = requestBody;
919
967
  }
968
+ // 记录实际发出的请求信息作为日志的一部分
969
+ upstreamRequestForLog = {
970
+ url: `${service.apiUrl}${mappedPath}`,
971
+ model: (requestBody === null || requestBody === void 0 ? void 0 : requestBody.model) || '',
972
+ };
920
973
  const response = yield (0, axios_1.default)(config);
921
974
  const responseHeaders = response.headers || {};
922
975
  const contentType = typeof responseHeaders['content-type'] === 'string' ? responseHeaders['content-type'] : '';