@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.
- package/README.md +112 -0
- package/dist/adapters/index.d.mts +5 -5
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/index.js +372 -39
- package/dist/adapters/index.mjs +372 -39
- package/dist/audio-BBSwnjo8.d.mts +92 -0
- package/dist/audio-BBSwnjo8.d.ts +92 -0
- package/dist/{base-CkeT7u-O.d.mts → base-BICaQaRS.d.mts} +7 -3
- package/dist/{base-BdTpUAWI.d.ts → base-CpRPB8u8.d.ts} +7 -3
- package/dist/fallback/index.d.mts +4 -4
- package/dist/fallback/index.d.ts +4 -4
- package/dist/index.d.mts +17 -35
- package/dist/index.d.ts +17 -35
- package/dist/index.js +229 -60
- package/dist/index.mjs +227 -61
- package/dist/providers/anthropic/index.d.mts +3 -3
- package/dist/providers/anthropic/index.d.ts +3 -3
- package/dist/providers/anthropic/index.js +123 -7
- package/dist/providers/anthropic/index.mjs +123 -7
- package/dist/providers/azure/index.d.mts +3 -3
- package/dist/providers/azure/index.d.ts +3 -3
- package/dist/providers/azure/index.js +101 -4
- package/dist/providers/azure/index.mjs +101 -4
- package/dist/providers/fireworks/index.d.mts +3 -3
- package/dist/providers/fireworks/index.d.ts +3 -3
- package/dist/providers/fireworks/index.js +321 -31
- package/dist/providers/fireworks/index.mjs +321 -31
- package/dist/providers/google/index.d.mts +3 -3
- package/dist/providers/google/index.d.ts +3 -3
- package/dist/providers/google/index.js +321 -31
- package/dist/providers/google/index.mjs +321 -31
- package/dist/providers/ollama/index.d.mts +4 -4
- package/dist/providers/ollama/index.d.ts +4 -4
- package/dist/providers/ollama/index.js +87 -2
- package/dist/providers/ollama/index.mjs +87 -2
- package/dist/providers/openai/index.d.mts +25 -6
- package/dist/providers/openai/index.d.ts +25 -6
- package/dist/providers/openai/index.js +750 -40
- package/dist/providers/openai/index.mjs +749 -41
- package/dist/providers/openrouter/index.d.mts +3 -3
- package/dist/providers/openrouter/index.d.ts +3 -3
- package/dist/providers/openrouter/index.js +321 -31
- package/dist/providers/openrouter/index.mjs +321 -31
- package/dist/providers/togetherai/index.d.mts +3 -3
- package/dist/providers/togetherai/index.d.ts +3 -3
- package/dist/providers/togetherai/index.js +321 -31
- package/dist/providers/togetherai/index.mjs +321 -31
- package/dist/providers/xai/index.d.mts +3 -3
- package/dist/providers/xai/index.d.ts +3 -3
- package/dist/providers/xai/index.js +321 -31
- package/dist/providers/xai/index.mjs +321 -31
- package/dist/{types-BWRYIvl_.d.mts → types-C3RgRbEC.d.mts} +1 -1
- package/dist/{types-CpXcT-tY.d.ts → types-DCFqkJAy.d.ts} +1 -1
- package/dist/{types-NOswlhm9.d.mts → types-DHQEMFHA.d.mts} +3 -3
- package/dist/{types-BW3csYo5.d.mts → types-Dg1Bcg1k.d.mts} +106 -3
- package/dist/{types-BW3csYo5.d.ts → types-Dg1Bcg1k.d.ts} +106 -3
- package/dist/{types-DA54bSmX.d.ts → types-Dikj3En4.d.ts} +3 -3
- package/dist/yourgpt/index.d.mts +1 -1
- package/dist/yourgpt/index.d.ts +1 -1
- package/docs/multimodal-and-audio.md +170 -0
- package/package.json +3 -2
package/dist/adapters/index.mjs
CHANGED
|
@@ -1,3 +1,106 @@
|
|
|
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
|
+
|
|
1
104
|
// src/core/openai-parameters.ts
|
|
2
105
|
function capabilities(model) {
|
|
3
106
|
const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
|
|
@@ -420,7 +523,27 @@ function messageToOpenAIContent(message) {
|
|
|
420
523
|
}
|
|
421
524
|
return blocks;
|
|
422
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
|
+
}
|
|
423
545
|
function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
546
|
+
const system = extractAnthropicSystemPrompt(messages, systemPrompt);
|
|
424
547
|
const formatted = [];
|
|
425
548
|
for (let i = 0; i < messages.length; i++) {
|
|
426
549
|
const msg = messages[i];
|
|
@@ -449,7 +572,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
449
572
|
{
|
|
450
573
|
type: "tool_result",
|
|
451
574
|
tool_use_id: msg.tool_call_id,
|
|
452
|
-
content: msg.content ?? ""
|
|
575
|
+
content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
|
|
453
576
|
}
|
|
454
577
|
];
|
|
455
578
|
while (i + 1 < messages.length && messages[i + 1].role === "tool") {
|
|
@@ -459,7 +582,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
459
582
|
toolResults.push({
|
|
460
583
|
type: "tool_result",
|
|
461
584
|
tool_use_id: nextTool.tool_call_id,
|
|
462
|
-
content: nextTool.content ?? ""
|
|
585
|
+
content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
|
|
463
586
|
});
|
|
464
587
|
}
|
|
465
588
|
}
|
|
@@ -475,11 +598,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
475
598
|
}
|
|
476
599
|
}
|
|
477
600
|
return {
|
|
478
|
-
system
|
|
601
|
+
system,
|
|
479
602
|
messages: formatted
|
|
480
603
|
};
|
|
481
604
|
}
|
|
482
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
605
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
483
606
|
const formatted = [];
|
|
484
607
|
if (systemPrompt) {
|
|
485
608
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -497,7 +620,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
497
620
|
const assistantMsg = {
|
|
498
621
|
role: "assistant",
|
|
499
622
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
500
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
623
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
624
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
501
625
|
};
|
|
502
626
|
if (hasToolCalls) {
|
|
503
627
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -506,7 +630,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
506
630
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
507
631
|
formatted.push({
|
|
508
632
|
role: "tool",
|
|
509
|
-
content: msg.content ?? "",
|
|
633
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
634
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
510
635
|
tool_call_id: msg.tool_call_id
|
|
511
636
|
});
|
|
512
637
|
}
|
|
@@ -514,6 +639,64 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
514
639
|
return formatted;
|
|
515
640
|
}
|
|
516
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
|
+
|
|
517
700
|
// src/core/provider-error.ts
|
|
518
701
|
function providerErrorEvent(error, code) {
|
|
519
702
|
const details = error ?? {};
|
|
@@ -590,13 +773,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
590
773
|
}));
|
|
591
774
|
}
|
|
592
775
|
shouldUseResponsesApi(request) {
|
|
593
|
-
|
|
594
|
-
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
|
|
595
|
-
return true;
|
|
596
|
-
return requiresOpenAIResponses(
|
|
776
|
+
return useOpenAIResponses(
|
|
597
777
|
request.config?.model || this.model,
|
|
598
778
|
this.config.baseUrl,
|
|
599
|
-
this.parameterOptions(request)
|
|
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
|
|
600
786
|
);
|
|
601
787
|
}
|
|
602
788
|
/**
|
|
@@ -629,11 +815,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
629
815
|
});
|
|
630
816
|
}
|
|
631
817
|
buildResponsesInput(request) {
|
|
632
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(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);
|
|
633
819
|
const input = [];
|
|
634
820
|
for (const message of sourceMessages) {
|
|
635
821
|
if (message.role === "system") {
|
|
636
|
-
if (
|
|
822
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
637
823
|
input.push({
|
|
638
824
|
type: "message",
|
|
639
825
|
role: "developer",
|
|
@@ -643,6 +829,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
643
829
|
continue;
|
|
644
830
|
}
|
|
645
831
|
if (message.role === "assistant") {
|
|
832
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
833
|
+
if (replay) {
|
|
834
|
+
input.push(...replay);
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
646
837
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
647
838
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
648
839
|
input.push({
|
|
@@ -666,7 +857,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
666
857
|
input.push({
|
|
667
858
|
type: "function_call_output",
|
|
668
859
|
call_id: message.tool_call_id,
|
|
669
|
-
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)
|
|
670
861
|
});
|
|
671
862
|
continue;
|
|
672
863
|
}
|
|
@@ -680,10 +871,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
680
871
|
return input;
|
|
681
872
|
}
|
|
682
873
|
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
683
|
-
formatRawMessages(messages) {
|
|
874
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
684
875
|
return messages.map((message) => {
|
|
685
|
-
const { attachments, ...rest } = message;
|
|
686
|
-
let content = message.content;
|
|
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;
|
|
687
880
|
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
688
881
|
content = null;
|
|
689
882
|
}
|
|
@@ -696,7 +889,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
696
889
|
content = [...parts, ...images];
|
|
697
890
|
}
|
|
698
891
|
}
|
|
699
|
-
return {
|
|
892
|
+
return {
|
|
893
|
+
...rest,
|
|
894
|
+
content,
|
|
895
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
896
|
+
};
|
|
700
897
|
});
|
|
701
898
|
}
|
|
702
899
|
/**
|
|
@@ -777,14 +974,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
777
974
|
).map((part) => part.text) : []
|
|
778
975
|
).join("") : "";
|
|
779
976
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
name
|
|
783
|
-
|
|
784
|
-
|
|
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
|
+
}) : [];
|
|
785
992
|
return {
|
|
786
993
|
content,
|
|
787
994
|
toolCalls,
|
|
995
|
+
providerMetadata: responseMetadata(response),
|
|
788
996
|
usage: response?.usage ? {
|
|
789
997
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
790
998
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -795,6 +1003,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
795
1003
|
}
|
|
796
1004
|
buildResponsesPayload(request, stream) {
|
|
797
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
|
+
);
|
|
798
1017
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
799
1018
|
request.config?.responseFormat
|
|
800
1019
|
);
|
|
@@ -810,13 +1029,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
810
1029
|
),
|
|
811
1030
|
tool_choice: openaiToolOptions?.toolChoice,
|
|
812
1031
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
813
|
-
...
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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
|
+
} : {},
|
|
820
1052
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
821
1053
|
stream
|
|
822
1054
|
};
|
|
@@ -875,6 +1107,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
875
1107
|
let usage;
|
|
876
1108
|
const citations = [];
|
|
877
1109
|
let completed = false;
|
|
1110
|
+
let terminalResponse;
|
|
1111
|
+
let responseIdentity = {};
|
|
1112
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1113
|
+
let streamedText = "";
|
|
878
1114
|
function* flushToolCalls() {
|
|
879
1115
|
for (const call of toolCalls.values()) {
|
|
880
1116
|
if (!call.name)
|
|
@@ -891,17 +1127,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
891
1127
|
for await (const event of stream) {
|
|
892
1128
|
throwIfAborted(request.signal);
|
|
893
1129
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1130
|
+
if (completed)
|
|
1131
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
894
1132
|
const index = event?.output_index ?? 0;
|
|
895
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;
|
|
896
1150
|
case "response.output_text.delta":
|
|
897
1151
|
if (event.delta) {
|
|
1152
|
+
streamedText += event.delta;
|
|
898
1153
|
yield { type: "message:delta", content: event.delta };
|
|
899
1154
|
}
|
|
900
1155
|
break;
|
|
901
1156
|
case "response.output_item.added":
|
|
902
1157
|
if (event.item?.type === "function_call") {
|
|
903
1158
|
toolCalls.set(index, {
|
|
904
|
-
id: event.item.call_id ??
|
|
1159
|
+
id: event.item.call_id ?? "",
|
|
905
1160
|
name: event.item.name ?? "",
|
|
906
1161
|
arguments: event.item.arguments ?? ""
|
|
907
1162
|
});
|
|
@@ -920,7 +1175,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
920
1175
|
break;
|
|
921
1176
|
}
|
|
922
1177
|
case "response.completed": {
|
|
1178
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1179
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
923
1180
|
completed = true;
|
|
1181
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
924
1182
|
const found = this.collectResponsesCitations(
|
|
925
1183
|
event.response?.output,
|
|
926
1184
|
citations.length
|
|
@@ -957,12 +1215,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
957
1215
|
throwIfAborted(request.signal);
|
|
958
1216
|
if (!completed)
|
|
959
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
|
+
}
|
|
960
1263
|
yield* flushToolCalls();
|
|
961
1264
|
if (citations.length > 0) {
|
|
962
1265
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
963
1266
|
}
|
|
964
|
-
yield {
|
|
965
|
-
|
|
1267
|
+
yield {
|
|
1268
|
+
type: "message:end",
|
|
1269
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1270
|
+
};
|
|
1271
|
+
yield {
|
|
1272
|
+
type: "done",
|
|
1273
|
+
usage,
|
|
1274
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1275
|
+
};
|
|
966
1276
|
}
|
|
967
1277
|
async *stream(request) {
|
|
968
1278
|
throwIfAborted(request.signal);
|
|
@@ -1348,7 +1658,7 @@ var AnthropicAdapter = class {
|
|
|
1348
1658
|
}
|
|
1349
1659
|
pendingToolResults.push({
|
|
1350
1660
|
tool_use_id: toolCallId,
|
|
1351
|
-
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)
|
|
1352
1662
|
});
|
|
1353
1663
|
} else if (msg.role === "user") {
|
|
1354
1664
|
if (pendingToolResults.length > 0) {
|
|
@@ -1469,14 +1779,23 @@ var AnthropicAdapter = class {
|
|
|
1469
1779
|
buildRequestOptions(request) {
|
|
1470
1780
|
const responseFormat = request.config?.responseFormat;
|
|
1471
1781
|
const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
|
|
1472
|
-
|
|
1782
|
+
let systemMessage;
|
|
1473
1783
|
let messages;
|
|
1474
1784
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1785
|
+
systemMessage = extractAnthropicSystemPrompt(
|
|
1786
|
+
request.rawMessages,
|
|
1787
|
+
request.systemPrompt
|
|
1788
|
+
);
|
|
1475
1789
|
messages = this.convertToAnthropicMessages(request.rawMessages);
|
|
1476
1790
|
} else {
|
|
1477
|
-
const formatted = formatMessagesForAnthropic(
|
|
1791
|
+
const formatted = formatMessagesForAnthropic(
|
|
1792
|
+
request.messages,
|
|
1793
|
+
request.systemPrompt
|
|
1794
|
+
);
|
|
1795
|
+
systemMessage = formatted.system;
|
|
1478
1796
|
messages = formatted.messages;
|
|
1479
1797
|
}
|
|
1798
|
+
systemMessage += jsonObjectSuffix;
|
|
1480
1799
|
const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
|
|
1481
1800
|
const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
|
|
1482
1801
|
request.toolDefinitions ?? [],
|
|
@@ -1819,7 +2138,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
|
|
|
1819
2138
|
for (const msg of messages) {
|
|
1820
2139
|
const baseMessage = {
|
|
1821
2140
|
role: msg.role,
|
|
1822
|
-
content: msg.content ?? ""
|
|
2141
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
|
|
1823
2142
|
};
|
|
1824
2143
|
if (msg.role === "user" && msg.metadata?.attachments) {
|
|
1825
2144
|
const images = await extractImagesFromAttachments(
|
|
@@ -1856,7 +2175,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
|
|
|
1856
2175
|
for (const msg of rawMessages) {
|
|
1857
2176
|
const baseMessage = {
|
|
1858
2177
|
role: msg.role,
|
|
1859
|
-
content: msg.content ?? ""
|
|
2178
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
|
|
1860
2179
|
};
|
|
1861
2180
|
if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
|
|
1862
2181
|
const attachments = msg.attachments;
|
|
@@ -2066,6 +2385,10 @@ function messageToGeminiContent(msg) {
|
|
|
2066
2385
|
if (msg.role === "system") return null;
|
|
2067
2386
|
const parts = [];
|
|
2068
2387
|
if (msg.role === "tool" && msg.tool_call_id) {
|
|
2388
|
+
msg = {
|
|
2389
|
+
...msg,
|
|
2390
|
+
content: textToolOutput(msg.modelOutput, msg.content ?? "")
|
|
2391
|
+
};
|
|
2069
2392
|
let responseData;
|
|
2070
2393
|
try {
|
|
2071
2394
|
responseData = JSON.parse(msg.content || "{}");
|
|
@@ -2526,6 +2849,13 @@ var AzureAdapter = class {
|
|
|
2526
2849
|
let messages;
|
|
2527
2850
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2528
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
|
+
};
|
|
2529
2859
|
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
2530
2860
|
if (hasAttachments) {
|
|
2531
2861
|
const content = [];
|
|
@@ -2656,7 +2986,10 @@ var AzureAdapter = class {
|
|
|
2656
2986
|
const client = await this.getClient();
|
|
2657
2987
|
let messages;
|
|
2658
2988
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2659
|
-
messages = request.rawMessages
|
|
2989
|
+
messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
|
|
2990
|
+
...msg,
|
|
2991
|
+
...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
|
|
2992
|
+
}));
|
|
2660
2993
|
if (request.systemPrompt) {
|
|
2661
2994
|
const hasSystem = messages.some((m) => m.role === "system");
|
|
2662
2995
|
if (!hasSystem) {
|