@yourgpt/llm-sdk 2.6.0-beta.1 → 2.6.0-beta.3
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 +146 -0
- package/dist/adapters/index.d.mts +5 -5
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/index.js +437 -39
- package/dist/adapters/index.mjs +437 -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 +20 -36
- package/dist/index.d.ts +20 -36
- package/dist/index.js +327 -69
- package/dist/index.mjs +325 -70
- package/dist/providers/anthropic/index.d.mts +3 -3
- package/dist/providers/anthropic/index.d.ts +3 -3
- package/dist/providers/anthropic/index.js +116 -7
- package/dist/providers/anthropic/index.mjs +116 -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 +393 -31
- package/dist/providers/fireworks/index.mjs +393 -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 +393 -31
- package/dist/providers/google/index.mjs +393 -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 +822 -40
- package/dist/providers/openai/index.mjs +821 -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 +393 -31
- package/dist/providers/openrouter/index.mjs +393 -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 +393 -31
- package/dist/providers/togetherai/index.mjs +393 -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 +393 -31
- package/dist/providers/xai/index.mjs +393 -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.js
CHANGED
|
@@ -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,20 @@ function messageToOpenAIContent(message) {
|
|
|
422
525
|
}
|
|
423
526
|
return blocks;
|
|
424
527
|
}
|
|
528
|
+
function extractAnthropicSystemPrompt(messages, systemPrompt) {
|
|
529
|
+
const explicit = typeof systemPrompt === "string" ? systemPrompt : "";
|
|
530
|
+
const parts = explicit ? [explicit] : [];
|
|
531
|
+
for (const message of messages) {
|
|
532
|
+
if (message.role !== "system") continue;
|
|
533
|
+
const text = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.filter(
|
|
534
|
+
(block) => block?.type === "text" && typeof block.text === "string"
|
|
535
|
+
).map((block) => block.text).filter(Boolean).join("\n") : "";
|
|
536
|
+
if (text && text !== explicit) parts.push(text);
|
|
537
|
+
}
|
|
538
|
+
return parts.join("\n");
|
|
539
|
+
}
|
|
425
540
|
function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
541
|
+
const system = extractAnthropicSystemPrompt(messages, systemPrompt);
|
|
426
542
|
const formatted = [];
|
|
427
543
|
for (let i = 0; i < messages.length; i++) {
|
|
428
544
|
const msg = messages[i];
|
|
@@ -451,7 +567,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
451
567
|
{
|
|
452
568
|
type: "tool_result",
|
|
453
569
|
tool_use_id: msg.tool_call_id,
|
|
454
|
-
content: msg.content ?? ""
|
|
570
|
+
content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
|
|
455
571
|
}
|
|
456
572
|
];
|
|
457
573
|
while (i + 1 < messages.length && messages[i + 1].role === "tool") {
|
|
@@ -461,7 +577,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
461
577
|
toolResults.push({
|
|
462
578
|
type: "tool_result",
|
|
463
579
|
tool_use_id: nextTool.tool_call_id,
|
|
464
|
-
content: nextTool.content ?? ""
|
|
580
|
+
content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
|
|
465
581
|
});
|
|
466
582
|
}
|
|
467
583
|
}
|
|
@@ -477,11 +593,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
477
593
|
}
|
|
478
594
|
}
|
|
479
595
|
return {
|
|
480
|
-
system
|
|
596
|
+
system,
|
|
481
597
|
messages: formatted
|
|
482
598
|
};
|
|
483
599
|
}
|
|
484
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
600
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
485
601
|
const formatted = [];
|
|
486
602
|
if (systemPrompt) {
|
|
487
603
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -499,7 +615,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
499
615
|
const assistantMsg = {
|
|
500
616
|
role: "assistant",
|
|
501
617
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
502
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
618
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
619
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
503
620
|
};
|
|
504
621
|
if (hasToolCalls) {
|
|
505
622
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -508,7 +625,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
508
625
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
509
626
|
formatted.push({
|
|
510
627
|
role: "tool",
|
|
511
|
-
content: msg.content ?? "",
|
|
628
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
629
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
512
630
|
tool_call_id: msg.tool_call_id
|
|
513
631
|
});
|
|
514
632
|
}
|
|
@@ -516,7 +634,120 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
516
634
|
return formatted;
|
|
517
635
|
}
|
|
518
636
|
|
|
637
|
+
// src/core/openai-responses.ts
|
|
638
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
639
|
+
return {
|
|
640
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
641
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
645
|
+
const api = transport.api ?? "auto";
|
|
646
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
647
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
648
|
+
}
|
|
649
|
+
if (api === "chat") {
|
|
650
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
651
|
+
throw new Error(
|
|
652
|
+
"Responses options and native tool search require the Responses API"
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
let officialEndpoint = !baseURL;
|
|
658
|
+
try {
|
|
659
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
660
|
+
} catch {
|
|
661
|
+
}
|
|
662
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
663
|
+
}
|
|
664
|
+
function responseMetadata(response) {
|
|
665
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
666
|
+
return {
|
|
667
|
+
openai: {
|
|
668
|
+
response: {
|
|
669
|
+
id: response?.id,
|
|
670
|
+
model: response?.model,
|
|
671
|
+
status: response?.status,
|
|
672
|
+
output,
|
|
673
|
+
usage: response?.usage,
|
|
674
|
+
store: response?.store,
|
|
675
|
+
refusals: output.flatMap(
|
|
676
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
677
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
678
|
+
).map((part) => part.refusal) : []
|
|
679
|
+
)
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function responseReplayMetadata(metadata) {
|
|
685
|
+
const response = metadata?.openai?.response;
|
|
686
|
+
if (!response) return metadata;
|
|
687
|
+
const output = response.output.map((item) => {
|
|
688
|
+
const fields = ["type", "id", "status"];
|
|
689
|
+
switch (item.type) {
|
|
690
|
+
case "reasoning":
|
|
691
|
+
fields.push("encrypted_content");
|
|
692
|
+
break;
|
|
693
|
+
case "message":
|
|
694
|
+
fields.push("role", "phase");
|
|
695
|
+
break;
|
|
696
|
+
case "function_call":
|
|
697
|
+
fields.push("call_id", "name", "namespace", "arguments");
|
|
698
|
+
break;
|
|
699
|
+
case "tool_search_call":
|
|
700
|
+
fields.push("call_id", "execution", "arguments");
|
|
701
|
+
break;
|
|
702
|
+
case "tool_search_output":
|
|
703
|
+
fields.push("call_id", "tools");
|
|
704
|
+
break;
|
|
705
|
+
case "web_search_call":
|
|
706
|
+
fields.push("action");
|
|
707
|
+
break;
|
|
708
|
+
default:
|
|
709
|
+
return stripItemDiagnostics(item);
|
|
710
|
+
}
|
|
711
|
+
const replay = Object.fromEntries(
|
|
712
|
+
fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
|
|
713
|
+
);
|
|
714
|
+
if (item.type === "reasoning") replay.summary = [];
|
|
715
|
+
if (item.type === "message")
|
|
716
|
+
replay.content = Array.isArray(item.content) ? item.content.flatMap(
|
|
717
|
+
(part) => part?.type === "output_text" && typeof part.text === "string" ? [{ type: "output_text", text: part.text, annotations: [] }] : part?.type === "refusal" && typeof part.refusal === "string" ? [{ type: "refusal", refusal: part.refusal }] : []
|
|
718
|
+
) : [];
|
|
719
|
+
return stripItemDiagnostics(replay);
|
|
720
|
+
});
|
|
721
|
+
return { openai: { response: { output, refusals: [] } } };
|
|
722
|
+
}
|
|
723
|
+
function stripItemDiagnostics(item) {
|
|
724
|
+
const {
|
|
725
|
+
usage: _usage,
|
|
726
|
+
logprobs: _logprobs,
|
|
727
|
+
metadata: _metadata,
|
|
728
|
+
...state
|
|
729
|
+
} = item;
|
|
730
|
+
return state;
|
|
731
|
+
}
|
|
732
|
+
function getResponseReplay(metadata) {
|
|
733
|
+
const response = metadata?.openai?.response;
|
|
734
|
+
if (response === void 0) return void 0;
|
|
735
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
736
|
+
throw new Error(
|
|
737
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
return responseReplayMetadata(metadata)?.openai?.response?.output;
|
|
741
|
+
}
|
|
742
|
+
|
|
519
743
|
// src/core/provider-error.ts
|
|
744
|
+
function invalidProviderResponse(message) {
|
|
745
|
+
return Object.assign(new Error(message), {
|
|
746
|
+
name: "InvalidProviderResponseError",
|
|
747
|
+
status: 502,
|
|
748
|
+
code: "INVALID_PROVIDER_RESPONSE"
|
|
749
|
+
});
|
|
750
|
+
}
|
|
520
751
|
function providerErrorEvent(error, code) {
|
|
521
752
|
const details = error ?? {};
|
|
522
753
|
const event = {
|
|
@@ -592,13 +823,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
592
823
|
}));
|
|
593
824
|
}
|
|
594
825
|
shouldUseResponsesApi(request) {
|
|
595
|
-
|
|
596
|
-
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
|
|
597
|
-
return true;
|
|
598
|
-
return requiresOpenAIResponses(
|
|
826
|
+
return useOpenAIResponses(
|
|
599
827
|
request.config?.model || this.model,
|
|
600
828
|
this.config.baseUrl,
|
|
601
|
-
this.parameterOptions(request)
|
|
829
|
+
this.parameterOptions(request),
|
|
830
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
831
|
+
this.toolDefinitions(request).length > 0,
|
|
832
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
833
|
+
(message) => message.providerMetadata?.openai?.response
|
|
834
|
+
),
|
|
835
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
602
836
|
);
|
|
603
837
|
}
|
|
604
838
|
/**
|
|
@@ -631,11 +865,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
631
865
|
});
|
|
632
866
|
}
|
|
633
867
|
buildResponsesInput(request) {
|
|
634
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
|
|
868
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
635
869
|
const input = [];
|
|
636
870
|
for (const message of sourceMessages) {
|
|
637
871
|
if (message.role === "system") {
|
|
638
|
-
if (
|
|
872
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
639
873
|
input.push({
|
|
640
874
|
type: "message",
|
|
641
875
|
role: "developer",
|
|
@@ -645,6 +879,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
645
879
|
continue;
|
|
646
880
|
}
|
|
647
881
|
if (message.role === "assistant") {
|
|
882
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
883
|
+
if (replay) {
|
|
884
|
+
input.push(...replay);
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
648
887
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
649
888
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
650
889
|
input.push({
|
|
@@ -668,7 +907,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
668
907
|
input.push({
|
|
669
908
|
type: "function_call_output",
|
|
670
909
|
call_id: message.tool_call_id,
|
|
671
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
910
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
672
911
|
});
|
|
673
912
|
continue;
|
|
674
913
|
}
|
|
@@ -682,10 +921,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
682
921
|
return input;
|
|
683
922
|
}
|
|
684
923
|
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
685
|
-
formatRawMessages(messages) {
|
|
924
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
686
925
|
return messages.map((message) => {
|
|
687
|
-
const { attachments, ...rest } = message;
|
|
688
|
-
let content = message.content;
|
|
926
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
927
|
+
let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
|
|
928
|
+
if (preserveProviderMetadata && modelOutput)
|
|
929
|
+
rest.modelOutput = modelOutput;
|
|
689
930
|
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
690
931
|
content = null;
|
|
691
932
|
}
|
|
@@ -698,7 +939,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
698
939
|
content = [...parts, ...images];
|
|
699
940
|
}
|
|
700
941
|
}
|
|
701
|
-
return {
|
|
942
|
+
return {
|
|
943
|
+
...rest,
|
|
944
|
+
content,
|
|
945
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
946
|
+
};
|
|
702
947
|
});
|
|
703
948
|
}
|
|
704
949
|
/**
|
|
@@ -779,14 +1024,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
779
1024
|
).map((part) => part.text) : []
|
|
780
1025
|
).join("") : "";
|
|
781
1026
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
name
|
|
785
|
-
|
|
786
|
-
|
|
1027
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
1028
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
1029
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
1030
|
+
throw invalidProviderResponse(
|
|
1031
|
+
"Responses contains an incomplete function call"
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
if (seenCallIds.has(item.call_id))
|
|
1035
|
+
throw invalidProviderResponse(
|
|
1036
|
+
"Responses contains duplicate function call IDs"
|
|
1037
|
+
);
|
|
1038
|
+
seenCallIds.add(item.call_id);
|
|
1039
|
+
let args;
|
|
1040
|
+
try {
|
|
1041
|
+
args = JSON.parse(item.arguments);
|
|
1042
|
+
} catch {
|
|
1043
|
+
throw invalidProviderResponse(
|
|
1044
|
+
"Responses function arguments must be valid JSON"
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
1048
|
+
throw invalidProviderResponse(
|
|
1049
|
+
"Responses function arguments must be a JSON object"
|
|
1050
|
+
);
|
|
1051
|
+
return {
|
|
1052
|
+
id: item.call_id,
|
|
1053
|
+
name: item.name,
|
|
1054
|
+
args
|
|
1055
|
+
};
|
|
1056
|
+
}) : [];
|
|
787
1057
|
return {
|
|
788
1058
|
content,
|
|
789
1059
|
toolCalls,
|
|
1060
|
+
providerMetadata: responseMetadata(response),
|
|
790
1061
|
usage: response?.usage ? {
|
|
791
1062
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
792
1063
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -797,6 +1068,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
797
1068
|
}
|
|
798
1069
|
buildResponsesPayload(request, stream) {
|
|
799
1070
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1071
|
+
const options = resolveOpenAITransport(
|
|
1072
|
+
this.config,
|
|
1073
|
+
request.config?.openai
|
|
1074
|
+
).responses;
|
|
1075
|
+
const parameters = buildOpenAIParameters(
|
|
1076
|
+
request.config?.model || this.model,
|
|
1077
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1078
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1079
|
+
this.parameterOptions(request),
|
|
1080
|
+
"responses"
|
|
1081
|
+
);
|
|
800
1082
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
801
1083
|
request.config?.responseFormat
|
|
802
1084
|
);
|
|
@@ -812,13 +1094,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
812
1094
|
),
|
|
813
1095
|
tool_choice: openaiToolOptions?.toolChoice,
|
|
814
1096
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
815
|
-
...
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1097
|
+
...parameters,
|
|
1098
|
+
...options?.reasoningSummary ? {
|
|
1099
|
+
reasoning: {
|
|
1100
|
+
...parameters.reasoning,
|
|
1101
|
+
summary: options.reasoningSummary
|
|
1102
|
+
}
|
|
1103
|
+
} : {},
|
|
1104
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1105
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1106
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1107
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1108
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1109
|
+
...options?.store === false || options?.include ? {
|
|
1110
|
+
include: [
|
|
1111
|
+
.../* @__PURE__ */ new Set([
|
|
1112
|
+
...options?.include ?? [],
|
|
1113
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1114
|
+
])
|
|
1115
|
+
]
|
|
1116
|
+
} : {},
|
|
822
1117
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
823
1118
|
stream
|
|
824
1119
|
};
|
|
@@ -877,6 +1172,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
877
1172
|
let usage;
|
|
878
1173
|
const citations = [];
|
|
879
1174
|
let completed = false;
|
|
1175
|
+
let terminalResponse;
|
|
1176
|
+
let responseIdentity = {};
|
|
1177
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1178
|
+
let streamedText = "";
|
|
880
1179
|
function* flushToolCalls() {
|
|
881
1180
|
for (const call of toolCalls.values()) {
|
|
882
1181
|
if (!call.name)
|
|
@@ -893,17 +1192,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
893
1192
|
for await (const event of stream) {
|
|
894
1193
|
throwIfAborted(request.signal);
|
|
895
1194
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1195
|
+
if (completed)
|
|
1196
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
896
1197
|
const index = event?.output_index ?? 0;
|
|
897
1198
|
switch (event?.type) {
|
|
1199
|
+
case "response.created":
|
|
1200
|
+
responseIdentity = {
|
|
1201
|
+
id: event.response?.id,
|
|
1202
|
+
model: event.response?.model
|
|
1203
|
+
};
|
|
1204
|
+
break;
|
|
1205
|
+
case "response.output_item.done":
|
|
1206
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1207
|
+
if (event.item?.type === "function_call") {
|
|
1208
|
+
toolCalls.set(index, {
|
|
1209
|
+
id: event.item.call_id,
|
|
1210
|
+
name: event.item.name,
|
|
1211
|
+
arguments: event.item.arguments
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
break;
|
|
898
1215
|
case "response.output_text.delta":
|
|
899
1216
|
if (event.delta) {
|
|
1217
|
+
streamedText += event.delta;
|
|
900
1218
|
yield { type: "message:delta", content: event.delta };
|
|
901
1219
|
}
|
|
902
1220
|
break;
|
|
903
1221
|
case "response.output_item.added":
|
|
904
1222
|
if (event.item?.type === "function_call") {
|
|
905
1223
|
toolCalls.set(index, {
|
|
906
|
-
id: event.item.call_id ??
|
|
1224
|
+
id: event.item.call_id ?? "",
|
|
907
1225
|
name: event.item.name ?? "",
|
|
908
1226
|
arguments: event.item.arguments ?? ""
|
|
909
1227
|
});
|
|
@@ -922,7 +1240,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
922
1240
|
break;
|
|
923
1241
|
}
|
|
924
1242
|
case "response.completed": {
|
|
1243
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1244
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
925
1245
|
completed = true;
|
|
1246
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
926
1247
|
const found = this.collectResponsesCitations(
|
|
927
1248
|
event.response?.output,
|
|
928
1249
|
citations.length
|
|
@@ -959,12 +1280,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
959
1280
|
throwIfAborted(request.signal);
|
|
960
1281
|
if (!completed)
|
|
961
1282
|
throw new Error("Responses stream ended without a completed response");
|
|
1283
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1284
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1285
|
+
toolCalls.clear();
|
|
1286
|
+
for (const [index, item] of output.entries()) {
|
|
1287
|
+
if (item.type === "function_call") {
|
|
1288
|
+
toolCalls.set(index, {
|
|
1289
|
+
id: item.call_id,
|
|
1290
|
+
name: item.name,
|
|
1291
|
+
arguments: item.arguments
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1296
|
+
for (const call of toolCalls.values()) {
|
|
1297
|
+
if (!call.id || !call.name)
|
|
1298
|
+
throw invalidProviderResponse(
|
|
1299
|
+
"Responses stream contains an incomplete tool call"
|
|
1300
|
+
);
|
|
1301
|
+
uniqueCalls.set(call.id, call);
|
|
1302
|
+
}
|
|
1303
|
+
toolCalls.clear();
|
|
1304
|
+
[...uniqueCalls.values()].forEach(
|
|
1305
|
+
(call, index) => toolCalls.set(index, call)
|
|
1306
|
+
);
|
|
1307
|
+
if (!output.length && toolCalls.size) {
|
|
1308
|
+
for (const call of toolCalls.values())
|
|
1309
|
+
output.push({
|
|
1310
|
+
type: "function_call",
|
|
1311
|
+
call_id: call.id,
|
|
1312
|
+
name: call.name,
|
|
1313
|
+
arguments: call.arguments
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
if (!output.length && streamedText)
|
|
1317
|
+
output.push({
|
|
1318
|
+
type: "message",
|
|
1319
|
+
role: "assistant",
|
|
1320
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1321
|
+
});
|
|
1322
|
+
terminalResponse.output = output;
|
|
1323
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1324
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1325
|
+
yield {
|
|
1326
|
+
type: "message:delta",
|
|
1327
|
+
content: finalText.slice(streamedText.length)
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
962
1330
|
yield* flushToolCalls();
|
|
963
1331
|
if (citations.length > 0) {
|
|
964
1332
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
965
1333
|
}
|
|
966
|
-
yield {
|
|
967
|
-
|
|
1334
|
+
yield {
|
|
1335
|
+
type: "message:end",
|
|
1336
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1337
|
+
};
|
|
1338
|
+
yield {
|
|
1339
|
+
type: "done",
|
|
1340
|
+
usage,
|
|
1341
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1342
|
+
};
|
|
968
1343
|
}
|
|
969
1344
|
async *stream(request) {
|
|
970
1345
|
throwIfAborted(request.signal);
|
|
@@ -1350,7 +1725,7 @@ var AnthropicAdapter = class {
|
|
|
1350
1725
|
}
|
|
1351
1726
|
pendingToolResults.push({
|
|
1352
1727
|
tool_use_id: toolCallId,
|
|
1353
|
-
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1728
|
+
content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1354
1729
|
});
|
|
1355
1730
|
} else if (msg.role === "user") {
|
|
1356
1731
|
if (pendingToolResults.length > 0) {
|
|
@@ -1471,14 +1846,23 @@ var AnthropicAdapter = class {
|
|
|
1471
1846
|
buildRequestOptions(request) {
|
|
1472
1847
|
const responseFormat = request.config?.responseFormat;
|
|
1473
1848
|
const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
|
|
1474
|
-
|
|
1849
|
+
let systemMessage;
|
|
1475
1850
|
let messages;
|
|
1476
1851
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1852
|
+
systemMessage = extractAnthropicSystemPrompt(
|
|
1853
|
+
request.rawMessages,
|
|
1854
|
+
request.systemPrompt
|
|
1855
|
+
);
|
|
1477
1856
|
messages = this.convertToAnthropicMessages(request.rawMessages);
|
|
1478
1857
|
} else {
|
|
1479
|
-
const formatted = formatMessagesForAnthropic(
|
|
1858
|
+
const formatted = formatMessagesForAnthropic(
|
|
1859
|
+
request.messages,
|
|
1860
|
+
request.systemPrompt
|
|
1861
|
+
);
|
|
1862
|
+
systemMessage = formatted.system;
|
|
1480
1863
|
messages = formatted.messages;
|
|
1481
1864
|
}
|
|
1865
|
+
systemMessage += jsonObjectSuffix;
|
|
1482
1866
|
const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
|
|
1483
1867
|
const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
|
|
1484
1868
|
request.toolDefinitions ?? [],
|
|
@@ -1821,7 +2205,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
|
|
|
1821
2205
|
for (const msg of messages) {
|
|
1822
2206
|
const baseMessage = {
|
|
1823
2207
|
role: msg.role,
|
|
1824
|
-
content: msg.content ?? ""
|
|
2208
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
|
|
1825
2209
|
};
|
|
1826
2210
|
if (msg.role === "user" && msg.metadata?.attachments) {
|
|
1827
2211
|
const images = await extractImagesFromAttachments(
|
|
@@ -1858,7 +2242,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
|
|
|
1858
2242
|
for (const msg of rawMessages) {
|
|
1859
2243
|
const baseMessage = {
|
|
1860
2244
|
role: msg.role,
|
|
1861
|
-
content: msg.content ?? ""
|
|
2245
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
|
|
1862
2246
|
};
|
|
1863
2247
|
if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
|
|
1864
2248
|
const attachments = msg.attachments;
|
|
@@ -2068,6 +2452,10 @@ function messageToGeminiContent(msg) {
|
|
|
2068
2452
|
if (msg.role === "system") return null;
|
|
2069
2453
|
const parts = [];
|
|
2070
2454
|
if (msg.role === "tool" && msg.tool_call_id) {
|
|
2455
|
+
msg = {
|
|
2456
|
+
...msg,
|
|
2457
|
+
content: textToolOutput(msg.modelOutput, msg.content ?? "")
|
|
2458
|
+
};
|
|
2071
2459
|
let responseData;
|
|
2072
2460
|
try {
|
|
2073
2461
|
responseData = JSON.parse(msg.content || "{}");
|
|
@@ -2528,6 +2916,13 @@ var AzureAdapter = class {
|
|
|
2528
2916
|
let messages;
|
|
2529
2917
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2530
2918
|
const processedMessages = request.rawMessages.map((msg) => {
|
|
2919
|
+
const { modelOutput, ...rest } = msg;
|
|
2920
|
+
msg = {
|
|
2921
|
+
...rest,
|
|
2922
|
+
...msg.role === "tool" ? {
|
|
2923
|
+
content: textToolOutput(modelOutput, String(msg.content ?? ""))
|
|
2924
|
+
} : {}
|
|
2925
|
+
};
|
|
2531
2926
|
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
2532
2927
|
if (hasAttachments) {
|
|
2533
2928
|
const content = [];
|
|
@@ -2658,7 +3053,10 @@ var AzureAdapter = class {
|
|
|
2658
3053
|
const client = await this.getClient();
|
|
2659
3054
|
let messages;
|
|
2660
3055
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2661
|
-
messages = request.rawMessages
|
|
3056
|
+
messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
|
|
3057
|
+
...msg,
|
|
3058
|
+
...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
|
|
3059
|
+
}));
|
|
2662
3060
|
if (request.systemPrompt) {
|
|
2663
3061
|
const hasSystem = messages.some((m) => m.role === "system");
|
|
2664
3062
|
if (!hasSystem) {
|