@yourgpt/llm-sdk 2.6.0-beta.1 → 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 (61) hide show
  1. package/README.md +112 -0
  2. package/dist/adapters/index.d.mts +5 -5
  3. package/dist/adapters/index.d.ts +5 -5
  4. package/dist/adapters/index.js +372 -39
  5. package/dist/adapters/index.mjs +372 -39
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-CkeT7u-O.d.mts → base-BICaQaRS.d.mts} +7 -3
  9. package/dist/{base-BdTpUAWI.d.ts → base-CpRPB8u8.d.ts} +7 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/index.d.mts +17 -35
  13. package/dist/index.d.ts +17 -35
  14. package/dist/index.js +229 -60
  15. package/dist/index.mjs +227 -61
  16. package/dist/providers/anthropic/index.d.mts +3 -3
  17. package/dist/providers/anthropic/index.d.ts +3 -3
  18. package/dist/providers/anthropic/index.js +123 -7
  19. package/dist/providers/anthropic/index.mjs +123 -7
  20. package/dist/providers/azure/index.d.mts +3 -3
  21. package/dist/providers/azure/index.d.ts +3 -3
  22. package/dist/providers/azure/index.js +101 -4
  23. package/dist/providers/azure/index.mjs +101 -4
  24. package/dist/providers/fireworks/index.d.mts +3 -3
  25. package/dist/providers/fireworks/index.d.ts +3 -3
  26. package/dist/providers/fireworks/index.js +321 -31
  27. package/dist/providers/fireworks/index.mjs +321 -31
  28. package/dist/providers/google/index.d.mts +3 -3
  29. package/dist/providers/google/index.d.ts +3 -3
  30. package/dist/providers/google/index.js +321 -31
  31. package/dist/providers/google/index.mjs +321 -31
  32. package/dist/providers/ollama/index.d.mts +4 -4
  33. package/dist/providers/ollama/index.d.ts +4 -4
  34. package/dist/providers/ollama/index.js +87 -2
  35. package/dist/providers/ollama/index.mjs +87 -2
  36. package/dist/providers/openai/index.d.mts +25 -6
  37. package/dist/providers/openai/index.d.ts +25 -6
  38. package/dist/providers/openai/index.js +750 -40
  39. package/dist/providers/openai/index.mjs +749 -41
  40. package/dist/providers/openrouter/index.d.mts +3 -3
  41. package/dist/providers/openrouter/index.d.ts +3 -3
  42. package/dist/providers/openrouter/index.js +321 -31
  43. package/dist/providers/openrouter/index.mjs +321 -31
  44. package/dist/providers/togetherai/index.d.mts +3 -3
  45. package/dist/providers/togetherai/index.d.ts +3 -3
  46. package/dist/providers/togetherai/index.js +321 -31
  47. package/dist/providers/togetherai/index.mjs +321 -31
  48. package/dist/providers/xai/index.d.mts +3 -3
  49. package/dist/providers/xai/index.d.ts +3 -3
  50. package/dist/providers/xai/index.js +321 -31
  51. package/dist/providers/xai/index.mjs +321 -31
  52. package/dist/{types-BWRYIvl_.d.mts → types-C3RgRbEC.d.mts} +1 -1
  53. package/dist/{types-CpXcT-tY.d.ts → types-DCFqkJAy.d.ts} +1 -1
  54. package/dist/{types-NOswlhm9.d.mts → types-DHQEMFHA.d.mts} +3 -3
  55. package/dist/{types-BW3csYo5.d.mts → types-Dg1Bcg1k.d.mts} +106 -3
  56. package/dist/{types-BW3csYo5.d.ts → types-Dg1Bcg1k.d.ts} +106 -3
  57. package/dist/{types-DA54bSmX.d.ts → types-Dikj3En4.d.ts} +3 -3
  58. package/dist/yourgpt/index.d.mts +1 -1
  59. package/dist/yourgpt/index.d.ts +1 -1
  60. package/docs/multimodal-and-audio.md +170 -0
  61. package/package.json +3 -2
