@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
|
@@ -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);
|
|
@@ -239,7 +342,7 @@ function messageToOpenAIContent(message) {
|
|
|
239
342
|
}
|
|
240
343
|
return blocks;
|
|
241
344
|
}
|
|
242
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
345
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
243
346
|
const formatted = [];
|
|
244
347
|
if (systemPrompt) {
|
|
245
348
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -257,7 +360,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
257
360
|
const assistantMsg = {
|
|
258
361
|
role: "assistant",
|
|
259
362
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
260
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
363
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
364
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
261
365
|
};
|
|
262
366
|
if (hasToolCalls) {
|
|
263
367
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -266,7 +370,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
266
370
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
267
371
|
formatted.push({
|
|
268
372
|
role: "tool",
|
|
269
|
-
content: msg.content ?? "",
|
|
373
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
374
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
270
375
|
tool_call_id: msg.tool_call_id
|
|
271
376
|
});
|
|
272
377
|
}
|
|
@@ -553,7 +658,7 @@ function formatMessagesForGoogle(messages) {
|
|
|
553
658
|
return {
|
|
554
659
|
role: "tool",
|
|
555
660
|
tool_call_id: msg.toolCallId,
|
|
556
|
-
content: msg.content
|
|
661
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
557
662
|
};
|
|
558
663
|
default:
|
|
559
664
|
return msg;
|
|
@@ -561,6 +666,64 @@ function formatMessagesForGoogle(messages) {
|
|
|
561
666
|
});
|
|
562
667
|
}
|
|
563
668
|
|
|
669
|
+
// src/core/openai-responses.ts
|
|
670
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
671
|
+
return {
|
|
672
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
673
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
677
|
+
const api = transport.api ?? "auto";
|
|
678
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
679
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
680
|
+
}
|
|
681
|
+
if (api === "chat") {
|
|
682
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
683
|
+
throw new Error(
|
|
684
|
+
"Responses options and native tool search require the Responses API"
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
let officialEndpoint = !baseURL;
|
|
690
|
+
try {
|
|
691
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
692
|
+
} catch {
|
|
693
|
+
}
|
|
694
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
695
|
+
}
|
|
696
|
+
function responseMetadata(response) {
|
|
697
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
698
|
+
return {
|
|
699
|
+
openai: {
|
|
700
|
+
response: {
|
|
701
|
+
id: response?.id,
|
|
702
|
+
model: response?.model,
|
|
703
|
+
status: response?.status,
|
|
704
|
+
output,
|
|
705
|
+
usage: response?.usage,
|
|
706
|
+
store: response?.store,
|
|
707
|
+
refusals: output.flatMap(
|
|
708
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
709
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
710
|
+
).map((part) => part.refusal) : []
|
|
711
|
+
)
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
function getResponseReplay(metadata) {
|
|
717
|
+
const response = metadata?.openai?.response;
|
|
718
|
+
if (response === void 0) return void 0;
|
|
719
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
720
|
+
throw new Error(
|
|
721
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
return response.output;
|
|
725
|
+
}
|
|
726
|
+
|
|
564
727
|
// src/core/provider-error.ts
|
|
565
728
|
function providerErrorEvent(error, code) {
|
|
566
729
|
const details = error ?? {};
|
|
@@ -637,13 +800,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
637
800
|
}));
|
|
638
801
|
}
|
|
639
802
|
shouldUseResponsesApi(request) {
|
|
640
|
-
|
|
641
|
-
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
|
|
642
|
-
return true;
|
|
643
|
-
return requiresOpenAIResponses(
|
|
803
|
+
return useOpenAIResponses(
|
|
644
804
|
request.config?.model || this.model,
|
|
645
805
|
this.config.baseUrl,
|
|
646
|
-
this.parameterOptions(request)
|
|
806
|
+
this.parameterOptions(request),
|
|
807
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
808
|
+
this.toolDefinitions(request).length > 0,
|
|
809
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
810
|
+
(message) => message.providerMetadata?.openai?.response
|
|
811
|
+
),
|
|
812
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
647
813
|
);
|
|
648
814
|
}
|
|
649
815
|
/**
|
|
@@ -676,11 +842,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
676
842
|
});
|
|
677
843
|
}
|
|
678
844
|
buildResponsesInput(request) {
|
|
679
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
|
|
845
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
680
846
|
const input = [];
|
|
681
847
|
for (const message of sourceMessages) {
|
|
682
848
|
if (message.role === "system") {
|
|
683
|
-
if (
|
|
849
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
684
850
|
input.push({
|
|
685
851
|
type: "message",
|
|
686
852
|
role: "developer",
|
|
@@ -690,6 +856,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
690
856
|
continue;
|
|
691
857
|
}
|
|
692
858
|
if (message.role === "assistant") {
|
|
859
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
860
|
+
if (replay) {
|
|
861
|
+
input.push(...replay);
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
693
864
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
694
865
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
695
866
|
input.push({
|
|
@@ -713,7 +884,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
713
884
|
input.push({
|
|
714
885
|
type: "function_call_output",
|
|
715
886
|
call_id: message.tool_call_id,
|
|
716
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
887
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
717
888
|
});
|
|
718
889
|
continue;
|
|
719
890
|
}
|
|
@@ -727,10 +898,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
727
898
|
return input;
|
|
728
899
|
}
|
|
729
900
|
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
730
|
-
formatRawMessages(messages) {
|
|
901
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
731
902
|
return messages.map((message) => {
|
|
732
|
-
const { attachments, ...rest } = message;
|
|
733
|
-
let content = message.content;
|
|
903
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
904
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
905
|
+
if (preserveProviderMetadata && modelOutput)
|
|
906
|
+
rest.modelOutput = modelOutput;
|
|
734
907
|
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
735
908
|
content = null;
|
|
736
909
|
}
|
|
@@ -743,7 +916,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
743
916
|
content = [...parts, ...images];
|
|
744
917
|
}
|
|
745
918
|
}
|
|
746
|
-
return {
|
|
919
|
+
return {
|
|
920
|
+
...rest,
|
|
921
|
+
content,
|
|
922
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
923
|
+
};
|
|
747
924
|
});
|
|
748
925
|
}
|
|
749
926
|
/**
|
|
@@ -824,14 +1001,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
824
1001
|
).map((part) => part.text) : []
|
|
825
1002
|
).join("") : "";
|
|
826
1003
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
827
|
-
const
|
|
828
|
-
|
|
829
|
-
name
|
|
830
|
-
|
|
831
|
-
|
|
1004
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
1005
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
1006
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
1007
|
+
throw new Error("Responses contains an incomplete function call");
|
|
1008
|
+
}
|
|
1009
|
+
if (seenCallIds.has(item.call_id))
|
|
1010
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
1011
|
+
seenCallIds.add(item.call_id);
|
|
1012
|
+
const args = JSON.parse(item.arguments);
|
|
1013
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
1014
|
+
throw new Error(
|
|
1015
|
+
"Responses function arguments must be a JSON object"
|
|
1016
|
+
);
|
|
1017
|
+
return { id: item.call_id, name: item.name, args };
|
|
1018
|
+
}) : [];
|
|
832
1019
|
return {
|
|
833
1020
|
content,
|
|
834
1021
|
toolCalls,
|
|
1022
|
+
providerMetadata: responseMetadata(response),
|
|
835
1023
|
usage: response?.usage ? {
|
|
836
1024
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
837
1025
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -842,6 +1030,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
842
1030
|
}
|
|
843
1031
|
buildResponsesPayload(request, stream) {
|
|
844
1032
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1033
|
+
const options = resolveOpenAITransport(
|
|
1034
|
+
this.config,
|
|
1035
|
+
request.config?.openai
|
|
1036
|
+
).responses;
|
|
1037
|
+
const parameters = buildOpenAIParameters(
|
|
1038
|
+
request.config?.model || this.model,
|
|
1039
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1040
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1041
|
+
this.parameterOptions(request),
|
|
1042
|
+
"responses"
|
|
1043
|
+
);
|
|
845
1044
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
846
1045
|
request.config?.responseFormat
|
|
847
1046
|
);
|
|
@@ -857,13 +1056,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
857
1056
|
),
|
|
858
1057
|
tool_choice: openaiToolOptions?.toolChoice,
|
|
859
1058
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
860
|
-
...
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1059
|
+
...parameters,
|
|
1060
|
+
...options?.reasoningSummary ? {
|
|
1061
|
+
reasoning: {
|
|
1062
|
+
...parameters.reasoning,
|
|
1063
|
+
summary: options.reasoningSummary
|
|
1064
|
+
}
|
|
1065
|
+
} : {},
|
|
1066
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1067
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1068
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1069
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1070
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1071
|
+
...options?.store === false || options?.include ? {
|
|
1072
|
+
include: [
|
|
1073
|
+
.../* @__PURE__ */ new Set([
|
|
1074
|
+
...options?.include ?? [],
|
|
1075
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1076
|
+
])
|
|
1077
|
+
]
|
|
1078
|
+
} : {},
|
|
867
1079
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
868
1080
|
stream
|
|
869
1081
|
};
|
|
@@ -922,6 +1134,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
922
1134
|
let usage;
|
|
923
1135
|
const citations = [];
|
|
924
1136
|
let completed = false;
|
|
1137
|
+
let terminalResponse;
|
|
1138
|
+
let responseIdentity = {};
|
|
1139
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1140
|
+
let streamedText = "";
|
|
925
1141
|
function* flushToolCalls() {
|
|
926
1142
|
for (const call of toolCalls.values()) {
|
|
927
1143
|
if (!call.name)
|
|
@@ -938,17 +1154,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
938
1154
|
for await (const event of stream) {
|
|
939
1155
|
throwIfAborted(request.signal);
|
|
940
1156
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1157
|
+
if (completed)
|
|
1158
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
941
1159
|
const index = event?.output_index ?? 0;
|
|
942
1160
|
switch (event?.type) {
|
|
1161
|
+
case "response.created":
|
|
1162
|
+
responseIdentity = {
|
|
1163
|
+
id: event.response?.id,
|
|
1164
|
+
model: event.response?.model
|
|
1165
|
+
};
|
|
1166
|
+
break;
|
|
1167
|
+
case "response.output_item.done":
|
|
1168
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1169
|
+
if (event.item?.type === "function_call") {
|
|
1170
|
+
toolCalls.set(index, {
|
|
1171
|
+
id: event.item.call_id,
|
|
1172
|
+
name: event.item.name,
|
|
1173
|
+
arguments: event.item.arguments
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
break;
|
|
943
1177
|
case "response.output_text.delta":
|
|
944
1178
|
if (event.delta) {
|
|
1179
|
+
streamedText += event.delta;
|
|
945
1180
|
yield { type: "message:delta", content: event.delta };
|
|
946
1181
|
}
|
|
947
1182
|
break;
|
|
948
1183
|
case "response.output_item.added":
|
|
949
1184
|
if (event.item?.type === "function_call") {
|
|
950
1185
|
toolCalls.set(index, {
|
|
951
|
-
id: event.item.call_id ??
|
|
1186
|
+
id: event.item.call_id ?? "",
|
|
952
1187
|
name: event.item.name ?? "",
|
|
953
1188
|
arguments: event.item.arguments ?? ""
|
|
954
1189
|
});
|
|
@@ -967,7 +1202,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
967
1202
|
break;
|
|
968
1203
|
}
|
|
969
1204
|
case "response.completed": {
|
|
1205
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1206
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
970
1207
|
completed = true;
|
|
1208
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
971
1209
|
const found = this.collectResponsesCitations(
|
|
972
1210
|
event.response?.output,
|
|
973
1211
|
citations.length
|
|
@@ -1004,12 +1242,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1004
1242
|
throwIfAborted(request.signal);
|
|
1005
1243
|
if (!completed)
|
|
1006
1244
|
throw new Error("Responses stream ended without a completed response");
|
|
1245
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1246
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1247
|
+
toolCalls.clear();
|
|
1248
|
+
for (const [index, item] of output.entries()) {
|
|
1249
|
+
if (item.type === "function_call") {
|
|
1250
|
+
toolCalls.set(index, {
|
|
1251
|
+
id: item.call_id,
|
|
1252
|
+
name: item.name,
|
|
1253
|
+
arguments: item.arguments
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1258
|
+
for (const call of toolCalls.values()) {
|
|
1259
|
+
if (!call.id || !call.name)
|
|
1260
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1261
|
+
uniqueCalls.set(call.id, call);
|
|
1262
|
+
}
|
|
1263
|
+
toolCalls.clear();
|
|
1264
|
+
[...uniqueCalls.values()].forEach(
|
|
1265
|
+
(call, index) => toolCalls.set(index, call)
|
|
1266
|
+
);
|
|
1267
|
+
if (!output.length && toolCalls.size) {
|
|
1268
|
+
for (const call of toolCalls.values())
|
|
1269
|
+
output.push({
|
|
1270
|
+
type: "function_call",
|
|
1271
|
+
call_id: call.id,
|
|
1272
|
+
name: call.name,
|
|
1273
|
+
arguments: call.arguments
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
if (!output.length && streamedText)
|
|
1277
|
+
output.push({
|
|
1278
|
+
type: "message",
|
|
1279
|
+
role: "assistant",
|
|
1280
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1281
|
+
});
|
|
1282
|
+
terminalResponse.output = output;
|
|
1283
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1284
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1285
|
+
yield {
|
|
1286
|
+
type: "message:delta",
|
|
1287
|
+
content: finalText.slice(streamedText.length)
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1007
1290
|
yield* flushToolCalls();
|
|
1008
1291
|
if (citations.length > 0) {
|
|
1009
1292
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
1010
1293
|
}
|
|
1011
|
-
yield {
|
|
1012
|
-
|
|
1294
|
+
yield {
|
|
1295
|
+
type: "message:end",
|
|
1296
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1297
|
+
};
|
|
1298
|
+
yield {
|
|
1299
|
+
type: "done",
|
|
1300
|
+
usage,
|
|
1301
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1302
|
+
};
|
|
1013
1303
|
}
|
|
1014
1304
|
async *stream(request) {
|
|
1015
1305
|
throwIfAborted(request.signal);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { c as OllamaProviderConfig, A as AIProvider } from '../../types-
|
|
2
|
-
export { d as OllamaModelOptions } from '../../types-
|
|
3
|
-
import '../../types-
|
|
1
|
+
import { c as OllamaProviderConfig, A as AIProvider } from '../../types-DHQEMFHA.mjs';
|
|
2
|
+
export { d as OllamaModelOptions } from '../../types-DHQEMFHA.mjs';
|
|
3
|
+
import '../../types-Dg1Bcg1k.mjs';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import '../../base-
|
|
5
|
+
import '../../base-BICaQaRS.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Ollama Provider
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { c as OllamaProviderConfig, A as AIProvider } from '../../types-
|
|
2
|
-
export { d as OllamaModelOptions } from '../../types-
|
|
3
|
-
import '../../types-
|
|
1
|
+
import { c as OllamaProviderConfig, A as AIProvider } from '../../types-Dikj3En4.js';
|
|
2
|
+
export { d as OllamaModelOptions } from '../../types-Dikj3En4.js';
|
|
3
|
+
import '../../types-Dg1Bcg1k.js';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import '../../base-
|
|
5
|
+
import '../../base-CpRPB8u8.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Ollama Provider
|
|
@@ -1,5 +1,90 @@
|
|
|
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: "text",
|
|
76
|
+
text: part.text
|
|
77
|
+
};
|
|
78
|
+
throw new Error(
|
|
79
|
+
"Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function textToolOutput(value, fallback) {
|
|
84
|
+
if (value === void 0) return fallback;
|
|
85
|
+
return toolOutputForProvider(value).map((part) => part.text).join("\n");
|
|
86
|
+
}
|
|
87
|
+
|
|
3
88
|
// src/core/utils.ts
|
|
4
89
|
function generateId(prefix = "id") {
|
|
5
90
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -150,7 +235,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
|
|
|
150
235
|
for (const msg of messages) {
|
|
151
236
|
const baseMessage = {
|
|
152
237
|
role: msg.role,
|
|
153
|
-
content: msg.content ?? ""
|
|
238
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
|
|
154
239
|
};
|
|
155
240
|
if (msg.role === "user" && msg.metadata?.attachments) {
|
|
156
241
|
const images = await extractImagesFromAttachments(
|
|
@@ -187,7 +272,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
|
|
|
187
272
|
for (const msg of rawMessages) {
|
|
188
273
|
const baseMessage = {
|
|
189
274
|
role: msg.role,
|
|
190
|
-
content: msg.content ?? ""
|
|
275
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
|
|
191
276
|
};
|
|
192
277
|
if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
|
|
193
278
|
const attachments = msg.attachments;
|