@codeproxy/core 0.1.9 → 0.1.12

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 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(name)) {
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 body = await upstream.json();
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(body, { model: request.model })
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(body, { model: request.model })
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 events = format === "anthropic" ? translateStream(upstream.body, { model: request.model, requestMetadata }) : translateStream2(upstream.body, { model: request.model, requestMetadata });
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
  {