@codeproxy/core 0.1.5 → 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/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
@@ -43,13 +43,21 @@ var DEFAULT_REASONING_BUDGETS = {
43
43
  function translateRequest(data, options = {}) {
44
44
  const model = data.model;
45
45
  const maxTokens = typeof data.max_output_tokens === "number" && data.max_output_tokens || typeof data.max_tokens === "number" && data.max_tokens || options.defaultMaxTokens || 8192;
46
- const systemBlocks = extractSystemBlocks(data.instructions);
46
+ let systemBlocks = extractSystemBlocks(data.instructions);
47
47
  const built = buildMessages(data, systemBlocks);
48
48
  let messages = built.messages;
49
- const hasPromptCache = built.hasPromptCache;
49
+ let hasPromptCache = built.hasPromptCache;
50
+ if (data.prompt_cache_key) {
51
+ hasPromptCache = true;
52
+ systemBlocks = markBlocksForCache(systemBlocks);
53
+ messages = markCacheBreakpoint(messages);
54
+ }
50
55
  messages = repairToolAdjacency(messages);
51
56
  messages = sanitizeMessages(messages);
52
57
  messages = ensureEndsWithUser(messages);
58
+ if (data.prompt_cache_key) {
59
+ messages = markCacheBreakpoint(messages);
60
+ }
53
61
  const request = {
54
62
  model,
55
63
  messages,
@@ -190,7 +198,15 @@ function buildMessages(data, systemBlocks) {
190
198
  } else if (part && typeof part === "object") {
191
199
  const contentPart = part;
192
200
  if (contentPart.type === "input_text" || contentPart.type === "text" || contentPart.type === "output_text") {
193
- contentBlocks.push({ type: "text", text: String(contentPart.text ?? "") });
201
+ const textBlock = {
202
+ type: "text",
203
+ text: String(contentPart.text ?? "")
204
+ };
205
+ const cc = contentPart.cache_control;
206
+ if (cc) {
207
+ textBlock.cache_control = cc;
208
+ }
209
+ contentBlocks.push(textBlock);
194
210
  } else if (contentPart.type === "input_image" || contentPart.type === "image" || contentPart.type === "image_url") {
195
211
  const imgUrlPart = contentPart;
196
212
  const imgUrl = imgUrlPart.image_url;
@@ -508,6 +524,48 @@ function ensureEndsWithUser(messages) {
508
524
  }
509
525
  return [...messages, { role: "user", content: [{ type: "text", text: "Continue." }] }];
510
526
  }
527
+ function markBlocksForCache(blocks) {
528
+ let count = 0;
529
+ for (const block of blocks) {
530
+ if (!block.cache_control) {
531
+ block.cache_control = { type: "ephemeral" };
532
+ count++;
533
+ if (count >= 3) {
534
+ break;
535
+ }
536
+ }
537
+ }
538
+ return blocks;
539
+ }
540
+ function markCacheBreakpoint(messages) {
541
+ for (const msg of messages) {
542
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
543
+ for (let j = msg.content.length - 1; j >= 0; j--) {
544
+ const block = msg.content[j];
545
+ if (block.type === "text") {
546
+ if (!block.cache_control) {
547
+ block.cache_control = { type: "ephemeral" };
548
+ }
549
+ return messages;
550
+ }
551
+ }
552
+ }
553
+ }
554
+ for (const msg of messages) {
555
+ if (msg.role === "user" && Array.isArray(msg.content)) {
556
+ for (let j = msg.content.length - 1; j >= 0; j--) {
557
+ const block = msg.content[j];
558
+ if (block.type === "text") {
559
+ if (!block.cache_control) {
560
+ block.cache_control = { type: "ephemeral" };
561
+ }
562
+ return messages;
563
+ }
564
+ }
565
+ }
566
+ }
567
+ return messages;
568
+ }
511
569
 
512
570
  // src/utils/json.ts
513
571
  function safeJsonParse(text) {
@@ -783,6 +841,8 @@ var StreamTranslator = class {
783
841
  *finalize() {
784
842
  const items = [];
785
843
  if (this.textItem) {
844
+ this.textItem.status = "completed";
845
+ this.textItem.content[0].text = this.textBuffer;
786
846
  items.push({ index: this.textItemIndex, item: this.textItem });
787
847
  }
788
848
  for (const block of this.blocks.values()) {
@@ -894,15 +954,22 @@ var StreamTranslator = class {
894
954
  if (btype === "tool_use") {
895
955
  const outputIndex = this.outputCounter++;
896
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 !== "{}";
897
959
  const item = {
898
960
  id: callId,
899
961
  type: "function_call",
900
962
  status: "in_progress",
901
963
  name: block.name ?? "",
902
- arguments: "",
964
+ arguments: hasInitialInput ? initialInput : "",
903
965
  call_id: callId
904
966
  };
905
- 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
+ });
906
973
  yield this.makeEvent("response.output_item.added", {
907
974
  response_id: this.responseId,
908
975
  output_index: outputIndex,
@@ -1021,7 +1088,7 @@ function translateRequest2(data, options = {}) {
1021
1088
  continue;
1022
1089
  }
1023
1090
  const rawItem = raw;
1024
- processInputItem(rawItem, messages, options.dropImages);
1091
+ processInputItem(rawItem, messages, options);
1025
1092
  }
1026
1093
  const request = {
1027
1094
  model: data.model,
@@ -1081,7 +1148,7 @@ function buildSystemContent(instructions) {
1081
1148
  }
1082
1149
  return out;
1083
1150
  }
1084
- function processInputItem(item, messages, dropImages) {
1151
+ function processInputItem(item, messages, options) {
1085
1152
  const itemType = String(item.type) || "message";
1086
1153
  const getLastAssistant = () => {
1087
1154
  const last = messages[messages.length - 1];
@@ -1143,7 +1210,7 @@ function processInputItem(item, messages, dropImages) {
1143
1210
  } else if (contentPart.type === "reasoning_text") {
1144
1211
  reasoningContent += String(contentPart.text ?? "");
1145
1212
  } else if (contentPart.type === "input_image" || contentPart.type === "image" || contentPart.type === "image_url") {
1146
- if (dropImages) {
1213
+ if (options.dropImages) {
1147
1214
  continue;
1148
1215
  }
1149
1216
  let url = "";
@@ -1218,7 +1285,7 @@ function processInputItem(item, messages, dropImages) {
1218
1285
  return;
1219
1286
  }
1220
1287
  if (itemType === "function_call" || itemType === "commandExecution" || itemType === "local_shell_call" || itemType === "fileChange" || itemType === "custom_tool_call" || itemType === "web_search_call") {
1221
- processToolCall(item, messages, getLastAssistant);
1288
+ processToolCall(item, messages, getLastAssistant, options.fallbackThoughtSignature);
1222
1289
  return;
1223
1290
  }
1224
1291
  if (itemType === "function_call_output" || itemType === "commandExecutionOutput" || itemType === "fileChangeOutput" || itemType === "custom_tool_call_output") {
@@ -1226,7 +1293,7 @@ function processInputItem(item, messages, dropImages) {
1226
1293
  return;
1227
1294
  }
1228
1295
  }
1229
- function processToolCall(item, messages, getLastAssistant) {
1296
+ function processToolCall(item, messages, getLastAssistant, fallbackThoughtSignature) {
1230
1297
  const callId = String(item.call_id ?? "") || String(item.id ?? "") || makeId("call");
1231
1298
  let name = item.name === void 0 ? void 0 : String(item.name);
1232
1299
  const itemType = item.type === void 0 ? void 0 : String(item.type);
@@ -1272,16 +1339,18 @@ function processToolCall(item, messages, getLastAssistant) {
1272
1339
  if (!amsg.tool_calls) {
1273
1340
  amsg.tool_calls = [];
1274
1341
  }
1275
- amsg.tool_calls.push({
1342
+ const toolCall = {
1276
1343
  id: callId,
1277
1344
  type: "function",
1278
1345
  function: { name, arguments: argsStr }
1279
- });
1280
- const sig = item.thought_signature;
1346
+ };
1347
+ const sig = item.thought_signature ?? fallbackThoughtSignature;
1281
1348
  const thought = item.thought;
1282
1349
  if (typeof sig === "string" && sig) {
1350
+ toolCall.extra_content = { google: { thought_signature: sig } };
1283
1351
  amsg.thought_signature = sig;
1284
1352
  }
1353
+ amsg.tool_calls.push(toolCall);
1285
1354
  if (typeof thought === "string" && thought) {
1286
1355
  amsg.reasoning_content = (amsg.reasoning_content ?? "") + thought;
1287
1356
  }
@@ -1485,6 +1554,10 @@ function mapToolCallToOutput(tc) {
1485
1554
  arguments: args,
1486
1555
  call_id: callId
1487
1556
  };
1557
+ const thoughtSignature = getThoughtSignature(tc);
1558
+ if (thoughtSignature) {
1559
+ item.thought_signature = thoughtSignature;
1560
+ }
1488
1561
  if (SHELL_TOOL_NAMES3.has(name)) {
1489
1562
  item.type = "local_shell_call";
1490
1563
  const parsed = safeJsonParse(args);
@@ -1492,6 +1565,10 @@ function mapToolCallToOutput(tc) {
1492
1565
  }
1493
1566
  return item;
1494
1567
  }
1568
+ function getThoughtSignature(tc) {
1569
+ const sig = tc.extra_content?.google?.thought_signature ?? tc.thought_signature;
1570
+ return typeof sig === "string" && sig ? sig : void 0;
1571
+ }
1495
1572
 
1496
1573
  // src/translate/openai/translateStream.ts
1497
1574
  var SHELL_TOOL_NAMES4 = /* @__PURE__ */ new Set(["shell", "container.exec", "shell_command"]);
@@ -1594,6 +1671,10 @@ var StreamTranslator2 = class {
1594
1671
  });
1595
1672
  }
1596
1673
  const fn = tc.function;
1674
+ const thoughtSignature = getThoughtSignature2(tc);
1675
+ if (thoughtSignature) {
1676
+ state.item.thought_signature = thoughtSignature;
1677
+ }
1597
1678
  if (fn?.name) {
1598
1679
  state.item.name = (state.item.name ?? "") + fn.name;
1599
1680
  }
@@ -1705,6 +1786,10 @@ var StreamTranslator2 = class {
1705
1786
  };
1706
1787
  }
1707
1788
  };
1789
+ function getThoughtSignature2(tc) {
1790
+ const sig = tc.extra_content?.google?.thought_signature ?? tc.thought_signature;
1791
+ return typeof sig === "string" && sig ? sig : void 0;
1792
+ }
1708
1793
 
1709
1794
  // src/fetch.ts
1710
1795
  function createResponsesFetch(options) {
@@ -1712,7 +1797,7 @@ function createResponsesFetch(options) {
1712
1797
  throw new Error("baseUrl is required");
1713
1798
  }
1714
1799
  const rawFormat = options.upstreamFormat;
1715
- const format = rawFormat ? normalizeFormat(rawFormat) : inferFormatFromUrl(options.baseUrl) ?? "openai-chat";
1800
+ const format = rawFormat ? normalizeFormat(rawFormat) : inferFormatFromUrl(options.baseUrl) ?? inferFormatFromModel(options.model) ?? "openai-chat";
1716
1801
  if (!format) {
1717
1802
  throw new Error(
1718
1803
  `Unsupported upstream format: ${options.upstreamFormat}. Use 'anthropic' or 'openai-chat'`
@@ -1883,7 +1968,8 @@ async function handleResponses(request, format, options, baseFetch, incomingHead
1883
1968
  options.baseUrl,
1884
1969
  dropImages,
1885
1970
  options.reasoning_effort,
1886
- options.thinking
1971
+ options.thinking,
1972
+ options.fallbackThoughtSignature
1887
1973
  );
1888
1974
  const upstreamHeaders = buildUpstreamHeaders(format, options, incomingHeaders);
1889
1975
  const upstream = await baseFetch(resolvedUrl, {
@@ -1939,7 +2025,7 @@ function buildRequestMetadata(request, temperature, top_p) {
1939
2025
  metadata: request.metadata ?? {}
1940
2026
  };
1941
2027
  }
1942
- function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reasoning_effort, thinking) {
2028
+ function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reasoning_effort, thinking, fallbackThoughtSignature) {
1943
2029
  if (format === "anthropic") {
1944
2030
  const { request: ar } = translateRequest(request);
1945
2031
  ar.stream = streaming;
@@ -1970,7 +2056,10 @@ function buildUpstreamBody(request, format, streaming, baseUrl, dropImages, reas
1970
2056
  requestMetadata: buildRequestMetadata(request, ar.temperature, ar.top_p)
1971
2057
  };
1972
2058
  }
1973
- const { request: cr } = translateRequest2(request, { dropImages });
2059
+ const { request: cr } = translateRequest2(request, {
2060
+ dropImages,
2061
+ fallbackThoughtSignature
2062
+ });
1974
2063
  cr.stream = streaming;
1975
2064
  if (streaming) {
1976
2065
  cr.stream_options = { include_usage: true };