@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 +3 -0
- package/README.zh-CN.md +3 -0
- package/dist/index.cjs +50 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +50 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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, {
|
|
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
|
|
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,
|
|
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
|
-
|
|
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, {
|
|
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 };
|