@@ -1,5 +1,108 @@
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
+
3
106
  // src/core/openai-parameters.ts
4
107
  function capabilities(model) {
5
108
  const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
@@ -422,7 +525,27 @@ function messageToOpenAIContent(message) {
422
525
  }
423
526
  return blocks;
424
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
+ }
425
547
  function formatMessagesForAnthropic(messages, systemPrompt) {
548
+ const system = extractAnthropicSystemPrompt(messages, systemPrompt);
426
549
  const formatted = [];
427
550
  for (let i = 0; i < messages.length; i++) {
428
551
  const msg = messages[i];
@@ -451,7 +574,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
451
574
  {
452
575
  type: "tool_result",
453
576
  tool_use_id: msg.tool_call_id,
454
- content: msg.content ?? ""
577
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
455
578
  }
456
579
  ];
457
580
  while (i + 1 < messages.length && messages[i + 1].role === "tool") {
@@ -461,7 +584,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
461
584
  toolResults.push({
462
585
  type: "tool_result",
463
586
  tool_use_id: nextTool.tool_call_id,
464
- content: nextTool.content ?? ""
587
+ content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
465
588
  });
466
589
  }
467
590
  }
@@ -477,11 +600,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
477
600
  }
478
601
  }
479
602
  return {
480
- system: systemPrompt || "",
603
+ system,
481
604
  messages: formatted
482
605
  };
483
606
  }
484
- function formatMessagesForOpenAI(messages, systemPrompt) {
607
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
485
608
  const formatted = [];
486
609
  if (systemPrompt) {
487
610
  formatted.push({ role: "system", content: systemPrompt });
@@ -499,7 +622,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
499
622
  const assistantMsg = {
500
623
  role: "assistant",
501
624
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
502
- content: hasToolCalls ? msg.content || null : msg.content
625
+ content: hasToolCalls ? msg.content || null : msg.content,
626
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
503
627
  };
504
628
  if (hasToolCalls) {
505
629
  assistantMsg.tool_calls = msg.tool_calls;
@@ -508,7 +632,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
508
632
  } else if (msg.role === "tool" && msg.tool_call_id) {
509
633
  formatted.push({
510
634
  role: "tool",
511
- content: msg.content ?? "",
635
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
636
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
512
637
  tool_call_id: msg.tool_call_id
513
638
  });
514
639
  }
@@ -516,6 +641,64 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
516
641
  return formatted;
517
642
  }
518
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
+
519
702
  // src/core/provider-error.ts
520
703
  function providerErrorEvent(error, code) {
521
704
  const details = error ?? {};
@@ -592,13 +775,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
592
775
  }));
593
776
  }
