@yourgpt/llm-sdk 2.6.0-beta.0 → 2.6.0-beta.2

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 (64) hide show
  1. package/README.md +189 -0
  2. package/dist/adapters/index.d.mts +11 -13
  3. package/dist/adapters/index.d.ts +11 -13
  4. package/dist/adapters/index.js +610 -186
  5. package/dist/adapters/index.mjs +610 -186
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-tNgbBaSo.d.mts → base-BICaQaRS.d.mts} +8 -3
  9. package/dist/{base-C58Dsr9p.d.ts → base-CpRPB8u8.d.ts} +8 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/fallback/index.js +75 -39
  13. package/dist/fallback/index.mjs +75 -39
  14. package/dist/index.d.mts +18 -35
  15. package/dist/index.d.ts +18 -35
  16. package/dist/index.js +304 -88
  17. package/dist/index.mjs +302 -89
  18. package/dist/providers/anthropic/index.d.mts +3 -3
  19. package/dist/providers/anthropic/index.d.ts +3 -3
  20. package/dist/providers/anthropic/index.js +123 -7
  21. package/dist/providers/anthropic/index.mjs +123 -7
  22. package/dist/providers/azure/index.d.mts +3 -3
  23. package/dist/providers/azure/index.d.ts +3 -3
  24. package/dist/providers/azure/index.js +101 -4
  25. package/dist/providers/azure/index.mjs +101 -4
  26. package/dist/providers/fireworks/index.d.mts +3 -3
  27. package/dist/providers/fireworks/index.d.ts +3 -3
  28. package/dist/providers/fireworks/index.js +559 -178
  29. package/dist/providers/fireworks/index.mjs +559 -178
  30. package/dist/providers/google/index.d.mts +3 -3
  31. package/dist/providers/google/index.d.ts +3 -3
  32. package/dist/providers/google/index.js +559 -178
  33. package/dist/providers/google/index.mjs +559 -178
  34. package/dist/providers/ollama/index.d.mts +4 -4
  35. package/dist/providers/ollama/index.d.ts +4 -4
  36. package/dist/providers/ollama/index.js +87 -2
  37. package/dist/providers/ollama/index.mjs +87 -2
  38. package/dist/providers/openai/index.d.mts +25 -23
  39. package/dist/providers/openai/index.d.ts +25 -23
  40. package/dist/providers/openai/index.js +1397 -481
  41. package/dist/providers/openai/index.mjs +1396 -482
  42. package/dist/providers/openrouter/index.d.mts +3 -3
  43. package/dist/providers/openrouter/index.d.ts +3 -3
  44. package/dist/providers/openrouter/index.js +559 -178
  45. package/dist/providers/openrouter/index.mjs +559 -178
  46. package/dist/providers/togetherai/index.d.mts +3 -3
  47. package/dist/providers/togetherai/index.d.ts +3 -3
  48. package/dist/providers/togetherai/index.js +559 -178
  49. package/dist/providers/togetherai/index.mjs +559 -178
  50. package/dist/providers/xai/index.d.mts +3 -3
  51. package/dist/providers/xai/index.d.ts +3 -3
  52. package/dist/providers/xai/index.js +559 -178
  53. package/dist/providers/xai/index.mjs +559 -178
  54. package/dist/{types-B6dhnguR.d.mts → types-C3RgRbEC.d.mts} +11 -4
  55. package/dist/{types-CCxPmkmK.d.ts → types-DCFqkJAy.d.ts} +11 -4
  56. package/dist/{types-BQ31QIsA.d.ts → types-DHQEMFHA.d.mts} +3 -11
  57. package/dist/{types-BkQCSiIt.d.mts → types-Dg1Bcg1k.d.mts} +127 -3
  58. package/dist/{types-BkQCSiIt.d.ts → types-Dg1Bcg1k.d.ts} +127 -3
  59. package/dist/{types-BSSiJW2o.d.mts → types-Dikj3En4.d.ts} +3 -11
  60. package/dist/yourgpt/index.d.mts +1 -1
  61. package/dist/yourgpt/index.d.ts +1 -1
  62. package/docs/multimodal-and-audio.md +170 -0
  63. package/package.json +117 -49
  64. package/LICENSE +0 -21
