@codeproxy/core 0.1.6 → 0.1.8

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.d.cts CHANGED
@@ -23,6 +23,8 @@ interface CreateResponsesFetchOptions {
23
23
  passthroughFetch?: typeof fetch;
24
24
  /** Drop image/file parts from user messages (e.g. DeepSeek text-only models). */
25
25
  dropImages?: boolean;
26
+ /** Fallback thought signature for Gemini OpenAI-compatible tool histories. */
27
+ fallbackThoughtSignature?: string;
26
28
  /** Optional callback to receive cache statistics. */
27
29
  onCacheStats?: (stats: CacheStats) => void;
28
30
  /** Override reasoning_effort sent to the upstream model (OpenAI Chat / Anthropic). */
@@ -94,6 +96,7 @@ interface ResponsesFunctionCallItem {
94
96
  call_id?: string;
95
97
  name?: string;
96
98
  arguments?: string | Record<string, unknown>;
99
+ thought_signature?: string;
97
100
  [key: string]: unknown;
98
101
  }
99
102
  interface ResponsesFunctionCallOutputItem {
@@ -239,6 +242,7 @@ interface ResponsesOutputFunctionCall {
239
242
  name?: string;
240
243
  arguments?: string;
241
244
  call_id?: string;
245
+ thought_signature?: string;
242
246
  action?: {
243
247
  type?: string;
244
248
  command?: string[];
@@ -488,10 +492,18 @@ declare namespace index$2 {
488
492
  interface OpenAiChatToolCall {
489
493
  id?: string;
490
494
  type?: 'function' | string;
495
+ extra_content?: {
496
+ google?: {
497
+ thought_signature?: string;
498
+ [key: string]: unknown;
499
+ };
500
+ [key: string]: unknown;
501
+ };
491
502
  function?: {
492
503
  name?: string;
493
504
  arguments?: string | Record<string, unknown>;
494
505
  };
506
+ thought_signature?: string;
495
507
  }
496
508
  interface OpenAiChatMessage {
497
509
  role: 'system' | 'user' | 'assistant' | 'tool' | string;
@@ -575,10 +587,12 @@ interface OpenAiChatStreamDeltaToolCall {
575
587
  index: number;
576
588
  id?: string;
577
589
  type?: string;
590
+ extra_content?: OpenAiChatToolCall['extra_content'];
578
591
  function?: {
579
592
  name?: string;
580
593
  arguments?: string | Record<string, unknown>;
581
594
  };
595
+ thought_signature?: string;
582
596
  }
583
597
  interface OpenAiChatStreamDelta {
584
598
  role?: string;
@@ -613,6 +627,8 @@ interface TranslateRequestOptions {
613
627
  /** If true, strip `strict` from function tools (some upstreams reject it). */
614
628
  /** If true, drop image/file parts from user messages (e.g. DeepSeek text-only models). */
615
629
  dropImages?: boolean;
630
+ /** Fallback signature for Gemini OpenAI histories that lack returned signatures. */
631
+ fallbackThoughtSignature?: string;
616
632
  }
617
633
  interface TranslateRequestResult {
618
634
  request: OpenAiChatRequest;
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ interface CreateResponsesFetchOptions {
23
23
  passthroughFetch?: typeof fetch;
24
24
  /** Drop image/file parts from user messages (e.g. DeepSeek text-only models). */
25
25
  dropImages?: boolean;
26
+ /** Fallback thought signature for Gemini OpenAI-compatible tool histories. */
27
+ fallbackThoughtSignature?: string;
26
28
  /** Optional callback to receive cache statistics. */
27
29
  onCacheStats?: (stats: CacheStats) => void;
28
30
  /** Override reasoning_effort sent to the upstream model (OpenAI Chat / Anthropic). */
@@ -94,6 +96,7 @@ interface ResponsesFunctionCallItem {
94
96
  call_id?: string;
95
97
  name?: string;
96
98
  arguments?: string | Record<string, unknown>;
99
+ thought_signature?: string;
97
100
  [key: string]: unknown;
98
101
  }
99
102
  interface ResponsesFunctionCallOutputItem {
@@ -239,6 +242,7 @@ interface ResponsesOutputFunctionCall {
239
242
  name?: string;
240
243
  arguments?: string;
241
244
  call_id?: string;
245
+ thought_signature?: string;
242
246
  action?: {
243
247
  type?: string;
244
248
  command?: string[];
@@ -488,10 +492,18 @@ declare namespace index$2 {
488
492
  interface OpenAiChatToolCall {
489
493
  id?: string;
490
494
  type?: 'function' | string;
495
+ extra_content?: {
496
+ google?: {
497
+ thought_signature?: string;
498
+ [key: string]: unknown;
499
+ };
500
+ [key: string]: unknown;
501
+ };
491
502
  function?: {
492
503
  name?: string;
493
504
  arguments?: string | Record<string, unknown>;
494
505
  };
506
+ thought_signature?: string;
495
507
  }
496
508
  interface OpenAiChatMessage {
497
509
  role: 'system' | 'user' | 'assistant' | 'tool' | string;
@@ -575,10 +587,12 @@ interface OpenAiChatStreamDeltaToolCall {
575
587
  index: number;
576
588
  id?: string;
577
589
  type?: string;
590
+ extra_content?: OpenAiChatToolCall['extra_content'];
578
591
  function?: {
579
592
  name?: string;
580
593
  arguments?: string | Record<string, unknown>;
581
594
  };
595
+ thought_signature?: string;
582
596
  }
583
597
  interface OpenAiChatStreamDelta {
584
598
  role?: string;
@@ -613,6 +627,8 @@ interface TranslateRequestOptions {
613
627
  /** If true, strip `strict` from function tools (some upstreams reject it). */
614
628
  /** If true, drop image/file parts from user messages (e.g. DeepSeek text-only models). */
615
629
  dropImages?: boolean;
630
+ /** Fallback signature for Gemini OpenAI histories that lack returned signatures. */
631
+ fallbackThoughtSignature?: string;
616
632
  }
617
633
  interface TranslateRequestResult {
618
634
  request: OpenAiChatRequest;
package/dist/index.js CHANGED
@@ -525,9 +525,14 @@ function ensureEndsWithUser(messages) {
525
525
  return [...messages, { role: "user", content: [{ type: "text", text: "Continue." }] }];
526
526
  }
527
527
  function markBlocksForCache(blocks) {
528
+ let count = 0;
528
529
  for (const block of blocks) {
529
530
  if (!block.cache_control) {
530
531
  block.cache_control = { type: "ephemeral" };
532
+ count++;
533
+ if (count >= 3) {
534
+ break;
535
+ }
531
536
  }
532
537
  }
533
538
  return blocks;
@@ -836,6 +841,8 @@ var StreamTranslator = class {
836
841
  *finalize() {
837
842
  const items = [];
838
843
  if (this.textItem) {
844
+ this.textItem.status = "completed";
845
+ this.textItem.content[0].text = this.textBuffer;
839
846
  items.push({ index: this.textItemIndex, item: this.textItem });
840
847
  }
841
848
  for (const block of this.blocks.values()) {
@@ -947,15 +954,22 @@ var StreamTranslator = class {
947
954
  if (btype === "tool_use") {
948
955
  const outputIndex = this.outputCounter++;
949
956
  const callId = block.id ?? makeId("call");
957
+ const initialInput = typeof block.input === "object" && block.input !== null ? jsonStringifySafe(block.input) : "";
958
+ const hasInitialInput = initialInput !== "" && initialInput !== "{}";
950
959
  const item = {
951
960
  id: callId,
952
961
  type: "function_call",
953
962
  status: "in_progress",
954
963
  name: block.name ?? "",
955
- arguments: "",
964
+ arguments: hasInitialInput ? initialInput : "",
956
965
  call_id: callId
957
966
  };
958
- this.blocks.set(index, { type: "tool_use", outputIndex, item, buffer: "" });
967
+ this.blocks.set(index, {
968
+ type: "tool_use",
969
+ outputIndex,
970
+ item,
971
+ buffer: hasInitialInput ? initialInput : ""
972
+ });
959
973
  yield this.makeEvent("response.output_item.added", {
960
974
  response_id: this.responseId,
961
975
  output_index: outputIndex,
@@ -1057,9 +1071,66 @@ __export(openai_exports, {
1057
1071
  translateStream: () => translateStream2
1058
1072
  });
1059
1073
 
1074
+ // src/translate/openai/toolSchema.ts
1075
+ function sanitizeJsonSchema(schema, depth = 0) {
1076
+ if (!schema || typeof schema !== "object" || Array.isArray(schema) || depth > 20) {
1077
+ return schema;
1078
+ }
1079
+ const src = schema;
1080
+ if ("$ref" in src) {
1081
+ return src.description ? { description: src.description } : {};
1082
+ }
1083
+ const out = {};
1084
+ for (const [key, val] of Object.entries(src)) {
1085
+ if (isDroppedSchemaKeyword(key)) {
1086
+ continue;
1087
+ }
1088
+ if (key === "properties" && val && typeof val === "object" && !Array.isArray(val)) {
1089
+ const props = {};
1090
+ for (const [propName, propSchema] of Object.entries(val)) {
1091
+ props[propName] = sanitizeJsonSchema(propSchema, depth + 1);
1092
+ }
1093
+ out[key] = props;
1094
+ } else if (key === "additionalProperties") {
1095
+ if (typeof val !== "boolean") {
1096
+ out[key] = sanitizeJsonSchema(val, depth + 1);
1097
+ }
1098
+ } else if (key === "items") {
1099
+ out[key] = sanitizeJsonSchema(val, depth + 1);
1100
+ } else if (isSchemaCompositionKeyword(key) && Array.isArray(val)) {
1101
+ out[key] = val.map((schemaItem) => sanitizeJsonSchema(schemaItem, depth + 1));
1102
+ } else {
1103
+ out[key] = val;
1104
+ }
1105
+ }
1106
+ return out;
1107
+ }
1108
+ function getValidFunctionNames(tools) {
1109
+ const names = /* @__PURE__ */ new Set();
1110
+ for (const tool of tools) {
1111
+ const maybeTool = tool;
1112
+ if (maybeTool.type === "function" && typeof maybeTool.function?.name === "string") {
1113
+ names.add(maybeTool.function.name);
1114
+ }
1115
+ }
1116
+ return names.size ? names : void 0;
1117
+ }
1118
+ function isDroppedSchemaKeyword(key) {
1119
+ return key === "$schema" || key === "$defs" || key === "definitions" || key === "$id" || key === "$anchor" || key === "$comment";
1120
+ }
1121
+ function isSchemaCompositionKeyword(key) {
1122
+ return key === "anyOf" || key === "oneOf" || key === "allOf" || key === "not";
1123
+ }
1124
+
1060
1125
  // src/translate/openai/translateRequest.ts
1061
1126
  function translateRequest2(data, options = {}) {
1062
1127
  const messages = [];
1128
+ const tools = mapTools2(data.tools ?? []);
1129
+ const validFunctionNames = getValidFunctionNames(tools);
1130
+ const context = {
1131
+ validFunctionNames,
1132
+ ignoredToolCallIds: /* @__PURE__ */ new Set()
1133
+ };
1063
1134
  const systemContent = buildSystemContent(data.instructions);
1064
1135
  if (systemContent) {
1065
1136
  messages.push({ role: "system", content: systemContent });
@@ -1074,7 +1145,7 @@ function translateRequest2(data, options = {}) {
1074
1145
  continue;
1075
1146
  }
1076
1147
  const rawItem = raw;
1077
- processInputItem(rawItem, messages, options.dropImages);
1148
+ processInputItem(rawItem, messages, options, context);
1078
1149
  }
1079
1150
  const request = {
1080
1151
  model: data.model,
@@ -1095,7 +1166,6 @@ function translateRequest2(data, options = {}) {
1095
1166
  if (typeof maxTokens === "number") {
1096
1167
  request.max_tokens = maxTokens;
1097
1168
  }
1098
- const tools = mapTools2(data.tools ?? []);
1099
1169
  if (tools.length) {
1100
1170
  request.tools = tools;
1101
1171
  const toolChoice = mapToolChoice2(data.tool_choice);
@@ -1134,7 +1204,7 @@ function buildSystemContent(instructions) {
1134
1204
  }
1135
1205
  return out;
1136
1206
  }
1137
- function processInputItem(item, messages, dropImages) {
1207
+ function processInputItem(item, messages, options, context) {
1138
1208
  const itemType = String(item.type) || "message";
1139
1209
  const getLastAssistant = () => {
1140
1210
  const last = messages[messages.length - 1];
@@ -1196,7 +1266,7 @@ function processInputItem(item, messages, dropImages) {
1196
1266
  } else if (contentPart.type === "reasoning_text") {
1197
1267
  reasoningContent += String(contentPart.text ?? "");
1198
1268
  } else if (contentPart.type === "input_image" || contentPart.type === "image" || contentPart.type === "image_url") {
1199
- if (dropImages) {
1269
+ if (options.dropImages) {
1200
1270
  continue;
1201
1271
  }
1202
1272
  let url = "";
@@ -1271,15 +1341,15 @@ function processInputItem(item, messages, dropImages) {
1271
1341
  return;
1272
1342
  }
1273
1343
  if (itemType === "function_call" || itemType === "commandExecution" || itemType === "local_shell_call" || itemType === "fileChange" || itemType === "custom_tool_call" || itemType === "web_search_call") {
1274
- processToolCall(item, messages, getLastAssistant);
1344
+ processToolCall(item, messages, getLastAssistant, options.fallbackThoughtSignature, context);
1275
1345
  return;
1276
1346
  }
1277
1347
  if (itemType === "function_call_output" || itemType === "commandExecutionOutput" || itemType === "fileChangeOutput" || itemType === "custom_tool_call_output") {
1278
- processToolOutput(item, messages);
1348
+ processToolOutput(item, messages, context);
1279
1349
  return;
1280
1350
  }
1281
1351
  }
1282
- function processToolCall(item, messages, getLastAssistant) {
1352
+ function processToolCall(item, messages, getLastAssistant, fallbackThoughtSignature, context) {
1283
1353
  const callId = String(item.call_id ?? "") || String(item.id ?? "") || makeId("call");
1284
1354
  let name = item.name === void 0 ? void 0 : String(item.name);
1285
1355
  const itemType = item.type === void 0 ? void 0 : String(item.type);
@@ -1321,25 +1391,35 @@ function processToolCall(item, messages, getLastAssistant) {
1321
1391
  if (!name) {
1322
1392
  return;
1323
1393
  }
1394
+ if (context?.validFunctionNames?.size && !context.validFunctionNames.has(name)) {
1395
+ context.ignoredToolCallIds.add(callId);
1396
+ const amsg2 = getLastAssistant();
1397
+ const note = `Unsupported tool call omitted: ${name}`;
1398
+ amsg2.content = typeof amsg2.content === "string" && amsg2.content ? `${amsg2.content}
1399
+ ${note}` : note;
1400
+ return;
1401
+ }
1324
1402
  const amsg = getLastAssistant();
1325
1403
  if (!amsg.tool_calls) {
1326
1404
  amsg.tool_calls = [];
1327
1405
  }
1328
- amsg.tool_calls.push({
1406
+ const toolCall = {
1329
1407
  id: callId,
1330
1408
  type: "function",
1331
1409
  function: { name, arguments: argsStr }
1332
- });
1333
- const sig = item.thought_signature;
1410
+ };
1411
+ const sig = item.thought_signature ?? fallbackThoughtSignature;
1334
1412
  const thought = item.thought;
1335
1413
  if (typeof sig === "string" && sig) {
1414
+ toolCall.extra_content = { google: { thought_signature: sig } };
1336
1415
  amsg.thought_signature = sig;
1337
1416
  }
1417
+ amsg.tool_calls.push(toolCall);
1338
1418
  if (typeof thought === "string" && thought) {
1339
1419
  amsg.reasoning_content = (amsg.reasoning_content ?? "") + thought;
1340
1420
  }
1341
1421
  }
1342
- function processToolOutput(item, messages) {
1422
+ function processToolOutput(item, messages, context) {
1343
1423
  const callId = item.call_id === void 0 ? void 0 : String(item.call_id);
1344
1424
  const outputRaw = item.output ?? item.content ?? item.stdout ?? "";
1345
1425
  let content = "";
@@ -1366,6 +1446,15 @@ function processToolOutput(item, messages) {
1366
1446
  if (!content && typeof item.stderr === "string" && item.stderr) {
1367
1447
  content = `Error: ${item.stderr}`;
1368
1448
  }
1449
+ if (callId && context?.ignoredToolCallIds.has(callId)) {
1450
+ if (content) {
1451
+ messages.push({
1452
+ role: "user",
1453
+ content: `Output for omitted unsupported tool call: ${content}`
1454
+ });
1455
+ }
1456
+ return;
1457
+ }
1369
1458
  messages.push({
1370
1459
  role: "tool",
1371
1460
  tool_call_id: callId,
@@ -1385,7 +1474,8 @@ function mapTools2(tools) {
1385
1474
  if (!name) {
1386
1475
  continue;
1387
1476
  }
1388
- const params = fn?.parameters ?? tool.parameters ?? { type: "object" };
1477
+ const rawParams = fn?.parameters ?? tool.parameters ?? { type: "object" };
1478
+ const params = sanitizeJsonSchema(rawParams);
1389
1479
  out.push({
1390
1480
  type: "function",
1391
1481
  function: {
@@ -1538,6 +1628,10 @@ function mapToolCallToOutput(tc) {
1538
1628
  arguments: args,
1539
1629
  call_id: callId
1540
1630
  };
1631
+ const thoughtSignature = getThoughtSignature(tc);
1632
+ if (thoughtSignature) {
1633
+ item.thought_signature = thoughtSignature;
1634
+ }
1541
1635
  if (SHELL_TOOL_NAMES3.has(name)) {
1542
1636
  item.type = "local_shell_call";
1543
1637
  const parsed = safeJsonParse(args);
@@ -1545,6 +1639,10 @@ function mapToolCallToOutput(tc) {
1545
1639
  }
1546
1640
  return item;
1547
1641
  }
1642
+ function getThoughtSignature(tc) {
1643
+ const sig = tc.extra_content?.google?.thought_signature ?? tc.thought_signature;
1644
+ return typeof sig === "string" && sig ? sig : void 0;
1645
+ }
1548
1646
 
1549
1647
  // src/translate/openai/translateStream.ts
1550
1648
  var SHELL_TOOL_NAMES4 = /* @__PURE__ */ new Set(["shell", "container.exec", "shell_command"]);
@@ -1647,6 +1745,10 @@ var StreamTranslator2 = class {
1647
1745
  });
1648
1746
  }
1649
1747
  const fn = tc.function;
1748
+ const thoughtSignature = getThoughtSignature2(tc);
1749
+ if (thoughtSignature) {
1750
+ state.item.thought_signature = thoughtSignature;
1751
+ }
1650
1752
  if (fn?.name) {
1651
1753
  state.item.name = (state.item.name ?? "") + fn.name;
1652
1754
  }
@@ -1758,6 +1860,10 @@ var StreamTranslator2 = class {
1758
1860
  };
1759
1861
  }
1760
1862
  };
1863
+ function getThoughtSignature2(tc) {
1864
+ const sig = tc.extra_content?.google?.thought_signature ?? tc.thought_signature;
1865
+ return typeof sig === "string" && sig ? sig : void 0;
1866
+ }
1761
1867
 
1762
1868
  // src/fetch.ts
1763
1869
  function createResponsesFetch(options) {
@@ -1765,7 +1871,7 @@ function createResponsesFetch(options) {
1765
1871
  throw new Error("baseUrl is required");
1766
1872
  }
1767
1873
  const rawFormat = options.upstreamFormat;
1768
- const format = rawFormat ? normalizeFormat(rawFormat) : inferFormatFromUrl(options.baseUrl) ?? "openai-chat";
1874
+ const format = rawFormat ? normalizeFormat(rawFormat) : inferFormatFromUrl(options.baseUrl) ?? inferFormatFromModel(options.model) ?? "openai-chat";
1769
1875
  if (!format) {
1770
1876
  throw new Error(
1771
1877
  `Unsupported upstream format: ${options.upstreamFormat}. Use 'anthropic' or 'openai-chat'`
@@ -1936,7 +2042,8 @@ async function handleResponses(request, format, options, baseFetch, incomingHead
1936
2042
  options.baseUrl,
1937
2043
  dropImages,
1938
2044
  options.reasoning_effort,
1939
- options.thinking
2045
+ options.thinking,
2046
+ options.fallbackThoughtSignature
1940
2047
  );
1941
2048
  const upstreamHeaders = buildUpstreamHeaders(format, options, incomingHeaders);
1942
2049
  const upstream = await baseFetch(resolvedUrl, {
@@ -1992,7 +2099,7 @@ function buildRequestMetadata(request, temperature, top_p) {
1992
2099
  metadata: request.metadata ?? {}
1993
2100
  };
1994
2101
  }
1995
- function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reasoning_effort, thinking) {
2102
+ function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reasoning_effort, thinking, fallbackThoughtSignature) {
1996
2103
  if (format === "anthropic") {
1997
2104
  const { request: ar } = translateRequest(request);
1998
2105
  ar.stream = streaming;
@@ -2023,7 +2130,10 @@ function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reas
2023
2130
  requestMetadata: buildRequestMetadata(request, ar.temperature, ar.top_p)
2024
2131
  };
2025
2132
  }
2026
- const { request: cr } = translateRequest2(request, { dropImages });
2133
+ const { request: cr } = translateRequest2(request, {
2134
+ dropImages,
2135
+ fallbackThoughtSignature
2136
+ });
2027
2137
  cr.stream = streaming;
2028
2138
  if (streaming) {
2029
2139
  cr.stream_options = { include_usage: true };