@opencode-ai/ai 0.0.0-beta-18219 → 0.0.0-beta-18269

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.
Files changed (32) hide show
  1. package/dist/cache-policy.js +1 -1
  2. package/dist/protocols/anthropic-messages.js +8 -4
  3. package/dist/protocols/open-responses.d.ts +231 -25
  4. package/dist/protocols/open-responses.js +98 -79
  5. package/dist/protocols/openai-chat.d.ts +1 -0
  6. package/dist/protocols/openai-chat.js +36 -17
  7. package/dist/protocols/openai-compatible-responses.d.ts +37 -3
  8. package/dist/protocols/openai-responses.d.ts +403 -50
  9. package/dist/protocols/openai-responses.js +30 -21
  10. package/dist/protocols/shared.d.ts +3 -4
  11. package/dist/protocols/shared.js +18 -5
  12. package/dist/protocols/utils/open-responses-options.d.ts +0 -1
  13. package/dist/protocols/utils/open-responses-options.js +0 -1
  14. package/dist/protocols/xai-responses.d.ts +52 -3
  15. package/dist/protocols/xai-responses.js +31 -1
  16. package/dist/provider-error.js +4 -2
  17. package/dist/providers/amazon-bedrock-mantle.d.ts +65 -3
  18. package/dist/providers/azure.d.ts +65 -3
  19. package/dist/providers/cerebras.d.ts +228 -0
  20. package/dist/providers/cerebras.js +35 -0
  21. package/dist/providers/deepinfra.d.ts +228 -0
  22. package/dist/providers/deepinfra.js +38 -0
  23. package/dist/providers/google-vertex-responses.d.ts +37 -3
  24. package/dist/providers/index.d.ts +3 -0
  25. package/dist/providers/index.js +3 -0
  26. package/dist/providers/openai-compatible-responses.d.ts +37 -3
  27. package/dist/providers/openai.d.ts +65 -3
  28. package/dist/providers/togetherai.d.ts +228 -0
  29. package/dist/providers/togetherai.js +35 -0
  30. package/dist/schema/options.d.ts +2 -0
  31. package/dist/schema/options.js +2 -0
  32. package/package.json +3 -3
@@ -54,10 +54,43 @@ const OpenResponsesReasoningItem = Schema.Struct({
54
54
  summary: Schema.Array(OpenResponsesReasoningSummaryText),
55
55
  encrypted_content: optionalNull(Schema.String),
56
56
  });
57
- const OpenResponsesItemReference = Schema.Struct({
58
- type: Schema.tag("item_reference"),
57
+ const OpenResponsesWebSearchCall = Schema.StructWithRest(Schema.Struct({
58
+ type: Schema.tag("web_search_call"),
59
59
  id: Schema.String,
60
- });
60
+ status: Schema.optional(Schema.String),
61
+ action: optionalNull(JsonObject),
62
+ }), [JsonObject]);
63
+ const OpenResponsesFileSearchCall = Schema.StructWithRest(Schema.Struct({
64
+ type: Schema.tag("file_search_call"),
65
+ id: Schema.String,
66
+ status: Schema.optional(Schema.String),
67
+ queries: Schema.optional(Schema.Array(Schema.String)),
68
+ results: optionalNull(Schema.Array(JsonObject)),
69
+ }), [JsonObject]);
70
+ const OpenResponsesCodeInterpreterCall = Schema.StructWithRest(Schema.Struct({
71
+ type: Schema.tag("code_interpreter_call"),
72
+ id: Schema.String,
73
+ status: Schema.optional(Schema.String),
74
+ code: optionalNull(Schema.String),
75
+ container_id: optionalNull(Schema.String),
76
+ outputs: optionalNull(Schema.Array(JsonObject)),
77
+ }), [JsonObject]);
78
+ const OpenResponsesMCPCall = Schema.StructWithRest(Schema.Struct({
79
+ type: Schema.tag("mcp_call"),
80
+ id: Schema.String,
81
+ status: Schema.optional(Schema.String),
82
+ server_label: Schema.optional(Schema.String),
83
+ name: Schema.optional(Schema.String),
84
+ arguments: Schema.optional(Schema.String),
85
+ output: optionalNull(Schema.String),
86
+ error: Schema.optional(Schema.Unknown),
87
+ }), [JsonObject]);
88
+ export const HostedToolItem = Schema.Union([
89
+ OpenResponsesWebSearchCall,
90
+ OpenResponsesFileSearchCall,
91
+ OpenResponsesCodeInterpreterCall,
92
+ OpenResponsesMCPCall,
93
+ ]);
61
94
  // `function_call_output.output` accepts either a plain string or an ordered