@@ -1,5 +1,159 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/tool-output.ts
4
+ var imageTypes = /* @__PURE__ */ new Set([
5
+ "image/png",
6
+ "image/jpeg",
7
+ "image/webp",
8
+ "image/gif"
9
+ ]);
10
+ function normalizeToolOutput(value) {
11
+ const output = value;
12
+ if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
13
+ throw new Error("Tool model output must contain a nonempty content array");
14
+ }
15
+ return {
16
+ type: "content",
17
+ content: output.content.map((part) => {
18
+ if (part?.type === "text" && typeof part.text === "string")
19
+ return { type: "text", text: part.text };
20
+ if (part?.type !== "image")
21
+ throw new Error("Unsupported tool output content type");
22
+ const sources = ["data", "url", "fileId"].filter((key) => key in part);
23
+ if (sources.length !== 1)
24
+ throw new Error(
25
+ "Tool image requires exactly one of data, url, or fileId"
26
+ );
27
+ if ("fileId" in part) {
28
+ if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
29
+ throw new Error(
30
+ "Tool image file references must name the openai provider and fileId"
31
+ );
32
+ return { type: "image", fileId: part.fileId, provider: "openai" };
33
+ }
34
+ if ("url" in part) {
35
+ let url;
36
+ try {
37
+ url = new URL(part.url);
38
+ } catch {
39
+ throw new Error("Invalid tool image URL");
40
+ }
41
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
42
+ throw new Error(
43
+ "Tool image URL must use HTTP(S) without credentials"
44
+ );
45
+ return { type: "image", url: url.href };
46
+ }
47
+ if (!("data" in part) || !imageTypes.has(part.mediaType))
48
+ throw new Error("Unsupported tool image media type");
49
+ let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
50
+ if (typeof data !== "string")
51
+ throw new Error("Tool image data must be base64 or bytes");
52
+ if (data.startsWith("data:")) {
53
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
54
+ if (!match || match[1] !== part.mediaType)
55
+ throw new Error(
56
+ "Tool image data URL media type does not match mediaType"
57
+ );
58
+ data = match[2];
59
+ }
60
+ if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
61
+ throw new Error("Invalid tool image base64 data");
62
+ if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
63
+ throw new Error("Tool image exceeds 20 MiB");
64
+ if (Buffer.from(data, "base64").toString("base64") !== data)
65
+ throw new Error("Invalid tool image base64 data");
66
+ return { type: "image", data, mediaType: part.mediaType };
67
+ })
68
+ };
69
+ }
70
+ function toolOutputForProvider(value, provider) {
71
+ const output = normalizeToolOutput(value);
72
+ return output.content.map((part) => {
73
+ if (part.type === "text")
74
+ return {
75
+ type: provider === "responses" ? "input_text" : "text",
76
+ text: part.text
77
+ };
78
+ if (provider === "unsupported")
79
+ throw new Error(
80
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
81
+ );
82
+ if ("fileId" in part) {
83
+ if (provider !== "responses")
84
+ throw new Error(
85
+ "OpenAI image file references cannot be sent to another provider"
86
+ );
87
+ return { type: "input_image", file_id: part.fileId, detail: "auto" };
88
+ }
89
+ if (provider === "responses")
90
+ return {
91
+ type: "input_image",
92
+ image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
93
+ detail: "auto"
94
+ };
95
+ return {
96
+ type: "image",
97
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
98
+ };
99
+ });
100
+ }
101
+ function textToolOutput(value, fallback) {
102
+ if (value === void 0) return fallback;
103
+ return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
104
+ }
105
+
106
+ // src/core/openai-parameters.ts
107
+ function capabilities(model) {
108
+ const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
109
+ const major = Number(gpt?.[1] ?? 0);
110
+ const minor = Number(gpt?.[2] ?? 0);
111
+ const chat = gpt?.[3]?.startsWith("chat") ?? false;
112
+ const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
113
+ const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
114
+ return { major, minor, reasoning, supportsNone };
115
+ }
116
+ function requiresOpenAIResponses(model, baseUrl, options = {}) {
117
+ if (options.parameterPolicy === "passthrough") return false;
118
+ const c = capabilities(model);
119
+ if (!c.reasoning || options.reasoningEffort === "none") return false;
120
+ if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
121
+ if (!baseUrl) return true;
122
+ try {
123
+ return new URL(baseUrl).hostname === "api.openai.com";
124
+ } catch {
125
+ return false;
126
+ }
127
+ }
128
+ function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
129
+ const c = capabilities(model ?? "");
130
+ const normalize = options.parameterPolicy !== "passthrough";
131
+ const effort = options.reasoningEffort;
132
+ if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
133
+ throw new Error(`${model} does not support reasoningEffort: none`);
134
+ }
135
+ if (normalize && c.major >= 6 && effort === "minimal") {
136
+ throw new Error(`${model} does not support reasoningEffort: minimal`);
137
+ }
138
+ const result = {
139
+ [api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
140
+ };
141
+ if (effort !== void 0) {
142
+ if (api === "responses") result.reasoning = { effort };
143
+ else result.reasoning_effort = effort;
144
+ }
145
+ if (temperature !== void 0) {
146
+ if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
147
+ result.temperature = temperature;
148
+ } else {
149
+ (options.onWarning ?? console.warn)(
150
+ `[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
151
+ );
152
+ }
153
+ }
154
+ return result;
155
+ }
156
+
3
157
  // src/adapters/base.ts
4
158
  function stringifyForDebug(value) {
5
159
  return JSON.stringify(
@@ -119,16 +273,6 @@ function normalizeObjectJsonSchema(schema) {
119
273
  }
120
274
  return normalized;
121
275
  }
122
- function isOpenAIReasoningModel(modelId) {
123
- if (!modelId) return false;
124
- return /^(o1|o3|o4|gpt-5)/i.test(modelId);
125
- }
126
- function buildOpenAITokenParams(modelId, maxTokens, temperature) {
127
- if (isOpenAIReasoningModel(modelId)) {
128
- return { max_completion_tokens: maxTokens };
129
- }
130
- return { max_tokens: maxTokens, temperature };
131
- }
132
276
  function stripSchemaKeys(schema, keysToDrop, options = {}) {
133
277
  if (Array.isArray(schema)) {
134
278
  return schema.map((item) => stripSchemaKeys(item, keysToDrop, options));
@@ -381,7 +525,27 @@ function messageToOpenAIContent(message) {
381
525
  }
382
526
  return blocks;
383
527
  }
528
+ function extractAnthropicSystemPrompt(messages, systemPrompt) {
529
+ const parts = systemPrompt ? [systemPrompt] : [];
530
+ for (const message of messages) {
531
+ if (message.role !== "system") continue;
532
+ if (typeof message.content === "string") {
533
+ if (message.content) parts.push(message.content);
534
+ } else if (Array.isArray(message.content)) {
535
+ for (const block of message.content) {
536
+ if (block?.type !== "text" || typeof block.text !== "string") {
537
+ throw new TypeError("Anthropic system messages must contain text.");
538
+ }
539
+ if (block.text) parts.push(block.text);
540
+ }
541
+ } else {
542
+ throw new TypeError("Anthropic system messages must contain text.");
543
+ }
544
+ }
545
+ return parts.join("\n");
546
+ }
384
547
  function formatMessagesForAnthropic(messages, systemPrompt) {
548
+ const system = extractAnthropicSystemPrompt(messages, systemPrompt);
385
549
  const formatted = [];
386
550
  for (let i = 0; i < messages.length; i++) {
387
551
  const msg = messages[i];
@@ -410,7 +574,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
410
574
  {
411
575
  type: "tool_result",
412
576
  tool_use_id: msg.tool_call_id,
413
- content: msg.content ?? ""
577
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
414
578
  }
415
579
  ];
416
580
  while (i + 1 < messages.length && messages[i + 1].role === "tool") {
@@ -420,7 +584,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
420
584
  toolResults.push({
421
585
  type: "tool_result",
422
586
  tool_use_id: nextTool.tool_call_id,
423
- content: nextTool.content ?? ""
587
+ content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
424
588
  });
425
589
  }
426
590
  }
@@ -436,11 +600,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
436
600
  }
437
601
  }
438
602
  return {
439
- system: systemPrompt || "",
603
+ system,
440
604
  messages: formatted
441
605
  };
442
606
  }
443
- function formatMessagesForOpenAI(messages, systemPrompt) {
607
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
444
608
  const formatted = [];
445
609
  if (systemPrompt) {
446
610
  formatted.push({ role: "system", content: systemPrompt });
@@ -458,7 +622,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
458
622
  const assistantMsg = {
459
623
  role: "assistant",
460
624
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
461
- content: hasToolCalls ? msg.content || null : msg.content
625
+ content: hasToolCalls ? msg.content || null : msg.content,
626
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
462
627
  };
463
628
  if (hasToolCalls) {
464
629
  assistantMsg.tool_calls = msg.tool_calls;
@@ -467,7 +632,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
467
632
  } else if (msg.role === "tool" && msg.tool_call_id) {
468
633
  formatted.push({
469
634
  role: "tool",
470
- content: msg.content ?? "",
635
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
636
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
471
637
  tool_call_id: msg.tool_call_id
472
638
  });
473
639
  }
@@ -475,6 +641,86 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
475
641
  return formatted;
476
642
  }
477
643
 
644
+ // src/core/openai-responses.ts
645
+ function resolveOpenAITransport(defaults, overrides) {
646
+ return {
647
+ api: overrides?.api ?? defaults.api ?? "auto",
648
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
649
+ };
650
+ }
651
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
652
+ const api = transport.api ?? "auto";
653
+ if (!["auto", "chat", "responses"].includes(api)) {
654
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
655
+ }
656
+ if (api === "chat") {
657
+ if (transport.responses !== void 0 || nativeToolSearch) {
658
+ throw new Error(
659
+ "Responses options and native tool search require the Responses API"
660
+ );
661
+ }
662
+ return false;
663
+ }
664
+ let officialEndpoint = !baseURL;
665
+ try {
666
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
667
+ } catch {
668
+ }
669
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
670
+ }
671
+ function responseMetadata(response) {
672
+ const output = Array.isArray(response?.output) ? response.output : [];
673
+ return {
674
+ openai: {
675
+ response: {
676
+ id: response?.id,
677
+ model: response?.model,
678
+ status: response?.status,
679
+ output,
680
+ usage: response?.usage,
681
+ store: response?.store,
682
+ refusals: output.flatMap(
683
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
684
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
685
+ ).map((part) => part.refusal) : []
686
+ )
687
+ }
688
+ }
689
+ };
690
+ }
691
+ function getResponseReplay(metadata) {
692
+ const response = metadata?.openai?.response;
693
+ if (response === void 0) return void 0;
694
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
695
+ throw new Error(
696
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
697
+ );
698
+ }
699
+ return response.output;
700
+ }
701
+
702
+ // src/core/provider-error.ts
703
+ function providerErrorEvent(error, code) {
704
+ const details = error ?? {};
705
+ const event = {
706
+ type: "error",
707
+ message: error instanceof Error ? error.message : String(error),
708
+ code,
709
+ ...typeof details.status === "number" ? { status: details.status } : {},
710
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
711
+ ...typeof details.param === "string" ? { param: details.param } : {},
712
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
713
+ };
714
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
715
+ return event;
716
+ }
717
+ function throwIfAborted(signal) {
718
+ if (!signal?.aborted) return;
719
+ const error = new Error("The operation was aborted");
720
+ error.name = "AbortError";
721
+ throw error;
722
+ }
723
+
478
724
  // src/core/utils.ts
479
725
  function generateId(prefix = "id") {
480
726
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -506,39 +752,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
506
752
  const { default: OpenAI } = await import('openai');
507
753
  this.client = new OpenAI({
508
754
  apiKey: this.config.apiKey,
509
- baseURL: this.config.baseUrl
755
+ baseURL: this.config.baseUrl,
756
+ organization: this.config.organization,
757
+ defaultHeaders: this.config.headers
510
758
  });
511
759
  }
512
760
  return this.client;
513
761
  }
762
+ parameterOptions(request) {
763
+ return {
764
+ ...this.config,
765
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
766
+ };
767
+ }
768
+ toolDefinitions(request) {
769
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
770
+ return formatTools(request.actions ?? []).map((tool) => ({
771
+ name: tool.function.name,
772
+ description: tool.function.description ?? "",
773
+ location: "server",
774
+ inputSchema: tool.function.parameters
775
+ }));
776
+ }
514
777
  shouldUseResponsesApi(request) {
515
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
516
- if (!hasTools) return false;
517
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
518
- return true;
519
- }
520
- return this.requiresResponsesApiForTools(
521
- request.config?.model || this.model
778
+ return useOpenAIResponses(
779
+ request.config?.model || this.model,
780
+ this.config.baseUrl,
781
+ this.parameterOptions(request),
782
+ resolveOpenAITransport(this.config, request.config?.openai),
783
+ this.toolDefinitions(request).length > 0,
784
+ !!(request.rawMessages ?? request.messages)?.some(
785
+ (message) => message.providerMetadata?.openai?.response
786
+ ),
787
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
522
788
  );
523
789
  }
524
- /**
525
- * Whether this model rejects function tools on /v1/chat/completions.
526
- *
527
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
528
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
529
- * restriction and may not implement /v1/responses at all.
530
- */
531
- requiresResponsesApiForTools(modelId) {
532
- if (!modelId) return false;
533
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
534
- const baseUrl = this.config.baseUrl;
535
- if (!baseUrl) return true;
536
- try {
537
- return new URL(baseUrl).hostname === "api.openai.com";
538
- } catch {
539
- return false;
540
- }
541
- }
542
790
  /**
543
791
  * Convert one message's content to Responses content parts.
544
792
  *
@@ -569,11 +817,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
569
817
  });
570
818
  }
571
819
  buildResponsesInput(request) {
572
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
820
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
573
821
  const input = [];
574
822
  for (const message of sourceMessages) {
575
823
  if (message.role === "system") {
576
- if (!request.systemPrompt && message.content) {
824
+ if (message.content && message.content !== request.systemPrompt) {
577
825
  input.push({
578
826
  type: "message",
579
827
  role: "developer",
@@ -583,6 +831,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
583
831
  continue;
584
832
  }
585
833
  if (message.role === "assistant") {
834
+ const replay = getResponseReplay(message.providerMetadata);
835
+ if (replay) {
836
+ input.push(...replay);
837
+ continue;
838
+ }
586
839
  const content = this.toResponsesContent(message.content, "assistant");
587
840
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
588
841
  input.push({
@@ -606,7 +859,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
606
859
  input.push({
607
860
  type: "function_call_output",
608
861
  call_id: message.tool_call_id,
609
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
862
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
610
863
  });
611
864
  continue;
612
865
  }
@@ -619,6 +872,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
619
872
  }
620
873
  return input;
621
874
  }
875
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
876
+ formatRawMessages(messages, preserveProviderMetadata = false) {
877
+ return messages.map((message) => {
878
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
879
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
880
+ if (preserveProviderMetadata && modelOutput)
881
+ rest.modelOutput = modelOutput;
882
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
883
+ content = null;
884
+ }
885
+ if (Array.isArray(attachments) && attachments.length > 0) {
886
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
887
+ (attachment) => attachmentToOpenAIImage(attachment)
888
+ ).filter((image) => image !== null);
889
+ if (images.length > 0) {
890
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
891
+ content = [...parts, ...images];
892
+ }
893
+ }
894
+ return {
895
+ ...rest,
896
+ content,
897
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
898
+ };
899
+ });
900
+ }
622
901
  /**
623
902
  * Build the web_search tool for the Responses API, or undefined when web
624
903
  * search is off.
@@ -697,20 +976,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
697
976
  ).map((part) => part.text) : []
698
977
  ).join("") : "";
699
978
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
700
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
701
- id: item.call_id ?? item.id ?? generateToolCallId(),
702
- name: item.name,
703
- args: (() => {
704
- try {
705
- return JSON.parse(item.arguments ?? "{}");
706
- } catch {
707
- return {};
708
- }
709
- })()
710
- })) : [];
979
+ const seenCallIds = /* @__PURE__ */ new Set();
980
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
981
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
982
+ throw new Error("Responses contains an incomplete function call");
983
+ }
984
+ if (seenCallIds.has(item.call_id))
985
+ throw new Error("Responses contains duplicate function call IDs");
986
+ seenCallIds.add(item.call_id);
987
+ const args = JSON.parse(item.arguments);
988
+ if (!args || typeof args !== "object" || Array.isArray(args))
989
+ throw new Error(
990
+ "Responses function arguments must be a JSON object"
991
+ );
992
+ return { id: item.call_id, name: item.name, args };
993
+ }) : [];
711
994
  return {
712
995
  content,
713
996
  toolCalls,
997
+ providerMetadata: responseMetadata(response),
714
998
  usage: response?.usage ? {
715
999
  promptTokens: response.usage.input_tokens ?? 0,
716
1000
  completionTokens: response.usage.output_tokens ?? 0,
@@ -721,6 +1005,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
721
1005
  }
722
1006
  buildResponsesPayload(request, stream) {
723
1007
  const openaiToolOptions = request.providerToolOptions?.openai;
1008
+ const options = resolveOpenAITransport(
1009
+ this.config,
1010
+ request.config?.openai
1011
+ ).responses;
1012
+ const parameters = buildOpenAIParameters(
1013
+ request.config?.model || this.model,
1014
+ request.config?.maxTokens ?? this.config.maxTokens,
1015
+ request.config?.temperature ?? this.config.temperature,
1016
+ this.parameterOptions(request),
1017
+ "responses"
1018
+ );
724
1019
  const responsesTextFormat = toOpenAIResponsesTextFormat(
725
1020
  request.config?.responseFormat
726
1021
  );
@@ -730,18 +1025,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
730
1025
  instructions: request.systemPrompt,
731
1026
  input: this.buildResponsesInput(request),
732
1027
  tools: this.buildResponsesTools(
733
- request.toolDefinitions ?? [],
1028
+ this.toolDefinitions(request),
734
1029
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
735
1030
  this.buildResponsesWebSearchTool(request)
736
1031
  ),
737
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
1032
+ tool_choice: openaiToolOptions?.toolChoice,
738
1033
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
739
- // Reasoning models reject `temperature` outright — sending it would swap one
740
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
741
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
742
- temperature: request.config?.temperature ?? this.config.temperature
743
- },
744
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
1034
+ ...parameters,
1035
+ ...options?.reasoningSummary ? {
1036
+ reasoning: {
1037
+ ...parameters.reasoning,
1038
+ summary: options.reasoningSummary
1039
+ }
1040
+ } : {},
1041
+ ...options?.store !== void 0 ? { store: options.store } : {},
1042
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1043
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1044
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1045
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1046
+ ...options?.store === false || options?.include ? {
1047
+ include: [
1048
+ .../* @__PURE__ */ new Set([
1049
+ ...options?.include ?? [],
1050
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1051
+ ])
1052
+ ]
1053
+ } : {},
745
1054
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
746
1055
  stream
747
1056
  };
@@ -749,10 +1058,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
749
1058
  }
750
1059
  async completeWithResponses(request) {
751
1060
  const client = await this.getClient();
1061
+ throwIfAborted(request.signal);
752
1062
  const payload = this.buildResponsesPayload(request, false);
753
1063
  logProviderPayload("openai", "request payload", payload, request.debug);
754
- const response = await client.responses.create(payload);
1064
+ if (!client.responses?.create)
1065
+ throw new Error(
1066
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1067
+ );
1068
+ const response = await client.responses.create(payload, {
1069
+ signal: request.signal
1070
+ });
1071
+ throwIfAborted(request.signal);
755
1072
  logProviderPayload("openai", "response payload", response, request.debug);
1073
+ if (response.status && response.status !== "completed") {
1074
+ throw Object.assign(
1075
+ new Error(
1076
+ response.error?.message ?? `Responses request ${response.status}`
1077
+ ),
1078
+ response.error
1079
+ );
1080
+ }
756
1081
  return this.parseResponsesResult(response);
757
1082
  }
758
1083
  /**
@@ -770,13 +1095,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
770
1095
  */
771
1096
  async *streamWithResponses(request) {
772
1097
  const client = await this.getClient();
1098
+ throwIfAborted(request.signal);
773
1099
  const payload = this.buildResponsesPayload(request, true);
774
1100
  logProviderPayload("openai", "request payload", payload, request.debug);
775
- const stream = await client.responses.create(payload);
1101
+ if (!client.responses?.create)
1102
+ throw new Error(
1103
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1104
+ );
1105
+ const stream = await client.responses.create(payload, {
1106
+ signal: request.signal
1107
+ });
776
1108
  const toolCalls = /* @__PURE__ */ new Map();
777
1109
  let usage;
778
1110
  const citations = [];
1111
+ let completed = false;
1112
+ let terminalResponse;
1113
+ let responseIdentity = {};
1114
+ const outputItems = /* @__PURE__ */ new Map();
1115
+ let streamedText = "";
779
1116
  function* flushToolCalls() {
1117
+ for (const call of toolCalls.values()) {
1118
+ if (!call.name)
1119
+ throw new Error("Responses tool call is missing its name");
1120
+ JSON.parse(call.arguments || "{}");
1121
+ }
780
1122
  for (const call of toolCalls.values()) {
781
1123
  yield { type: "action:start", id: call.id, name: call.name };
782
1124
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -785,19 +1127,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
785
1127
  toolCalls.clear();
786
1128
  }
787
1129
  for await (const event of stream) {
788
- if (request.signal?.aborted) break;
1130
+ throwIfAborted(request.signal);
789
1131
  logProviderPayload("openai", "stream chunk", event, request.debug);
1132
+ if (completed)
1133
+ throw new Error("Responses stream emitted an event after completion");
790
1134
  const index = event?.output_index ?? 0;
791
1135
  switch (event?.type) {
1136
+ case "response.created":
1137
+ responseIdentity = {
1138
+ id: event.response?.id,
1139
+ model: event.response?.model
1140
+ };
1141
+ break;
1142
+ case "response.output_item.done":
1143
+ if (event.item) outputItems.set(index, event.item);
1144
+ if (event.item?.type === "function_call") {
1145
+ toolCalls.set(index, {
1146
+ id: event.item.call_id,
1147
+ name: event.item.name,
1148
+ arguments: event.item.arguments
1149
+ });
1150
+ }
1151
+ break;
792
1152
  case "response.output_text.delta":
793
1153
  if (event.delta) {
1154
+ streamedText += event.delta;
794
1155
  yield { type: "message:delta", content: event.delta };
795
1156
  }
796
1157
  break;
797
1158
  case "response.output_item.added":
798
1159
  if (event.item?.type === "function_call") {
799
1160
  toolCalls.set(index, {
800
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1161
+ id: event.item.call_id ?? "",
801
1162
  name: event.item.name ?? "",
802
1163
  arguments: event.item.arguments ?? ""
803
1164
  });
@@ -815,8 +1176,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
815
1176
  }
816
1177
  break;
817
1178
  }
818
- case "response.completed":
819
- case "response.incomplete": {
1179
+ case "response.completed": {
1180
+ if (event.response?.status && event.response.status !== "completed")
1181
+ throw new Error(`Responses stream ${event.response.status}`);
1182
+ completed = true;
1183
+ terminalResponse = { ...responseIdentity, ...event.response };
820
1184
  const found = this.collectResponsesCitations(
821
1185
  event.response?.output,
822
1186
  citations.length
@@ -832,21 +1196,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
832
1196
  }
833
1197
  break;
834
1198
  }
835
- case "error":
836
- case "response.failed":
1199
+ case "response.incomplete":
837
1200
  throw new Error(
838
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
1201
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
839
1202
  );
1203
+ case "error":
1204
+ case "response.failed": {
1205
+ const details = event.response?.error ?? event;
1206
+ throw Object.assign(
1207
+ new Error(details.message ?? "Responses stream failed"),
1208
+ {
1209
+ code: details.code,
1210
+ param: details.param,
1211
+ status: details.status
1212
+ }
1213
+ );
1214
+ }
840
1215
  }
841
1216
  }
1217
+ throwIfAborted(request.signal);
1218
+ if (!completed)
1219
+ throw new Error("Responses stream ended without a completed response");
1220
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1221
+ if (output.some((item) => item.type === "function_call"))
1222
+ toolCalls.clear();
1223
+ for (const [index, item] of output.entries()) {
1224
+ if (item.type === "function_call") {
1225
+ toolCalls.set(index, {
1226
+ id: item.call_id,
1227
+ name: item.name,
1228
+ arguments: item.arguments
1229
+ });
1230
+ }
1231
+ }
1232
+ const uniqueCalls = /* @__PURE__ */ new Map();
1233
+ for (const call of toolCalls.values()) {
1234
+ if (!call.id || !call.name)
1235
+ throw new Error("Responses stream contains an incomplete tool call");
1236
+ uniqueCalls.set(call.id, call);
1237
+ }
1238
+ toolCalls.clear();
1239
+ [...uniqueCalls.values()].forEach(
1240
+ (call, index) => toolCalls.set(index, call)
1241
+ );
1242
+ if (!output.length && toolCalls.size) {
1243
+ for (const call of toolCalls.values())
1244
+ output.push({
1245
+ type: "function_call",
1246
+ call_id: call.id,
1247
+ name: call.name,
1248
+ arguments: call.arguments
1249
+ });
1250
+ }
1251
+ if (!output.length && streamedText)
1252
+ output.push({
1253
+ type: "message",
1254
+ role: "assistant",
1255
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1256
+ });
1257
+ terminalResponse.output = output;
1258
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1259
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1260
+ yield {
1261
+ type: "message:delta",
1262
+ content: finalText.slice(streamedText.length)
1263
+ };
1264
+ }
842
1265
  yield* flushToolCalls();
843
1266
  if (citations.length > 0) {
844
1267
  yield { type: "citation", citations: deduplicateCitations(citations) };
845
1268
  }
846
- yield { type: "message:end" };
847
- yield { type: "done", usage };
1269
+ yield {
1270
+ type: "message:end",
1271
+ providerMetadata: responseMetadata(terminalResponse)
1272
+ };
1273
+ yield {
1274
+ type: "done",
1275
+ usage,
1276
+ providerMetadata: responseMetadata(terminalResponse)
1277
+ };
848
1278
  }
849
1279
  async *stream(request) {
1280
+ throwIfAborted(request.signal);
850
1281
  if (this.shouldUseResponsesApi(request)) {
851
1282
  const messageId2 = generateMessageId();
852
1283
  yield { type: "message:start", id: messageId2 };
@@ -854,47 +1285,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
854
1285
  yield* this.streamWithResponses(request);
855
1286
  return;
856
1287
  } catch (error) {
857
- yield {
858
- type: "error",
859
- message: error instanceof Error ? error.message : "Unknown error",
860
- code: "OPENAI_RESPONSES_ERROR"
861
- };
1288
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
862
1289
  return;
863
1290
  }
864
1291
  }
865
1292
  const client = await this.getClient();
866
1293
  let messages;
867
1294
  if (request.rawMessages && request.rawMessages.length > 0) {
868
- const processedMessages = request.rawMessages.map((msg) => {
869
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
870
- return { ...msg, content: null };
871
- }
872
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
873
- if (hasAttachments) {
874
- const content = [];
875
- if (msg.content) {
876
- content.push({ type: "text", text: msg.content });
877
- }
878
- for (const attachment of msg.attachments) {
879
- if (attachment.type === "image") {
880
- let imageUrl;
881
- if (attachment.url) {
882
- imageUrl = attachment.url;
883
- } else if (attachment.data) {
884
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
885
- } else {
886
- continue;
887
- }
888
- content.push({
889
- type: "image_url",
890
- image_url: { url: imageUrl, detail: "auto" }
891
- });
892
- }
893
- }
894
- return { ...msg, content, attachments: void 0 };
895
- }
896
- return msg;
897
- });
1295
+ const processedMessages = this.formatRawMessages(request.rawMessages);
898
1296
  if (request.systemPrompt) {
899
1297
  const hasSystem = processedMessages.some((m) => m.role === "system");
900
1298
  if (!hasSystem) {
@@ -914,7 +1312,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
914
1312
  request.systemPrompt
915
1313
  );
916
1314
  }
917
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1315
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1316
+ type: "function",
1317
+ function: {
1318
+ name: tool.name,
1319
+ description: tool.description,
1320
+ parameters: normalizeObjectJsonSchema(
1321
+ tool.inputSchema
1322
+ )
1323
+ }
1324
+ })) : formatTools(request.actions ?? []);
918
1325
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
919
1326
  if (webSearchConfig) {
920
1327
  const webSearchTool = {
@@ -943,26 +1350,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
943
1350
  tools: tools.length > 0 ? tools : void 0,
944
1351
  tool_choice: tools.length > 0 ? toolChoice : void 0,
945
1352
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
946
- ...buildOpenAITokenParams(
1353
+ ...buildOpenAIParameters(
947
1354
  modelIdForPayload,
948
1355
  request.config?.maxTokens ?? this.config.maxTokens,
949
- request.config?.temperature ?? this.config.temperature
1356
+ request.config?.temperature ?? this.config.temperature,
1357
+ this.parameterOptions(request)
950
1358
  ),
951
1359
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
952
1360
  stream: true,
953
1361
  stream_options: { include_usage: true }
954
1362
  };
955
1363
  logProviderPayload("openai", "request payload", payload, request.debug);
956
- const stream = await client.chat.completions.create(payload);
957
- let currentToolCall = null;
1364
+ const stream = await client.chat.completions.create(payload, {
1365
+ signal: request.signal
1366
+ });
1367
+ const toolCalls = /* @__PURE__ */ new Map();
1368
+ let finishReason;
958
1369
  const collectedCitations = [];
959
1370
  let citationIndex = 0;
960
1371
  let usage;
961
1372
  for await (const chunk of stream) {
962
1373
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
963
- if (request.signal?.aborted) {
964
- break;
965
- }
1374
+ throwIfAborted(request.signal);
966
1375
  const delta = chunk.choices[0]?.delta;
967
1376
  const choice = chunk.choices[0];
968
1377
  if (delta?.content) {
@@ -985,38 +1394,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
985
1394
  }
986
1395
  }
987
1396
  }
988
- if (delta?.tool_calls) {
989
- for (const toolCall of delta.tool_calls) {
990
- if (toolCall.id) {
991
- if (currentToolCall) {
992
- yield {
993
- type: "action:args",
994
- id: currentToolCall.id,
995
- args: currentToolCall.arguments
996
- };
997
- yield {
998
- type: "action:end",
999
- id: currentToolCall.id,
1000
- name: currentToolCall.name
1001
- };
1002
- }
1003
- const tcExtraContent = toolCall.extra_content;
1004
- currentToolCall = {
1005
- id: toolCall.id,
1006
- name: toolCall.function?.name || "",
1007
- arguments: toolCall.function?.arguments || "",
1008
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1009
- };
1010
- yield {
1011
- type: "action:start",
1012
- id: currentToolCall.id,
1013
- name: currentToolCall.name,
1014
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1015
- };
1016
- } else if (currentToolCall && toolCall.function?.arguments) {
1017
- currentToolCall.arguments += toolCall.function.arguments;
1018
- }
1019
- }
1397
+ for (const part of delta?.tool_calls ?? []) {
1398
+ const index = part.index ?? 0;
1399
+ const call = toolCalls.get(index) ?? {
1400
+ id: "",
1401
+ name: "",
1402
+ arguments: ""
1403
+ };
1404
+ if (part.id) call.id = part.id;
1405
+ if (part.function?.name) call.name += part.function.name;
1406
+ if (part.function?.arguments)
1407
+ call.arguments += part.function.arguments;
1408
+ if (part.extra_content) call.extra_content = part.extra_content;
1409
+ toolCalls.set(index, call);
1020
1410
  }
1021
1411
  if (chunk.usage) {
1022
1412
  usage = {
@@ -1025,21 +1415,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1025
1415
  total_tokens: chunk.usage.total_tokens
1026
1416
  };
1027
1417
  }
1028
- if (choice?.finish_reason) {
1029
- if (currentToolCall) {
1030
- yield {
1031
- type: "action:args",
1032
- id: currentToolCall.id,
1033
- args: currentToolCall.arguments
1034
- };
1035
- yield {
1036
- type: "action:end",
1037
- id: currentToolCall.id,
1038
- name: currentToolCall.name
1039
- };
1040
- currentToolCall = null;
1041
- }
1042
- }
1418
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1419
+ }
1420
+ throwIfAborted(request.signal);
1421
+ if (!finishReason)
1422
+ throw new Error("OpenAI stream ended without a finish reason");
1423
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1424
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1425
+ }
1426
+ for (const call of toolCalls.values()) {
1427
+ if (!call.id || !call.name)
1428
+ throw new Error("OpenAI stream contains an incomplete tool call");
1429
+ JSON.parse(call.arguments || "{}");
1430
+ }
1431
+ for (const call of toolCalls.values()) {
1432
+ yield {
1433
+ type: "action:start",
1434
+ id: call.id,
1435
+ name: call.name,
1436
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1437
+ };
1438
+ yield { type: "action:args", id: call.id, args: call.arguments };
1439
+ yield { type: "action:end", id: call.id, name: call.name };
1043
1440
  }
1044
1441
  if (collectedCitations.length > 0) {
1045
1442
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1048,26 +1445,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1048
1445
  yield { type: "message:end" };
1049
1446
  yield { type: "done", usage };
1050
1447
  } catch (error) {
1051
- yield {
1052
- type: "error",
1053
- message: error instanceof Error ? error.message : "Unknown error",
1054
- code: `${this.provider.toUpperCase()}_ERROR`
1055
- };
1448
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1056
1449
  }
1057
1450
  }
1058
1451
  async complete(request) {
1452
+ throwIfAborted(request.signal);
1059
1453
  if (this.shouldUseResponsesApi(request)) {
1060
1454
  return this.completeWithResponses(request);
1061
1455
  }
1062
1456
  const client = await this.getClient();
1063
1457
  let messages;
1064
1458
  if (request.rawMessages && request.rawMessages.length > 0) {
1065
- const sanitized = request.rawMessages.map((msg) => {
1066
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1067
- return { ...msg, content: null };
1068
- }
1069
- return msg;
1070
- });
1459
+ const sanitized = this.formatRawMessages(request.rawMessages);
1071
1460
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1072
1461
  messages = [
1073
1462
  { role: "system", content: request.systemPrompt },
@@ -1082,7 +1471,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1082
1471
  request.systemPrompt
1083
1472
  );
1084
1473
  }
1085
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1474
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1475
+ type: "function",
1476
+ function: {
1477
+ name: tool.name,
1478
+ description: tool.description,
1479
+ parameters: normalizeObjectJsonSchema(
1480
+ tool.inputSchema
1481
+ )
1482
+ }
1483
+ })) : formatTools(request.actions ?? []);
1086
1484
  const openaiToolOptions = request.providerToolOptions?.openai;
1087
1485
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1088
1486
  type: "function",
@@ -1097,16 +1495,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1097
1495
  tools: tools.length > 0 ? tools : void 0,
1098
1496
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1099
1497
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1100
- ...buildOpenAITokenParams(
1498
+ ...buildOpenAIParameters(
1101
1499
  modelIdForCompletePayload,
1102
1500
  request.config?.maxTokens ?? this.config.maxTokens,
1103
- request.config?.temperature ?? this.config.temperature
1501
+ request.config?.temperature ?? this.config.temperature,
1502
+ this.parameterOptions(request)
1104
1503
  ),
1105
1504
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1106
1505
  stream: false
1107
1506
  };
1108
1507
  logProviderPayload("openai", "request payload", payload, request.debug);
1109
- const response = await client.chat.completions.create(payload);
1508
+ const response = await client.chat.completions.create(payload, {
1509
+ signal: request.signal
1510
+ });
1110
1511
  logProviderPayload("openai", "response payload", response, request.debug);
1111
1512
  const choice = response.choices?.[0];
1112
1513
  const message = choice?.message;
@@ -1259,7 +1660,7 @@ var AnthropicAdapter = class {
1259
1660
  }
1260
1661
  pendingToolResults.push({
1261
1662
  tool_use_id: toolCallId,
1262
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1663
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1263
1664
  });
1264
1665
  } else if (msg.role === "user") {
1265
1666
  if (pendingToolResults.length > 0) {
@@ -1380,14 +1781,23 @@ var AnthropicAdapter = class {
1380
1781
  buildRequestOptions(request) {
1381
1782
  const responseFormat = request.config?.responseFormat;
1382
1783
  const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
1383
- const systemMessage = (request.systemPrompt || "") + jsonObjectSuffix;
1784
+ let systemMessage;
1384
1785
  let messages;
1385
1786
  if (request.rawMessages && request.rawMessages.length > 0) {
1787
+ systemMessage = extractAnthropicSystemPrompt(
1788
+ request.rawMessages,
1789
+ request.systemPrompt
1790
+ );
1386
1791
  messages = this.convertToAnthropicMessages(request.rawMessages);
1387
1792
  } else {
1388
- const formatted = formatMessagesForAnthropic(request.messages, void 0);
1793
+ const formatted = formatMessagesForAnthropic(
1794
+ request.messages,
1795
+ request.systemPrompt
1796
+ );
1797
+ systemMessage = formatted.system;
1389
1798
  messages = formatted.messages;
1390
1799
  }
1800
+ systemMessage += jsonObjectSuffix;
1391
1801
  const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
1392
1802
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
1393
1803
  request.toolDefinitions ?? [],
@@ -1730,7 +2140,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
1730
2140
  for (const msg of messages) {
1731
2141
  const baseMessage = {
1732
2142
  role: msg.role,
1733
- content: msg.content ?? ""
2143
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
1734
2144
  };
1735
2145
  if (msg.role === "user" && msg.metadata?.attachments) {
1736
2146
  const images = await extractImagesFromAttachments(
@@ -1767,7 +2177,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
1767
2177
  for (const msg of rawMessages) {
1768
2178
  const baseMessage = {
1769
2179
  role: msg.role,
1770
- content: msg.content ?? ""
2180
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
1771
2181
  };
1772
2182
  if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
1773
2183
  const attachments = msg.attachments;
@@ -1977,6 +2387,10 @@ function messageToGeminiContent(msg) {
1977
2387
  if (msg.role === "system") return null;
1978
2388
  const parts = [];
1979
2389
  if (msg.role === "tool" && msg.tool_call_id) {
2390
+ msg = {
2391
+ ...msg,
2392
+ content: textToolOutput(msg.modelOutput, msg.content ?? "")
2393
+ };
1980
2394
  let responseData;
1981
2395
  try {
1982
2396
  responseData = JSON.parse(msg.content || "{}");
@@ -2437,6 +2851,13 @@ var AzureAdapter = class {
2437
2851
  let messages;
2438
2852
  if (request.rawMessages && request.rawMessages.length > 0) {
2439
2853
  const processedMessages = request.rawMessages.map((msg) => {
2854
+ const { modelOutput, ...rest } = msg;
2855
+ msg = {
2856
+ ...rest,
2857
+ ...msg.role === "tool" ? {
2858
+ content: textToolOutput(modelOutput, String(msg.content ?? ""))
2859
+ } : {}
2860
+ };
2440
2861
  const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
2441
2862
  if (hasAttachments) {
2442
2863
  const content = [];
@@ -2567,7 +2988,10 @@ var AzureAdapter = class {
2567
2988
  const client = await this.getClient();
2568
2989
  let messages;
2569
2990
  if (request.rawMessages && request.rawMessages.length > 0) {
2570
- messages = request.rawMessages;
2991
+ messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
2992
+ ...msg,
2993
+ ...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
2994
+ }));
2571
2995
  if (request.systemPrompt) {
2572
2996
  const hasSystem = messages.some((m) => m.role === "system");
2573
2997
  if (!hasSystem) {