@codeproxy/core 0.1.6 → 0.1.7

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/README.md CHANGED
@@ -62,8 +62,11 @@ Creates a `fetch` wrapper that translates Responses API traffic to the configure
62
62
  | `dropImages` | `boolean` | Strip image parts from messages (text-only models) |
63
63
  | `timeoutMs` | `number` | Upstream request timeout |
64
64
  | `onCacheStats` | `(stats) => void` | Receive cache usage stats |
65
+ | `fallbackThoughtSignature` | `string` | Fallback Gemini thought signature for OpenAI-compatible tool call histories |
65
66
  | `fallbackUpstream` | `object` | When `dropImages: true` and the request contains images, automatically route to this upstream instead (e.g. a vision-capable model) |
66
67
 
68
+ Gemini OpenAI-compatible tool calls preserve `thought_signature` through Responses function-call items. When migrating old histories that do not include a returned signature, pass `fallbackThoughtSignature` so the next upstream tool-call request can still include `extra_content.google.thought_signature`.
69
+
67
70
  ### Translators
68
71
 
69
72
  Low-level translators are also available by namespace:
package/README.zh-CN.md CHANGED
@@ -62,8 +62,11 @@ npm install @codeproxy/core
62
62
  | `dropImages` | `boolean` | 从消息中移除图片部分(纯文本模型) |
63
63
  | `timeoutMs` | `number` | 上游请求超时 |
64
64
  | `onCacheStats` | `(stats) => void` | 接收缓存使用统计 |
65
+ | `fallbackThoughtSignature` | `string` | Gemini OpenAI 兼容工具调用历史的兜底 thought signature |
65
66
  | `fallbackUpstream` | `object` | 当 `dropImages: true` 且请求包含图片时,自动切换到该上游(如支持视觉的模型) |
66
67
 
68
+ Gemini OpenAI 兼容工具调用会在 Responses function-call item 中保留 `thought_signature`。迁移旧历史时,如果历史里没有上游返回的 signature,可以传入 `fallbackThoughtSignature`,让下一次上游工具调用请求继续带上 `extra_content.google.thought_signature`。
69
+
67
70
  ### 转换器
68
71
 
69
72
  底层转换器也按命名空间导出:
package/dist/index.cjs CHANGED
@@ -527,9 +527,14 @@ function ensureEndsWithUser(messages) {
527
527
  return [...messages, { role: "user", content: [{ type: "text", text: "Continue." }] }];
528
528
  }