62
95
  // array of content items so tools can return images and files in addition to text.
63
96
  // https://www.openresponses.org/reference
@@ -83,7 +116,6 @@ export const InputItem = Schema.Union([
83
116
  phase: Schema.optionalKey(MessagePhase),
84
117
  }),
85
118
  OpenResponsesReasoningItem,
86
- OpenResponsesItemReference,
87
119
  Schema.Struct({
88
120
  type: Schema.tag("function_call"),
89
121
  id: Schema.optionalKey(Schema.String),
@@ -96,6 +128,7 @@ export const InputItem = Schema.Union([
96
128
  call_id: Schema.String,
97
129
  output: OpenResponsesFunctionCallOutput,
98
130
  }),
131
+ HostedToolItem,
99
132
  ]);
100
133
  export const Tool = Schema.Struct({
101
134
  type: Schema.tag("function"),
@@ -248,41 +281,36 @@ export const lowerToolChoice = (protocolName, toolChoice) => ProviderShared.matc
248
281
  required: () => "required",
249
282
  tool: (toolName) => ({ type: "function", name: toolName }),
250
283
  });
251
- // Servers validate item ids on replayed history, and a malformed or oversized
252
- // id can fail an otherwise valid request. Only server-issued tokens are worth
253
- // resending; anything else is treated as absent so the item is resent without
254
- // an id (or skipped, for items that cannot be expressed without one).
255
- const ITEM_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
284
+ // Server-issued item ids need a nonempty prefix and suffix, but the prefix is
285
+ // provider-defined and does not necessarily identify the item's semantic type.
256
286
  const itemID = (providerMetadata, providerMetadataKey) => {
257
287
  const metadata = providerMetadata?.[providerMetadataKey];
258
- return ProviderShared.isRecord(metadata) &&
259
- typeof metadata.itemId === "string" &&
260
- ITEM_ID_PATTERN.test(metadata.itemId)
261
- ? metadata.itemId
262
- : undefined;
288
+ if (!ProviderShared.isRecord(metadata) || typeof metadata.itemId !== "string")
289
+ return undefined;
290
+ const separator = metadata.itemId.indexOf("_");
291
+ return separator > 0 && separator < metadata.itemId.length - 1 ? metadata.itemId : undefined;
263
292
  };