594
777
  shouldUseResponsesApi(request) {
595
- if (this.toolDefinitions(request).length === 0) return false;
596
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
597
- return true;
598
- return requiresOpenAIResponses(
778
+ return useOpenAIResponses(
599
779
  request.config?.model || this.model,
600
780
  this.config.baseUrl,
601
- this.parameterOptions(request)
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
602
788
  );
603
789
  }
604
790
  /**
@@ -631,11 +817,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
631
817
  });
632
818
  }
633
819
  buildResponsesInput(request) {
634
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(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);
635
821
  const input = [];
636
822
  for (const message of sourceMessages) {
637
823
  if (message.role === "system") {
638
- if (!request.systemPrompt && message.content) {
824
+ if (message.content && message.content !== request.systemPrompt) {
639
825
  input.push({
640
826
  type: "message",
641
827
  role: "developer",
@@ -645,6 +831,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
645
831
  continue;
646
832
  }
647
833
  if (message.role === "assistant") {
834
+ const replay = getResponseReplay(message.providerMetadata);
835
+ if (replay) {
836
+ input.push(...replay);
837
+ continue;
838
+ }
648
839
  const content = this.toResponsesContent(message.content, "assistant");
649
840
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
650
841
  input.push({
@@ -668,7 +859,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
668
859
  input.push({
669
860
  type: "function_call_output",
670
861
  call_id: message.tool_call_id,
671
- 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)
672
863
  });
673
864
  continue;
674
865
  }
@@ -682,10 +873,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
682
873
  return input;
683
874
  }
684
875
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
685
- formatRawMessages(messages) {
876
+ formatRawMessages(messages, preserveProviderMetadata = false) {
686
877
  return messages.map((message) => {
687
- const { attachments, ...rest } = message;
688
- let content = message.content;
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;
689
882
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
690
883
  content = null;
691
884
  }
@@ -698,7 +891,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
698
891
  content = [...parts, ...images];
699
892
  }
700
893
  }
701
- return { ...rest, content };
894
+ return {
895
+ ...rest,
896
+ content,
897
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
898
+ };
702
899
  });
703
900
  }
704
901
  /**
@@ -779,14 +976,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
779
976
  ).map((part) => part.text) : []
780
977
  ).join("") : "";
781
978
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
782
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
783
- id: item.call_id ?? item.id ?? generateToolCallId(),
784
- name: item.name,
785
- args: JSON.parse(item.arguments ?? "{}")
786
- })) : [];
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
+ }) : [];
787
994
  return {
788
995
  content,
789
996
  toolCalls,
997
+ providerMetadata: responseMetadata(response),
790
998
  usage: response?.usage ? {
791
999
  promptTokens: response.usage.input_tokens ?? 0,
792
1000
  completionTokens: response.usage.output_tokens ?? 0,
@@ -797,6 +1005,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
797
1005
  }
798
1006
  buildResponsesPayload(request, stream) {
799
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
+ );
800
1019
  const responsesTextFormat = toOpenAIResponsesTextFormat(
801
1020
  request.config?.responseFormat
802
1021
  );
@@ -812,13 +1031,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
812
1031
  ),
813
1032
  tool_choice: openaiToolOptions?.toolChoice,
814
1033
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
815
- ...buildOpenAIParameters(
816
- modelIdForPayload,
817
- request.config?.maxTokens ?? this.config.maxTokens,
818
- request.config?.temperature ?? this.config.temperature,
819
- this.parameterOptions(request),
820
- "responses"
821
- ),
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
+ } : {},
822
1054
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
823
1055
  stream
824
1056
  };
@@ -877,6 +1109,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
877
1109
  let usage;
878
1110
  const citations = [];
879
1111
  let completed = false;
1112
+ let terminalResponse;
1113
+ let responseIdentity = {};
1114
+ const outputItems = /* @__PURE__ */ new Map();
1115
+ let streamedText = "";
880
1116
  function* flushToolCalls() {
881
1117
  for (const call of toolCalls.values()) {
882
1118
  if (!call.name)
@@ -893,17 +1129,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
893
1129
  for await (const event of stream) {
894
1130
  throwIfAborted(request.signal);
895
1131
  logProviderPayload("openai", "stream chunk", event, request.debug);
1132
+ if (completed)
1133
+ throw new Error("Responses stream emitted an event after completion");
896
1134
  const index = event?.output_index ?? 0;
897
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;
898
1152
  case "response.output_text.delta":
899
1153
  if (event.delta) {
1154
+ streamedText += event.delta;
900
1155
  yield { type: "message:delta", content: event.delta };
901
1156
  }
902
1157
  break;
903
1158
  case "response.output_item.added":
904
1159
  if (event.item?.type === "function_call") {
905
1160
  toolCalls.set(index, {
906
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1161
+ id: event.item.call_id ?? "",
907
1162
  name: event.item.name ?? "",
908
1163
  arguments: event.item.arguments ?? ""
909
1164
  });
@@ -922,7 +1177,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
922
1177
  break;
923
1178
  }
924
1179
  case "response.completed": {
1180
+ if (event.response?.status && event.response.status !== "completed")
1181
+ throw new Error(`Responses stream ${event.response.status}`);
925
1182
  completed = true;
1183
+ terminalResponse = { ...responseIdentity, ...event.response };
926
1184
  const found = this.collectResponsesCitations(
927
1185
  event.response?.output,
928
1186
  citations.length
@@ -959,12 +1217,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
959
1217
  throwIfAborted(request.signal);
960
1218
  if (!completed)
961
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
+ }
962
1265
  yield* flushToolCalls();
963
1266
  if (citations.length > 0) {
964
1267
  yield { type: "citation", citations: deduplicateCitations(citations) };
965
1268
  }
966
- yield { type: "message:end" };
967
- 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
+ };
968
1278
  }
969
1279
  async *stream(request) {
970
1280
  throwIfAborted(request.signal);
@@ -1350,7 +1660,7 @@ var AnthropicAdapter = class {
1350
1660
  }
1351
1661
  pendingToolResults.push({
1352
1662
  tool_use_id: toolCallId,
1353
- 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)
1354
1664
  });
1355
1665
  } else if (msg.role === "user") {
1356
1666
  if (pendingToolResults.length > 0) {
@@ -1471,14 +1781,23 @@ var AnthropicAdapter = class {
1471
1781
  buildRequestOptions(request) {
1472
1782
  const responseFormat = request.config?.responseFormat;
1473
1783
  const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
1474
- const systemMessage = (request.systemPrompt || "") + jsonObjectSuffix;
1784
+ let systemMessage;
1475
1785
  let messages;
1476
1786
  if (request.rawMessages && request.rawMessages.length > 0) {
1787
+ systemMessage = extractAnthropicSystemPrompt(
1788
+ request.rawMessages,
1789
+ request.systemPrompt
1790
+ );
1477
1791
  messages = this.convertToAnthropicMessages(request.rawMessages);
1478
1792
  } else {
1479
- const formatted = formatMessagesForAnthropic(request.messages, void 0);
1793
+ const formatted = formatMessagesForAnthropic(
1794
+ request.messages,
1795
+ request.systemPrompt
1796
+ );
1797
+ systemMessage = formatted.system;
1480
1798
  messages = formatted.messages;
1481
1799
  }
1800
+ systemMessage += jsonObjectSuffix;
1482
1801
  const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
1483
1802
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
1484
1803
  request.toolDefinitions ?? [],
@@ -1821,7 +2140,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
1821
2140
  for (const msg of messages) {
1822
2141
  const baseMessage = {
1823
2142
  role: msg.role,
1824
- content: msg.content ?? ""
2143
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
1825
2144
  };
1826
2145
  if (msg.role === "user" && msg.metadata?.attachments) {
1827
2146
  const images = await extractImagesFromAttachments(
@@ -1858,7 +2177,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
1858
2177
  for (const msg of rawMessages) {
1859
2178
  const baseMessage = {
1860
2179
  role: msg.role,
1861
- content: msg.content ?? ""
2180
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
1862
2181
  };
1863
2182
  if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
1864
2183
  const attachments = msg.attachments;
@@ -2068,6 +2387,10 @@ function messageToGeminiContent(msg) {
2068
2387
  if (msg.role === "system") return null;
2069
2388
  const parts = [];
2070
2389
  if (msg.role === "tool" && msg.tool_call_id) {
2390
+ msg = {
2391
+ ...msg,
2392
+ content: textToolOutput(msg.modelOutput, msg.content ?? "")
2393
+ };
2071
2394
  let responseData;
2072
2395
  try {
2073
2396
  responseData = JSON.parse(msg.content || "{}");
@@ -2528,6 +2851,13 @@ var AzureAdapter = class {
2528
2851
  let messages;
2529
2852
  if (request.rawMessages && request.rawMessages.length > 0) {
2530
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
+ };
2531
2861
  const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
2532
2862
  if (hasAttachments) {
2533
2863
  const content = [];
@@ -2658,7 +2988,10 @@ var AzureAdapter = class {
2658
2988
  const client = await this.getClient();
2659
2989
  let messages;
2660
2990
  if (request.rawMessages && request.rawMessages.length > 0) {
2661
- messages = request.rawMessages;
2991
+ messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
2992
+ ...msg,
2993
+ ...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
2994
+ }));
2662
2995
  if (request.systemPrompt) {
2663
2996
  const hasSystem = messages.some((m) => m.role === "system");
2664
2997
  if (!hasSystem) {