529
529
  function markBlocksForCache(blocks) {
530
+ let count = 0;
530
531
  for (const block of blocks) {
531
532
  if (!block.cache_control) {
532
533
  block.cache_control = { type: "ephemeral" };
534
+ count++;
535
+ if (count >= 3) {
536
+ break;
537
+ }
533
538
  }
534
539
  }
535
540
  return blocks;
@@ -838,6 +843,8 @@ var StreamTranslator = class {
838
843
  *finalize() {
839
844
  const items = [];
840
845
  if (this.textItem) {
846
+ this.textItem.status = "completed";
847
+ this.textItem.content[0].text = this.textBuffer;
841
848
  items.push({ index: this.textItemIndex, item: this.textItem });
842
849
  }
843
850
  for (const block of this.blocks.values()) {
@@ -949,15 +956,22 @@ var StreamTranslator = class {
949
956
  if (btype === "tool_use") {
950
957
  const outputIndex = this.outputCounter++;
951
958
  const callId = block.id ?? makeId("call");
959
+ const initialInput = typeof block.input === "object" && block.input !== null ? jsonStringifySafe(block.input) : "";
960
+ const hasInitialInput = initialInput !== "" && initialInput !== "{}";
952
961
  const item = {
953
962
  id: callId,
954
963
  type: "function_call",
955
964
  status: "in_progress",
956
965
  name: block.name ?? "",
957
- arguments: "",
966
+ arguments: hasInitialInput ? initialInput : "",
958
967
  call_id: callId
959
968
  };
960
- this.blocks.set(index, { type: "tool_use", outputIndex, item, buffer: "" });
969
+ this.blocks.set(index, {
970
+ type: "tool_use",
971
+ outputIndex,
972
+ item,
973
+ buffer: hasInitialInput ? initialInput : ""
974
+ });
961
975
  yield this.makeEvent("response.output_item.added", {
962
976
  response_id: this.responseId,
963
977
  output_index: outputIndex,
@@ -1076,7 +1090,7 @@ function translateRequest2(data, options = {}) {
1076
1090
  continue;
1077
1091
  }
1078
1092
  const rawItem = raw;
1079
- processInputItem(rawItem, messages, options.dropImages);
1093
+ processInputItem(rawItem, messages, options);
1080
1094
  }
1081
1095
  const request = {
1082
1096
  model: data.model,
@@ -1136,7 +1150,7 @@ function buildSystemContent(instructions) {
1136
1150
  }
1137
1151
  return out;
1138
1152
  }
1139
- function processInputItem(item, messages, dropImages) {
1153
+ function processInputItem(item, messages, options) {
1140
1154
  const itemType = String(item.type) || "message";
1141
1155
  const getLastAssistant = () => {
1142
1156
  const last = messages[messages.length - 1];
@@ -1198,7 +1212,7 @@ function processInputItem(item, messages, dropImages) {
1198
1212
  } else if (contentPart.type === "reasoning_text") {
1199
1213
  reasoningContent += String(contentPart.text ?? "");
1200
1214
  } else if (contentPart.type === "input_image" || contentPart.type === "image" || contentPart.type === "image_url") {
1201
- if (dropImages) {
1215
+ if (options.dropImages) {
1202
1216
  continue;
1203
1217
  }
1204
1218
  let url = "";
@@ -1273,7 +1287,7 @@ function processInputItem(item, messages, dropImages) {
1273
1287
  return;
1274
1288
  }
1275
1289
  if (itemType === "function_call" || itemType === "commandExecution" || itemType === "local_shell_call" || itemType === "fileChange" || itemType === "custom_tool_call" || itemType === "web_search_call") {
1276
- processToolCall(item, messages, getLastAssistant);
1290
+ processToolCall(item, messages, getLastAssistant, options.fallbackThoughtSignature);
1277
1291
  return;
1278
1292
  }
1279
1293
  if (itemType === "function_call_output" || itemType === "commandExecutionOutput" || itemType === "fileChangeOutput" || itemType === "custom_tool_call_output") {
@@ -1281,7 +1295,7 @@ function processInputItem(item, messages, dropImages) {
1281
1295
  return;
1282
1296
  }
1283
1297
  }
1284
- function processToolCall(item, messages, getLastAssistant) {
1298
+ function processToolCall(item, messages, getLastAssistant, fallbackThoughtSignature) {
1285
1299
  const callId = String(item.call_id ?? "") || String(item.id ?? "") || makeId("call");
1286
1300
  let name = item.name === void 0 ? void 0 : String(item.name);
1287
1301
  const itemType = item.type === void 0 ? void 0 : String(item.type);
@@ -1327,16 +1341,18 @@ function processToolCall(item, messages, getLastAssistant) {
1327
1341
  if (!amsg.tool_calls) {
1328
1342
  amsg.tool_calls = [];
1329
1343
  }
1330
- amsg.tool_calls.push({
1344
+ const toolCall = {
1331
1345
  id: callId,
1332
1346
  type: "function",
1333
1347
  function: { name, arguments: argsStr }
1334
- });
1335
- const sig = item.thought_signature;
1348
+ };
1349
+ const sig = item.thought_signature ?? fallbackThoughtSignature;
1336
1350
  const thought = item.thought;
1337
1351
  if (typeof sig === "string" && sig) {
1352
+ toolCall.extra_content = { google: { thought_signature: sig } };
1338
1353
  amsg.thought_signature = sig;
1339
1354
  }
1355
+ amsg.tool_calls.push(toolCall);
1340
1356
  if (typeof thought === "string" && thought) {
1341
1357
  amsg.reasoning_content = (amsg.reasoning_content ?? "") + thought;
1342
1358
  }
@@ -1540,6 +1556,10 @@ function mapToolCallToOutput(tc) {
1540
1556
  arguments: args,
1541
1557
  call_id: callId
1542
1558
  };
1559
+ const thoughtSignature = getThoughtSignature(tc);
1560
+ if (thoughtSignature) {
1561
+ item.thought_signature = thoughtSignature;
1562
+ }
1543
1563
  if (SHELL_TOOL_NAMES3.has(name)) {
1544
1564
  item.type = "local_shell_call";
1545
1565
  const parsed = safeJsonParse(args);
@@ -1547,6 +1567,10 @@ function mapToolCallToOutput(tc) {
1547
1567
  }
1548
1568
  return item;
1549
1569
  }
1570
+ function getThoughtSignature(tc) {
1571
+ const sig = tc.extra_content?.google?.thought_signature ?? tc.thought_signature;
1572
+ return typeof sig === "string" && sig ? sig : void 0;
1573
+ }
1550
1574
 
1551
1575
  // src/translate/openai/translateStream.ts
1552
1576
  var SHELL_TOOL_NAMES4 = /* @__PURE__ */ new Set(["shell", "container.exec", "shell_command"]);
@@ -1649,6 +1673,10 @@ var StreamTranslator2 = class {
1649
1673
  });
1650
1674
  }
1651
1675
  const fn = tc.function;
1676
+ const thoughtSignature = getThoughtSignature2(tc);
1677
+ if (thoughtSignature) {
1678
+ state.item.thought_signature = thoughtSignature;
1679
+ }
1652
1680
  if (fn?.name) {
1653
1681
  state.item.name = (state.item.name ?? "") + fn.name;
1654
1682
  }
@@ -1760,6 +1788,10 @@ var StreamTranslator2 = class {
1760
1788
  };
1761
1789
  }
1762
1790
  };
1791
+ function getThoughtSignature2(tc) {
1792
+ const sig = tc.extra_content?.google?.thought_signature ?? tc.thought_signature;
1793
+ return typeof sig === "string" && sig ? sig : void 0;
1794
+ }
1763
1795
 
1764
1796
  // src/fetch.ts
1765
1797
  function createResponsesFetch(options) {
@@ -1767,7 +1799,7 @@ function createResponsesFetch(options) {
1767
1799
  throw new Error("baseUrl is required");
1768
1800
  }
1769
1801
  const rawFormat = options.upstreamFormat;
1770
- const format = rawFormat ? normalizeFormat(rawFormat) : inferFormatFromUrl(options.baseUrl) ?? "openai-chat";
1802
+ const format = rawFormat ? normalizeFormat(rawFormat) : inferFormatFromUrl(options.baseUrl) ?? inferFormatFromModel(options.model) ?? "openai-chat";
1771
1803
  if (!format) {
1772
1804
  throw new Error(
1773
1805
  `Unsupported upstream format: ${options.upstreamFormat}. Use 'anthropic' or 'openai-chat'`
@@ -1938,7 +1970,8 @@ async function handleResponses(request, format, options, baseFetch, incomingHead
1938
1970
  options.baseUrl,
1939
1971
  dropImages,
1940
1972
  options.reasoning_effort,
1941
- options.thinking
1973
+ options.thinking,
1974
+ options.fallbackThoughtSignature
1942
1975
  );
1943
1976
  const upstreamHeaders = buildUpstreamHeaders(format, options, incomingHeaders);
1944
1977
  const upstream = await baseFetch(resolvedUrl, {
@@ -1994,7 +2027,7 @@ function buildRequestMetadata(request, temperature, top_p) {
1994
2027
  metadata: request.metadata ?? {}
1995
2028
  };
1996
2029
  }
1997
- function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reasoning_effort, thinking) {
2030
+ function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reasoning_effort, thinking, fallbackThoughtSignature) {
1998
2031
  if (format === "anthropic") {
1999
2032
  const { request: ar } = translateRequest(request);
2000
2033
  ar.stream = streaming;
@@ -2025,7 +2058,10 @@ function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reas
2025
2058
  requestMetadata: buildRequestMetadata(request, ar.temperature, ar.top_p)
2026
2059
  };
2027
2060
  }
2028
- const { request: cr } = translateRequest2(request, { dropImages });
2061
+ const { request: cr } = translateRequest2(request, {
2062
+ dropImages,
2063
+ fallbackThoughtSignature
2064
+ });
2029
2065
  cr.stream = streaming;
2030
2066
  if (streaming) {
2031
2067
  cr.stream_options = { include_usage: true };