264
- const acceptsItemID = (extension, kind, id) => id !== undefined && (extension.acceptsItemID?.(kind, id) ?? true);
265
- const lowerToolCall = (part, providerMetadataKey, extension) => {
293
+ const lowerToolCall = (part, providerMetadataKey) => {
266
294
  const id = itemID(part.providerMetadata, providerMetadataKey);
267
295
  return {
268
296
  type: "function_call",
269
- ...(acceptsItemID(extension, "function-call", id) ? { id } : {}),
297
+ ...(id === undefined ? {} : { id }),
270
298
  call_id: part.id,
271
299
  name: part.name,
272
300
  arguments: ProviderShared.encodeJson(part.input),
273
301
  };
274
302
  };
275
- const lowerReasoning = (part, providerMetadataKey, extension) => {
303
+ const lowerReasoning = (part, providerMetadataKey) => {
276
304
  const metadata = part.providerMetadata?.[providerMetadataKey];
277
- const id = itemID(part.providerMetadata, providerMetadataKey);
278
- if (!ProviderShared.isRecord(metadata) || !acceptsItemID(extension, "reasoning", id))
305
+ if (!ProviderShared.isRecord(metadata))
279
306
  return undefined;
307
+ const id = itemID(part.providerMetadata, providerMetadataKey);
280
308
  const encryptedContent = typeof metadata.reasoningEncryptedContent === "string" || metadata.reasoningEncryptedContent === null
281
309
  ? metadata.reasoningEncryptedContent
282
310
  : undefined;
283
311
  return {
284
312
  type: "reasoning",
285
- id,
313
+ ...(id === undefined ? {} : { id }),
286
314
  summary: part.text.length > 0 ? [{ type: "summary_text", text: part.text }] : [],
287
315
  encrypted_content: encryptedContent,
288
316
  };
@@ -341,9 +369,7 @@ const lowerToolResultOutput = Effect.fnUntraced(function* (part, request, extens
341
369
  return yield* Effect.forEach(content, (item) => lowerToolResultContentItem(item, request, extension));
342
370
  });
343
371
  const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (request, extension) {
344
- const system = request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }];
345
- const input = [...system];
346
- const store = OpenResponsesOptions.resolve(request).store;
372
+ const input = [];
347
373
  const providerMetadataKey = request.model.route.providerMetadataKey ?? "openresponses";
348
374
  for (const message of request.messages) {
349
375
  if (message.role === "system") {
@@ -363,15 +389,13 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
363
389
  if (message.role === "assistant") {
364
390
  const content = [];
365
391
  const reasoningItems = {};
366
- const reasoningReferences = new Set();
367
- const hostedToolReferences = new Set();
392
+ const hostedToolItems = new Set();
368
393
  const flushText = () => {
369
394
  if (content.length === 0)
370
395
  return;
371
396
  const groups = content.reduce((groups, part) => {
372
397
  const metadata = part.providerMetadata?.[providerMetadataKey];
373
- const rawID = itemID(part.providerMetadata, providerMetadataKey);
374
- const id = acceptsItemID(extension, "message", rawID) ? rawID : undefined;
398
+ const id = itemID(part.providerMetadata, providerMetadataKey);
375
399
  const phase = ProviderShared.isRecord(metadata) ? messagePhase(metadata.phase) : undefined;
376
400
  const group = groups.at(-1);
377
401
  if (group && group.id === id && group.phase === phase)
@@ -396,23 +420,18 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
396
420
  }
397
421
  if (part.type === "reasoning") {
398
422
  flushText();
399
- const reasoning = lowerReasoning(part, providerMetadataKey, extension);
423
+ const reasoning = lowerReasoning(part, providerMetadataKey);
400
424
  if (!reasoning)
401
425
  continue;
402
- if (store !== false) {
403
- if (!reasoningReferences.has(reasoning.id))
404
- input.push({ type: "item_reference", id: reasoning.id });
405
- reasoningReferences.add(reasoning.id);
406
- continue;
407
- }
408
- const existing = reasoningItems[reasoning.id];
426
+ const existing = reasoning.id === undefined ? undefined : reasoningItems[reasoning.id];
409
427
  if (existing) {
410
428
  existing.summary.push(...reasoning.summary);
411
429
  if (typeof reasoning.encrypted_content === "string")
412
430
  existing.encrypted_content = reasoning.encrypted_content;
413
431
  continue;
414
432
  }
415
- reasoningItems[reasoning.id] = reasoning;
433
+ if (reasoning.id !== undefined)
434
+ reasoningItems[reasoning.id] = reasoning;
416
435
  input.push(reasoning);
417
436
  continue;
418
437
  }
@@ -420,28 +439,31 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
420
439
  flushText();
421
440
  if (part.providerExecuted === true)
422
441
  continue;
423
- input.push(lowerToolCall(part, providerMetadataKey, extension));
442
+ input.push(lowerToolCall(part, providerMetadataKey));
424
443
  continue;
425
444
  }
426
445
  if (part.type === "tool-result" && part.providerExecuted === true) {
427
446
  flushText();
428
447
  const id = itemID(part.providerMetadata, providerMetadataKey);
429
- const reference = acceptsItemID(extension, "reference", id) ? id : undefined;
430
- if (store !== false && reference && !hostedToolReferences.has(reference))
431
- input.push({ type: "item_reference", id: reference });
432
- if (store === false) {
433
- // The server is not storing this exchange, so the tool outcome has to
434
- // travel in the input. Non-content results degrade to their text form.
435
- const content = part.result.type === "content"
448
+ const hosted = part.result.type !== "json"
449
+ ? undefined
450
+ : Schema.is(HostedToolItem)(part.result.value)
436
451
  ? part.result.value
437
- : [{ type: "text", text: ProviderShared.toolResultText(part) }];
438
- input.push({
439
- role: "user",
440
- content: yield* Effect.forEach(content, (item) => lowerHostedToolResultContentItem(item, request, extension)),
441
- });
452
+ : extension.lowerHostedToolItem?.(part.result.value);
453
+ if (id !== undefined && hosted?.id === id) {
454
+ if (!hostedToolItems.has(id)) {
455
+ input.push(hosted);
456
+ hostedToolItems.add(id);
457
+ }
458
+ continue;
442
459
  }
443
- if (reference)
444
- hostedToolReferences.add(reference);
460
+ const content = part.result.type === "content"
461
+ ? part.result.value
462
+ : [{ type: "text", text: ProviderShared.toolResultText(part) }];
463
+ input.push({
464
+ role: "user",
465
+ content: yield* Effect.forEach(content, (item) => lowerHostedToolResultContentItem(item, request, extension)),
466
+ });
445
467
  continue;
446
468
  }
447
469
  return yield* ProviderShared.unsupportedContent(extension.name, "assistant", [
@@ -468,10 +490,11 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
468
490
  });
469
491
  const lowerOptions = (request) => {
470
492
  const options = OpenResponsesOptions.resolve(request);
493
+ const instructions = ProviderShared.joinText(request.system);
471
494
  const cacheKey = ProviderShared.promptCacheKey(request);
472
495
  const parallelToolCalls = resolveParallelToolCalls(request);
473
496
  return {
474
- ...(options.instructions ? { instructions: options.instructions } : {}),
497
+ ...(instructions ? { instructions } : {}),
475
498
  ...(options.store !== undefined ? { store: options.store } : {}),
476
499
  ...(options.metadata ? { metadata: options.metadata } : {}),
477
500
  ...(options.safetyIdentifier ? { safety_identifier: options.safetyIdentifier } : {}),
@@ -675,26 +698,24 @@ const onOutputItemAdded = (state, event) => {
675
698
  events,
676
699
  ];
677
700
  }
678
- if (item?.type !== "function_call" || !item.id)
701
+ if (item?.type !== "function_call" || !item.call_id)
679
702
  return [state, NO_EVENTS];
680
- const metadata = providerMetadata(state, { itemId: item.id });
703
+ const id = item.id ?? item.call_id;
704
+ const metadata = item.id ? providerMetadata(state, { itemId: item.id }) : undefined;
681
705
  const events = [];
682
706
  const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
683
707
  return [
684
708
  {
685
709
  ...state,
686
710
  lifecycle,
687
- tools: ToolStream.start(state.tools, item.id, {
688
- id: item.call_id ?? item.id,
711
+ tools: ToolStream.start(state.tools, id, {
712
+ id: item.call_id,
689
713
  name: item.name ?? "",
690
714
  input: item.arguments ?? "",
691
715
  providerMetadata: metadata,
692
716
  }),
693
717
  },
694
- [
695
- ...events,
696
- LLMEvent.toolInputStart({ id: item.call_id ?? item.id, name: item.name ?? "", providerMetadata: metadata }),
697
- ],
718
+ [...events, LLMEvent.toolInputStart({ id: item.call_id, name: item.name ?? "", providerMetadata: metadata })],
698
719
  ];
699
720
  };
700
721
  const onReasoningSummaryPartAdded = (state, event) => {
@@ -733,25 +754,21 @@ const onReasoningSummaryPartDone = (state, event) => {
733
754
  const item = state.reasoningItems[event.item_id];
734
755
  if (!item)
735
756
  return [state, NO_EVENTS];
736
- const events = [];
737
757
  return [
738
758
  {
739
759
  ...state,
740
- lifecycle: state.store !== false
741
- ? Lifecycle.reasoningEnd(state.lifecycle, events, `${event.item_id}:${event.summary_index}`, providerMetadata(state, { itemId: event.item_id }))
742
- : state.lifecycle,
743
760
  reasoningItems: {
744
761
  ...state.reasoningItems,
745
762
  [event.item_id]: {
746
763
  ...item,
747
764
  summaryParts: {
748
765
  ...item.summaryParts,
749
- [event.summary_index]: state.store !== false ? "concluded" : "can-conclude",
766
+ [event.summary_index]: "can-conclude",
750
767
  },
751
768
  },
752
769
  },
753
770
  },
754
- events,
771
+ NO_EVENTS,
755
772
  ];
756
773
  };
757
774
  const onFunctionCallArgumentsDelta = Effect.fn("OpenResponses.onFunctionCallArgumentsDelta")(function* (state, event) {
@@ -801,18 +818,19 @@ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (
801
818
  ];
802
819
  }
803
820
  if (item.type === "function_call") {
804
- if (!item.id || !item.call_id || !item.name)
821
+ if (!item.call_id || !item.name)
805
822
  return [state, NO_EVENTS];
806
- const tools = state.tools[item.id]
823
+ const id = item.id ?? item.call_id;
824
+ const tools = state.tools[id]
807
825
  ? state.tools
808
- : ToolStream.start(state.tools, item.id, {
826
+ : ToolStream.start(state.tools, id, {
809
827
  id: item.call_id,
810
828
  name: item.name,
811
- providerMetadata: providerMetadata(state, { itemId: item.id }),
829
+ providerMetadata: item.id ? providerMetadata(state, { itemId: item.id }) : undefined,
812
830
  });
813
831
  const result = item.arguments === undefined
814
- ? yield* ToolStream.finish(state.id, tools, item.id)
815
- : yield* ToolStream.finishWithInput(state.id, tools, item.id, item.arguments);
832
+ ? yield* ToolStream.finish(state.id, tools, id)
833
+ : yield* ToolStream.finishWithInput(state.id, tools, id, item.arguments);
816
834
  const events = [];
817
835
  const resultEvents = result.events ?? [];
818
836
  const lifecycle = resultEvents.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
@@ -855,9 +873,10 @@ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (
855
873
  const onResponseFinish = Effect.fn("OpenResponses.onResponseFinish")(function* (state, event) {
856
874
  const reconciled = event.type === "response.completed"
857
875
  ? yield* Effect.reduce(event.response?.output ?? [], () => [state, NO_EVENTS], ([current, events], item) => {
858
- if (!item.id ||
859
- ((item.type !== "function_call" || !current.tools[item.id]) &&
860
- (item.type !== "reasoning" || !current.reasoningItems[item.id])))
876
+ const id = item.id ?? (item.type === "function_call" ? item.call_id : undefined);
877
+ if (!id ||
878
+ ((item.type !== "function_call" || !current.tools[id]) &&
879
+ (item.type !== "reasoning" || !current.reasoningItems[id])))
861
880
  return Effect.succeed([current, events]);
862
881
  return onOutputItemDone(current, { type: "response.output_item.done", item }).pipe(Effect.map(([next, emitted]) => [next, [...events, ...emitted]]));
863
882
  })
@@ -967,8 +986,9 @@ export const step = (state, input) => {
967
986
  if (event.type === "response.output_item.added") {
968
987
  if (event.item?.type === "message" && !event.item.id)
969
988
  return ProviderShared.eventError(state.id, `${event.type} message is missing id`);
970
- return Effect.succeed(onOutputItemAdded(event.output_index !== undefined && event.item?.id
971
- ? { ...state, outputItems: { ...state.outputItems, [event.output_index]: event.item.id } }
989
+ const id = event.item?.id ?? (event.item?.type === "function_call" ? event.item.call_id : undefined);
990
+ return Effect.succeed(onOutputItemAdded(event.output_index !== undefined && id
991
+ ? { ...state, outputItems: { ...state.outputItems, [event.output_index]: id } }
972
992
  : state, event));
973
993
  }
974
994
  if (event.type === "response.function_call_arguments.delta" || event.type === "response.function_call_arguments.done")
@@ -1006,7 +1026,6 @@ export const initial = (request, extension = BASE) => ({
1006
1026
  messageItems: new Set(),
1007
1027
  messagePhases: {},
1008
1028
  reasoningItems: {},
1009
- store: OpenResponsesOptions.resolve(request).store,
1010
1029
  });
1011
1030
  export const protocol = Protocol.make({
1012
1031
  id: ADAPTER,
@@ -285,6 +285,7 @@ export interface ParserState {
285
285
  }
286
286
  interface LoweringOptions {
287
287
  readonly cacheControl?: (cache: CacheHint | undefined) => Schema.Schema.Type<typeof OpenAIChatCacheControl> | undefined;
288
+ readonly toolCallID?: (id: string) => string;
288
289
  }
289
290
  export declare const fromRequest: (request: LLMRequest, options?: LoweringOptions | undefined) => Effect.Effect<{
290
291
  reasoning_effort?: import("./utils/open-responses-options.js").ReasoningEffort | undefined;
@@ -192,8 +192,8 @@ const lowerToolChoice = (toolChoice) => ProviderShared.matchToolChoice("OpenAI C
192
192
  required: () => "required",
193
193
  tool: (name) => ({ type: "function", function: { name } }),
194
194
  });
195
- const lowerToolCall = (part) => ({
196
- id: part.id,
195
+ const lowerToolCall = (part, options) => ({
196
+ id: options.toolCallID?.(part.id) ?? part.id,
197
197
  type: "function",
198
198
  function: {
199
199
  name: part.name,
@@ -241,7 +241,7 @@ const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (mes
241
241
  };
242
242
  return { role: "user", content };
243
243
  });
244
- const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(function* (message, configuredField, options = {}) {
244
+ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(function* (message, configuredField, requireReasoning, options) {
245
245
  const content = [];
246
246
  const reasoning = [];
247
247
  const toolCalls = [];
@@ -257,7 +257,7 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
257
257
  continue;
258
258
  }
259
259
  if (part.type === "tool-call") {
260
- toolCalls.push(lowerToolCall(part));
260
+ toolCalls.push(lowerToolCall(part, options));
261
261
  continue;
262
262
  }
263
263
  }
@@ -267,22 +267,22 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
267
267
  const nativeReasoning = openAICompatibleReasoningContent(message.native?.openaiCompatible);
268
268
  const fullyStructured = reasoning.every((part) => Array.isArray(part.providerMetadata?.openai?.reasoningDetails));
269
269
  const field = (() => {
270
- if (configuredField !== undefined)
270
+ if (configuredField !== undefined && (requireReasoning || reasoning.length > 0 || nativeReasoning !== undefined))
271
271
  return configuredField;
272
272
  if (reasoning.length === 0)
273
- return undefined;
273
+ return requireReasoning ? "reasoning_content" : undefined;
274
274
  if (observedField !== undefined)
275
275
  return observedField;
276
276
  if (nativeReasoning !== undefined)
277
277
  return "reasoning_content";
278
- if (!fullyStructured)
278
+ if (!fullyStructured || requireReasoning)
279
279
  return "reasoning_content";
280
280
  })();
281
281
  const reasoningText = (() => {
282
282
  if (configuredField !== undefined)
283
- return reasoning.length === 0 ? (nativeReasoning ?? "") : text;
283
+ return reasoning.length === 0 ? (nativeReasoning ?? (requireReasoning ? "" : undefined)) : text;
284
284
  if (reasoning.length === 0)
285
- return nativeReasoning;
285
+ return nativeReasoning ?? (requireReasoning ? "" : undefined);
286
286
  return text;
287
287
  })();
288
288
  const cached = message.content.findLast((part) => "cache" in part && part.cache !== undefined);
@@ -307,7 +307,7 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m
307
307
  if (part.result.type !== "content") {
308
308
  messages.push({
309
309
  role: "tool",
310
- tool_call_id: part.id,
310
+ tool_call_id: options.toolCallID?.(part.id) ?? part.id,
311
311
  content: ProviderShared.toolResultText(part),
312
312
  cache_control: options.cacheControl?.(part.cache),
313
313
  });
@@ -317,7 +317,7 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m
317
317
  const text = content.filter((item) => item.type === "text").map((item) => item.text);
318
318
  messages.push({
319
319
  role: "tool",
320
- tool_call_id: part.id,
320
+ tool_call_id: options.toolCallID?.(part.id) ?? part.id,
321
321
  content: text.join("\n"),
322
322
  cache_control: options.cacheControl?.(part.cache),
323
323
  });
@@ -326,11 +326,11 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m
326
326
  }
327
327
  return { messages, images };
328
328
  });
329
- const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message, reasoningField, options = {}) {
329
+ const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message, reasoningField, requireReasoning, options) {
330
330
  if (message.role === "user")
331
331
  return [yield* lowerUserMessage(message, options)];
332
332
  if (message.role === "assistant")
333
- return [yield* lowerAssistantMessage(message, reasoningField, options)];
333
+ return [yield* lowerAssistantMessage(message, reasoningField, requireReasoning, options)];
334
334
  return (yield* lowerToolMessages(message, options)).messages;
335
335
  });
336
336
  const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request, options) {
@@ -349,8 +349,27 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
349
349
  ]
350
350
  : [{ role: "system", content: ProviderShared.joinText(request.system) }];
351
351
  const messages = [...system];
352
- const requireAssistantAfterTool = request.model.compatibility?.requireAssistantAfterTool ??
353
- ["mistral", "devstral", "codestral", "pixtral", "mixtral"].some((family) => request.model.id.toLowerCase().includes(family));
352
+ const modelID = request.model.id.toLowerCase();
353
+ const requireReasoning = request.model.compatibility?.requireReasoning ??
354
+ (request.model.compatibility?.reasoningField !== undefined ||
355
+ request.model.provider === "deepseek" ||
356
+ request.model.route.endpoint.baseURL?.toLowerCase().includes("deepseek.com") ||
357
+ modelID.includes("deepseek"));
358
+ const reasoningField = request.model.compatibility?.reasoningField;
359
+ const mistral = ["mistral", "devstral", "codestral", "pixtral", "mixtral"].some((family) => modelID.includes(family));
360
+ const lowering = {
361
+ ...options,
362
+ toolCallID: (id) => {
363
+ if (mistral)
364
+ return id.replace(/[^a-zA-Z0-9]/g, "").slice(0, 9).padEnd(9, "0");
365
+ if (modelID.includes("claude"))
366
+ return id.replace(/[^a-zA-Z0-9_-]/g, "_");
367
+ if (request.model.provider === "openai" || request.model.provider === "azure" || modelID.startsWith("openai/"))
368
+ return id.slice(0, 40);
369
+ return id;
370
+ },
371
+ };
372
+ const requireAssistantAfterTool = request.model.compatibility?.requireAssistantAfterTool ?? mistral;
354
373
  const bridgeTools = () => {
355
374
  if (requireAssistantAfterTool && messages.at(-1)?.role === "tool")
356
375
  messages.push({ role: "assistant", content: "Done." });
@@ -408,13 +427,13 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
408
427
  if (message.role === "assistant" && message.content.every((part) => part.type === "text" && part.text.trim() === ""))
409
428
  continue;
410
429
  if (message.role === "tool") {
411
- const lowered = yield* lowerToolMessages(message, options);
430
+ const lowered = yield* lowerToolMessages(message, lowering);
412
431
  messages.push(...lowered.messages);
413
432
  pendingImages.push(...lowered.images);
414
433
  continue;
415
434
  }
416
435
  flushImages();
417
- messages.push(...(yield* lowerMessage(message, request.model.compatibility?.reasoningField, options)));
436
+ messages.push(...(yield* lowerMessage(message, reasoningField, requireReasoning, lowering)));
418
437
  }
419
438
  flushImages();
420
439
  return messages;
@@ -7,6 +7,43 @@ export type OpenAICompatibleResponsesLanguageModelInput = RouteRoutedLanguageMod
7
7
  */
8
8
  export declare const route: Route<{
9
9
  readonly input: readonly ({
10
+ readonly [x: string]: unknown;
11
+ readonly id: string;
12
+ readonly type: "web_search_call";
13
+ readonly status?: string | undefined;
14
+ readonly action?: {
15
+ readonly [x: string]: unknown;
16
+ } | null | undefined;
17
+ } | {
18
+ readonly [x: string]: unknown;
19
+ readonly id: string;
20
+ readonly type: "file_search_call";
21
+ readonly status?: string | undefined;
22
+ readonly queries?: readonly string[] | undefined;
23
+ readonly results?: readonly {
24
+ readonly [x: string]: unknown;
25
+ }[] | null | undefined;
26
+ } | {
27
+ readonly [x: string]: unknown;
28
+ readonly id: string;
29
+ readonly type: "code_interpreter_call";
30
+ readonly status?: string | undefined;
31
+ readonly code?: string | null | undefined;
32
+ readonly container_id?: string | null | undefined;
33
+ readonly outputs?: readonly {
34
+ readonly [x: string]: unknown;
35
+ }[] | null | undefined;
36
+ } | {
37
+ readonly [x: string]: unknown;
38
+ readonly id: string;
39
+ readonly type: "mcp_call";
40
+ readonly status?: string | undefined;
41
+ readonly name?: string | undefined;
42
+ readonly output?: string | null | undefined;
43
+ readonly error?: unknown;
44
+ readonly arguments?: string | undefined;
45
+ readonly server_label?: string | undefined;
46
+ } | {
10
47
  readonly type: "reasoning";
11
48
  readonly summary: readonly {
12
49
  readonly type: "summary_text";
@@ -14,9 +51,6 @@ export declare const route: Route<{
14
51
  }[];
15
52
  readonly id?: string | undefined;
16
53
  readonly encrypted_content?: string | null | undefined;
17
- } | {
18
- readonly type: "item_reference";
19
- readonly id: string;
20
54
  } | {
21
55
  readonly role: "system";
22
56
  readonly content: string;