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