@codeproxy/core 0.1.9 → 0.1.11
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/dist/index.cjs +246 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -48
- package/dist/index.d.ts +56 -48
- package/dist/index.js +246 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1414,6 +1414,32 @@ function mapTools2(tools) {
|
|
|
1414
1414
|
});
|
|
1415
1415
|
continue;
|
|
1416
1416
|
}
|
|
1417
|
+
if (tt === "namespace") {
|
|
1418
|
+
const ns = tool.name;
|
|
1419
|
+
const nested = tool.tools;
|
|
1420
|
+
if (ns && Array.isArray(nested)) {
|
|
1421
|
+
for (const sub of nested) {
|
|
1422
|
+
if (!sub || typeof sub !== "object" || sub.type !== "function") {
|
|
1423
|
+
continue;
|
|
1424
|
+
}
|
|
1425
|
+
const subName = sub.name;
|
|
1426
|
+
if (!subName) {
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
const params = sub.parameters ?? { type: "object" };
|
|
1430
|
+
out.push({
|
|
1431
|
+
type: "function",
|
|
1432
|
+
function: {
|
|
1433
|
+
name: `${ns}.${subName}`,
|
|
1434
|
+
description: sub.description ?? "",
|
|
1435
|
+
// eslint-disable-next-line no-restricted-syntax -- schema is Record<string,unknown> by OpenAPI convention
|
|
1436
|
+
parameters: params
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1417
1443
|
}
|
|
1418
1444
|
return out;
|
|
1419
1445
|
}
|
|
@@ -1492,6 +1518,36 @@ function repairToolMessageOrder(messages) {
|
|
|
1492
1518
|
|
|
1493
1519
|
// src/translate/openai/translateResponse.ts
|
|
1494
1520
|
var SHELL_TOOL_NAMES3 = /* @__PURE__ */ new Set(["shell", "container.exec", "shell_command"]);
|
|
1521
|
+
function buildShortNameToNamespace(tools) {
|
|
1522
|
+
const map = /* @__PURE__ */ new Map();
|
|
1523
|
+
for (const tool of tools) {
|
|
1524
|
+
if (!tool || typeof tool !== "object") {
|
|
1525
|
+
continue;
|
|
1526
|
+
}
|
|
1527
|
+
const entry = tool;
|
|
1528
|
+
const fn = entry.function;
|
|
1529
|
+
const flatName = typeof fn?.name === "string" ? fn.name : typeof entry.name === "string" ? entry.name : "";
|
|
1530
|
+
const dotIdx = flatName.indexOf(".");
|
|
1531
|
+
if (dotIdx !== -1) {
|
|
1532
|
+
map.set(flatName.slice(dotIdx + 1), flatName.slice(0, dotIdx));
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
if (entry.type === "namespace" && typeof entry.name === "string" && Array.isArray(entry.tools)) {
|
|
1536
|
+
const ns = entry.name;
|
|
1537
|
+
for (const sub of entry.tools) {
|
|
1538
|
+
if (!sub || typeof sub !== "object") {
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
const subEntry = sub;
|
|
1542
|
+
const subName = typeof subEntry.name === "string" ? subEntry.name : "";
|
|
1543
|
+
if (subName) {
|
|
1544
|
+
map.set(subName, ns);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return map;
|
|
1550
|
+
}
|
|
1495
1551
|
function translateResponse2(body, options = {}) {
|
|
1496
1552
|
const createdAt = options.createdAt ?? body.created ?? Math.floor(Date.now() / 1e3);
|
|
1497
1553
|
const id = options.responseId ?? body.id ?? makeId("resp");
|
|
@@ -1499,9 +1555,10 @@ function translateResponse2(body, options = {}) {
|
|
|
1499
1555
|
const choice = body.choices?.[0];
|
|
1500
1556
|
const message = choice?.message;
|
|
1501
1557
|
const output = [];
|
|
1558
|
+
const shortNameToNs = buildShortNameToNamespace(options.requestTools ?? []);
|
|
1502
1559
|
if (message?.tool_calls?.length) {
|
|
1503
1560
|
for (const tc of message.tool_calls) {
|
|
1504
|
-
const item = mapToolCallToOutput(tc);
|
|
1561
|
+
const item = mapToolCallToOutput(tc, shortNameToNs);
|
|
1505
1562
|
if (item) {
|
|
1506
1563
|
output.push(item);
|
|
1507
1564
|
}
|
|
@@ -1538,7 +1595,7 @@ function translateResponse2(body, options = {}) {
|
|
|
1538
1595
|
}
|
|
1539
1596
|
};
|
|
1540
1597
|
}
|
|
1541
|
-
function mapToolCallToOutput(tc) {
|
|
1598
|
+
function mapToolCallToOutput(tc, shortNameToNs) {
|
|
1542
1599
|
const name = tc.function?.name;
|
|
1543
1600
|
if (!name) {
|
|
1544
1601
|
return void 0;
|
|
@@ -1548,11 +1605,23 @@ function mapToolCallToOutput(tc) {
|
|
|
1548
1605
|
if (typeof args !== "string") {
|
|
1549
1606
|
args = jsonStringifySafe(args ?? {});
|
|
1550
1607
|
}
|
|
1608
|
+
let resolvedName = name;
|
|
1609
|
+
let resolvedNamespace;
|
|
1610
|
+
if (!SHELL_TOOL_NAMES3.has(name)) {
|
|
1611
|
+
const dotIdx = name.indexOf(".");
|
|
1612
|
+
if (dotIdx !== -1) {
|
|
1613
|
+
resolvedNamespace = name.slice(0, dotIdx);
|
|
1614
|
+
resolvedName = name.slice(dotIdx + 1);
|
|
1615
|
+
} else {
|
|
1616
|
+
resolvedNamespace = shortNameToNs?.get(name);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1551
1619
|
const item = {
|
|
1552
1620
|
id: callId,
|
|
1553
1621
|
type: "function_call",
|
|
1554
1622
|
status: "completed",
|
|
1555
|
-
name,
|
|
1623
|
+
name: resolvedName,
|
|
1624
|
+
...resolvedNamespace ? { namespace: resolvedNamespace } : {},
|
|
1556
1625
|
arguments: args,
|
|
1557
1626
|
call_id: callId
|
|
1558
1627
|
};
|
|
@@ -1560,7 +1629,7 @@ function mapToolCallToOutput(tc) {
|
|
|
1560
1629
|
if (thoughtSignature) {
|
|
1561
1630
|
item.thought_signature = thoughtSignature;
|
|
1562
1631
|
}
|
|
1563
|
-
if (SHELL_TOOL_NAMES3.has(
|
|
1632
|
+
if (SHELL_TOOL_NAMES3.has(resolvedName)) {
|
|
1564
1633
|
item.type = "local_shell_call";
|
|
1565
1634
|
const parsed = safeJsonParse(args);
|
|
1566
1635
|
item.action = { type: "exec", command: parsed?.command ?? [] };
|
|
@@ -1574,6 +1643,36 @@ function getThoughtSignature(tc) {
|
|
|
1574
1643
|
|
|
1575
1644
|
// src/translate/openai/translateStream.ts
|
|
1576
1645
|
var SHELL_TOOL_NAMES4 = /* @__PURE__ */ new Set(["shell", "container.exec", "shell_command"]);
|
|
1646
|
+
function buildShortNameToNamespace2(tools) {
|
|
1647
|
+
const map = /* @__PURE__ */ new Map();
|
|
1648
|
+
for (const tool of tools) {
|
|
1649
|
+
if (!tool || typeof tool !== "object") {
|
|
1650
|
+
continue;
|
|
1651
|
+
}
|
|
1652
|
+
const entry = tool;
|
|
1653
|
+
const fn = entry.function;
|
|
1654
|
+
const flatName = typeof fn?.name === "string" ? fn.name : typeof entry.name === "string" ? entry.name : "";
|
|
1655
|
+
const dotIdx = flatName.indexOf(".");
|
|
1656
|
+
if (dotIdx !== -1) {
|
|
1657
|
+
map.set(flatName.slice(dotIdx + 1), flatName.slice(0, dotIdx));
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
if (entry.type === "namespace" && typeof entry.name === "string" && Array.isArray(entry.tools)) {
|
|
1661
|
+
const ns = entry.name;
|
|
1662
|
+
for (const sub of entry.tools) {
|
|
1663
|
+
if (!sub || typeof sub !== "object") {
|
|
1664
|
+
continue;
|
|
1665
|
+
}
|
|
1666
|
+
const subEntry = sub;
|
|
1667
|
+
const subName = typeof subEntry.name === "string" ? subEntry.name : "";
|
|
1668
|
+
if (subName) {
|
|
1669
|
+
map.set(subName, ns);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
return map;
|
|
1675
|
+
}
|
|
1577
1676
|
async function* translateStream2(stream, options = {}) {
|
|
1578
1677
|
const translator = new StreamTranslator2(options);
|
|
1579
1678
|
yield translator.createInitialEvent();
|
|
@@ -1613,6 +1712,10 @@ var StreamTranslator2 = class {
|
|
|
1613
1712
|
__publicField(this, "textItemIndex", -1);
|
|
1614
1713
|
__publicField(this, "textBuffer", "");
|
|
1615
1714
|
__publicField(this, "toolCalls", /* @__PURE__ */ new Map());
|
|
1715
|
+
// shortName → namespace reverse map built from flattened namespace tools in
|
|
1716
|
+
// the translated request. Used to restore the namespace when an upstream
|
|
1717
|
+
// (e.g. DeepSeek) omits the "namespace." prefix in its tool-call response.
|
|
1718
|
+
__publicField(this, "shortNameToNamespace");
|
|
1616
1719
|
__publicField(this, "inputTokens", 0);
|
|
1617
1720
|
__publicField(this, "outputTokens", 0);
|
|
1618
1721
|
__publicField(this, "cachedTokens", 0);
|
|
@@ -1620,6 +1723,7 @@ var StreamTranslator2 = class {
|
|
|
1620
1723
|
this.responseId = options.responseId ?? makeId("resp");
|
|
1621
1724
|
this.createdAt = options.createdAt ?? Math.floor(Date.now() / 1e3);
|
|
1622
1725
|
this.metadata = options.requestMetadata ?? {};
|
|
1726
|
+
this.shortNameToNamespace = buildShortNameToNamespace2(this.metadata.tools ?? []);
|
|
1623
1727
|
}
|
|
1624
1728
|
createInitialEvent() {
|
|
1625
1729
|
const toolsArr = this.metadata.tools ?? [];
|
|
@@ -1741,6 +1845,18 @@ var StreamTranslator2 = class {
|
|
|
1741
1845
|
for (const state of this.toolCalls.values()) {
|
|
1742
1846
|
const item = state.item;
|
|
1743
1847
|
item.status = "completed";
|
|
1848
|
+
if (item.name && !SHELL_TOOL_NAMES4.has(item.name)) {
|
|
1849
|
+
const dotIdx = item.name.indexOf(".");
|
|
1850
|
+
if (dotIdx !== -1) {
|
|
1851
|
+
item.namespace = item.name.slice(0, dotIdx);
|
|
1852
|
+
item.name = item.name.slice(dotIdx + 1);
|
|
1853
|
+
} else {
|
|
1854
|
+
const ns = this.shortNameToNamespace.get(item.name);
|
|
1855
|
+
if (ns) {
|
|
1856
|
+
item.namespace = ns;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1744
1860
|
if (item.name && SHELL_TOOL_NAMES4.has(item.name)) {
|
|
1745
1861
|
item.type = "local_shell_call";
|
|
1746
1862
|
const parsed = safeJsonParse(item.arguments ?? "");
|
|
@@ -1802,6 +1918,109 @@ function getThoughtSignature2(tc) {
|
|
|
1802
1918
|
return typeof sig === "string" && sig ? sig : void 0;
|
|
1803
1919
|
}
|
|
1804
1920
|
|
|
1921
|
+
// src/tool-name-sanitizer.ts
|
|
1922
|
+
function sanitizeUpstreamToolNames(body) {
|
|
1923
|
+
const map = /* @__PURE__ */ new Map();
|
|
1924
|
+
const tools = body.tools;
|
|
1925
|
+
if (!Array.isArray(tools)) {
|
|
1926
|
+
return map;
|
|
1927
|
+
}
|
|
1928
|
+
for (const tool of tools) {
|
|
1929
|
+
if (!tool || typeof tool !== "object") {
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
const toolRecord = tool;
|
|
1933
|
+
const fn = toolRecord.function;
|
|
1934
|
+
if (!fn || typeof fn !== "object") {
|
|
1935
|
+
continue;
|
|
1936
|
+
}
|
|
1937
|
+
const fnRecord = fn;
|
|
1938
|
+
const original = fnRecord.name;
|
|
1939
|
+
if (typeof original !== "string") {
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
const sanitized = original.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1943
|
+
if (sanitized !== original) {
|
|
1944
|
+
map.set(sanitized, original);
|
|
1945
|
+
fnRecord.name = sanitized;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return map;
|
|
1949
|
+
}
|
|
1950
|
+
function restoreToolNamesInChatResponse(body, map) {
|
|
1951
|
+
if (map.size === 0) {
|
|
1952
|
+
return body;
|
|
1953
|
+
}
|
|
1954
|
+
const choices = body.choices;
|
|
1955
|
+
if (!Array.isArray(choices)) {
|
|
1956
|
+
return body;
|
|
1957
|
+
}
|
|
1958
|
+
for (const choice of choices) {
|
|
1959
|
+
if (!choice || typeof choice !== "object") {
|
|
1960
|
+
continue;
|
|
1961
|
+
}
|
|
1962
|
+
const choiceRecord = choice;
|
|
1963
|
+
const message = choiceRecord.message;
|
|
1964
|
+
if (!message || typeof message !== "object") {
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
const msg = message;
|
|
1968
|
+
const toolCalls = msg.tool_calls;
|
|
1969
|
+
if (!Array.isArray(toolCalls)) {
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
for (const tc of toolCalls) {
|
|
1973
|
+
if (!tc || typeof tc !== "object") {
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
const callRecord = tc;
|
|
1977
|
+
const fn = callRecord.function;
|
|
1978
|
+
if (!fn || typeof fn !== "object") {
|
|
1979
|
+
continue;
|
|
1980
|
+
}
|
|
1981
|
+
const fnRecord = fn;
|
|
1982
|
+
const name = fnRecord.name;
|
|
1983
|
+
if (typeof name === "string" && map.has(name)) {
|
|
1984
|
+
fnRecord.name = map.get(name);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
return body;
|
|
1989
|
+
}
|
|
1990
|
+
function createToolNameRestoreStream(body, map) {
|
|
1991
|
+
if (map.size === 0) {
|
|
1992
|
+
return body;
|
|
1993
|
+
}
|
|
1994
|
+
const entries = Array.from(map.entries());
|
|
1995
|
+
const decoder = new TextDecoder();
|
|
1996
|
+
const encoder = new TextEncoder();
|
|
1997
|
+
const reader = body.getReader();
|
|
1998
|
+
return new ReadableStream({
|
|
1999
|
+
async pull(controller) {
|
|
2000
|
+
try {
|
|
2001
|
+
const { value, done } = await reader.read();
|
|
2002
|
+
if (done) {
|
|
2003
|
+
controller.close();
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
let text = decoder.decode(value, { stream: true });
|
|
2007
|
+
for (const [sanitized, original] of entries) {
|
|
2008
|
+
const escapedSanitized = sanitized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2009
|
+
const re = new RegExp(`("name"\\s*:\\s*")${escapedSanitized}(")`, "g");
|
|
2010
|
+
text = text.replace(re, `$1${original}$2`);
|
|
2011
|
+
}
|
|
2012
|
+
controller.enqueue(encoder.encode(text));
|
|
2013
|
+
} catch (err) {
|
|
2014
|
+
controller.error(err);
|
|
2015
|
+
}
|
|
2016
|
+
},
|
|
2017
|
+
cancel() {
|
|
2018
|
+
reader.cancel().catch(() => {
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
|
|
1805
2024
|
// src/fetch.ts
|
|
1806
2025
|
function createResponsesFetch(options) {
|
|
1807
2026
|
if (!options.baseUrl) {
|
|
@@ -1970,6 +2189,10 @@ async function handleResponses(request, format, options, baseFetch, incomingHead
|
|
|
1970
2189
|
const fbModel = fb.model ? `, model: ${fb.model}` : "";
|
|
1971
2190
|
console.warn(`[fallback] last user message has image, routing to ${fb.baseUrl}${fbModel}`);
|
|
1972
2191
|
}
|
|
2192
|
+
if (options.dropTools && request.tools) {
|
|
2193
|
+
const { dropTools } = options;
|
|
2194
|
+
request = { ...request, tools: request.tools.filter((tool) => !dropTools(tool)) };
|
|
2195
|
+
}
|
|
1973
2196
|
const streaming = request.stream ?? false;
|
|
1974
2197
|
const resolvedUrl = normalizeBaseUrl(options.baseUrl, format);
|
|
1975
2198
|
const { upstreamBody, requestMetadata } = buildUpstreamBody(
|
|
@@ -1983,6 +2206,10 @@ async function handleResponses(request, format, options, baseFetch, incomingHead
|
|
|
1983
2206
|
options.fallbackThoughtSignature
|
|
1984
2207
|
);
|
|
1985
2208
|
const upstreamHeaders = buildUpstreamHeaders(format, options, incomingHeaders);
|
|
2209
|
+
const toolNameMap = format === "openai-chat" ? (
|
|
2210
|
+
// eslint-disable-next-line no-restricted-syntax -- upstreamBody is unknown; cast to Record for tool mutation
|
|
2211
|
+
sanitizeUpstreamToolNames(upstreamBody)
|
|
2212
|
+
) : /* @__PURE__ */ new Map();
|
|
1986
2213
|
const upstream = await baseFetch(resolvedUrl, {
|
|
1987
2214
|
method: "POST",
|
|
1988
2215
|
headers: upstreamHeaders,
|
|
@@ -1996,13 +2223,23 @@ async function handleResponses(request, format, options, baseFetch, incomingHead
|
|
|
1996
2223
|
});
|
|
1997
2224
|
}
|
|
1998
2225
|
if (!streaming) {
|
|
1999
|
-
const
|
|
2226
|
+
const rawBody = await upstream.json();
|
|
2227
|
+
const restoredBody = restoreToolNamesInChatResponse(
|
|
2228
|
+
// eslint-disable-next-line no-restricted-syntax -- upstream json() returns unknown; cast to Record for tool name restoration
|
|
2229
|
+
rawBody,
|
|
2230
|
+
toolNameMap
|
|
2231
|
+
);
|
|
2000
2232
|
const translated = format === "anthropic" ? (
|
|
2001
2233
|
// eslint-disable-next-line no-restricted-syntax -- union type narrowing requires type assertion
|
|
2002
|
-
translateResponse(
|
|
2234
|
+
translateResponse(restoredBody, {
|
|
2235
|
+
model: request.model
|
|
2236
|
+
})
|
|
2003
2237
|
) : (
|
|
2004
2238
|
// eslint-disable-next-line no-restricted-syntax -- union type narrowing requires type assertion
|
|
2005
|
-
translateResponse2(
|
|
2239
|
+
translateResponse2(restoredBody, {
|
|
2240
|
+
model: request.model,
|
|
2241
|
+
requestTools: request.tools ?? []
|
|
2242
|
+
})
|
|
2006
2243
|
);
|
|
2007
2244
|
options.onCacheStats?.(extractCacheStatsFromResponse(translated));
|
|
2008
2245
|
return new Response(JSON.stringify(translated), {
|
|
@@ -2013,7 +2250,8 @@ async function handleResponses(request, format, options, baseFetch, incomingHead
|
|
|
2013
2250
|
if (!upstream.body) {
|
|
2014
2251
|
return jsonErrorResponse(502, "Upstream streaming response has no body");
|
|
2015
2252
|
}
|
|
2016
|
-
const
|
|
2253
|
+
const upstreamBodyStream = createToolNameRestoreStream(upstream.body, toolNameMap);
|
|
2254
|
+
const events = format === "anthropic" ? translateStream(upstreamBodyStream, { model: request.model, requestMetadata }) : translateStream2(upstreamBodyStream, { model: request.model, requestMetadata });
|
|
2017
2255
|
return new Response(
|
|
2018
2256
|
responsesEventsToSseStream(collectCacheStatsFromStream(events, options.onCacheStats)),
|
|
2019
2257
|
{
|