@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
|
@@ -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);
|
|
@@ -241,7 +344,7 @@ function messageToOpenAIContent(message) {
|
|
|
241
344
|
}
|
|
242
345
|
return blocks;
|
|
243
346
|
}
|
|
244
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
347
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
245
348
|
const formatted = [];
|
|
246
349
|
if (systemPrompt) {
|
|
247
350
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -259,7 +362,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
259
362
|
const assistantMsg = {
|
|
260
363
|
role: "assistant",
|
|
261
364
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
262
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
365
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
366
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
263
367
|
};
|
|
264
368
|
if (hasToolCalls) {
|
|
265
369
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -268,7 +372,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
268
372
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
269
373
|
formatted.push({
|
|
270
374
|
role: "tool",
|
|
271
|
-
content: msg.content ?? "",
|
|
375
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
376
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
272
377
|
tool_call_id: msg.tool_call_id
|
|
273
378
|
});
|
|
274
379
|
}
|
|
@@ -480,7 +585,7 @@ function formatMessages(messages) {
|
|
|
480
585
|
return {
|
|
481
586
|
role: "tool",
|
|
482
587
|
tool_call_id: msg.toolCallId,
|
|
483
|
-
content: msg.content
|
|
588
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
484
589
|
};
|
|
485
590
|
default:
|
|
486
591
|
return msg;
|
|
@@ -488,7 +593,120 @@ function formatMessages(messages) {
|
|
|
488
593
|
});
|
|
489
594
|
}
|
|
490
595
|
|
|
596
|
+
// src/core/openai-responses.ts
|
|
597
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
598
|
+
return {
|
|
599
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
600
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
604
|
+
const api = transport.api ?? "auto";
|
|
605
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
606
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
607
|
+
}
|
|
608
|
+
if (api === "chat") {
|
|
609
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
610
|
+
throw new Error(
|
|
611
|
+
"Responses options and native tool search require the Responses API"
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
let officialEndpoint = !baseURL;
|
|
617
|
+
try {
|
|
618
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
619
|
+
} catch {
|
|
620
|
+
}
|
|
621
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
622
|
+
}
|
|
623
|
+
function responseMetadata(response) {
|
|
624
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
625
|
+
return {
|
|
626
|
+
openai: {
|
|
627
|
+
response: {
|
|
628
|
+
id: response?.id,
|
|
629
|
+
model: response?.model,
|
|
630
|
+
status: response?.status,
|
|
631
|
+
output,
|
|
632
|
+
usage: response?.usage,
|
|
633
|
+
store: response?.store,
|
|
634
|
+
refusals: output.flatMap(
|
|
635
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
636
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
637
|
+
).map((part) => part.refusal) : []
|
|
638
|
+
)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function responseReplayMetadata(metadata) {
|
|
644
|
+
const response = metadata?.openai?.response;
|
|
645
|
+
if (!response) return metadata;
|
|
646
|
+
const output = response.output.map((item) => {
|
|
647
|
+
const fields = ["type", "id", "status"];
|
|
648
|
+
switch (item.type) {
|
|
649
|
+
case "reasoning":
|
|
650
|
+
fields.push("encrypted_content");
|
|
651
|
+
break;
|
|
652
|
+
case "message":
|
|
653
|
+
fields.push("role", "phase");
|
|
654
|
+
break;
|
|
655
|
+
case "function_call":
|
|
656
|
+
fields.push("call_id", "name", "namespace", "arguments");
|
|
657
|
+
break;
|
|
658
|
+
case "tool_search_call":
|
|
659
|
+
fields.push("call_id", "execution", "arguments");
|
|
660
|
+
break;
|
|
661
|
+
case "tool_search_output":
|
|
662
|
+
fields.push("call_id", "tools");
|
|
663
|
+
break;
|
|
664
|
+
case "web_search_call":
|
|
665
|
+
fields.push("action");
|
|
666
|
+
break;
|
|
667
|
+
default:
|
|
668
|
+
return stripItemDiagnostics(item);
|
|
669
|
+
}
|
|
670
|
+
const replay = Object.fromEntries(
|
|
671
|
+
fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
|
|
672
|
+
);
|
|
673
|
+
if (item.type === "reasoning") replay.summary = [];
|
|
674
|
+
if (item.type === "message")
|
|
675
|
+
replay.content = Array.isArray(item.content) ? item.content.flatMap(
|
|
676
|
+
(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 }] : []
|
|
677
|
+
) : [];
|
|
678
|
+
return stripItemDiagnostics(replay);
|
|
679
|
+
});
|
|
680
|
+
return { openai: { response: { output, refusals: [] } } };
|
|
681
|
+
}
|
|
682
|
+
function stripItemDiagnostics(item) {
|
|
683
|
+
const {
|
|
684
|
+
usage: _usage,
|
|
685
|
+
logprobs: _logprobs,
|
|
686
|
+
metadata: _metadata,
|
|
687
|
+
...state
|
|
688
|
+
} = item;
|
|
689
|
+
return state;
|
|
690
|
+
}
|
|
691
|
+
function getResponseReplay(metadata) {
|
|
692
|
+
const response = metadata?.openai?.response;
|
|
693
|
+
if (response === void 0) return void 0;
|
|
694
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
return responseReplayMetadata(metadata)?.openai?.response?.output;
|
|
700
|
+
}
|
|
701
|
+
|
|
491
702
|
// src/core/provider-error.ts
|
|
703
|
+
function invalidProviderResponse(message) {
|
|
704
|
+
return Object.assign(new Error(message), {
|
|
705
|
+
name: "InvalidProviderResponseError",
|
|
706
|
+
status: 502,
|
|
707
|
+
code: "INVALID_PROVIDER_RESPONSE"
|
|
708
|
+
});
|
|
709
|
+
}
|
|
492
710
|
function providerErrorEvent(error, code) {
|
|
493
711
|
const details = error ?? {};
|
|
494
712
|
const event = {
|
|
@@ -564,13 +782,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
564
782
|
}));
|
|
565
783
|
}
|
|
566
784
|
shouldUseResponsesApi(request) {
|
|
567
|
-
|
|
568
|
-
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
|
|
569
|
-
return true;
|
|
570
|
-
return requiresOpenAIResponses(
|
|
785
|
+
return useOpenAIResponses(
|
|
571
786
|
request.config?.model || this.model,
|
|
572
787
|
this.config.baseUrl,
|
|
573
|
-
this.parameterOptions(request)
|
|
788
|
+
this.parameterOptions(request),
|
|
789
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
790
|
+
this.toolDefinitions(request).length > 0,
|
|
791
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
792
|
+
(message) => message.providerMetadata?.openai?.response
|
|
793
|
+
),
|
|
794
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
574
795
|
);
|
|
575
796
|
}
|
|
576
797
|
/**
|
|
@@ -603,11 +824,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
603
824
|
});
|
|
604
825
|
}
|
|
605
826
|
buildResponsesInput(request) {
|
|
606
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
|
|
827
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
607
828
|
const input = [];
|
|
608
829
|
for (const message of sourceMessages) {
|
|
609
830
|
if (message.role === "system") {
|
|
610
|
-
if (
|
|
831
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
611
832
|
input.push({
|
|
612
833
|
type: "message",
|
|
613
834
|
role: "developer",
|
|
@@ -617,6 +838,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
617
838
|
continue;
|
|
618
839
|
}
|
|
619
840
|
if (message.role === "assistant") {
|
|
841
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
842
|
+
if (replay) {
|
|
843
|
+
input.push(...replay);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
620
846
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
621
847
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
622
848
|
input.push({
|
|
@@ -640,7 +866,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
640
866
|
input.push({
|
|
641
867
|
type: "function_call_output",
|
|
642
868
|
call_id: message.tool_call_id,
|
|
643
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
869
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
644
870
|
});
|
|
645
871
|
continue;
|
|
646
872
|
}
|
|
@@ -654,10 +880,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
654
880
|
return input;
|
|
655
881
|
}
|
|
656
882
|
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
657
|
-
formatRawMessages(messages) {
|
|
883
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
658
884
|
return messages.map((message) => {
|
|
659
|
-
const { attachments, ...rest } = message;
|
|
660
|
-
let content = message.content;
|
|
885
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
886
|
+
let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
|
|
887
|
+
if (preserveProviderMetadata && modelOutput)
|
|
888
|
+
rest.modelOutput = modelOutput;
|
|
661
889
|
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
662
890
|
content = null;
|
|
663
891
|
}
|
|
@@ -670,7 +898,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
670
898
|
content = [...parts, ...images];
|
|
671
899
|
}
|
|
672
900
|
}
|
|
673
|
-
return {
|
|
901
|
+
return {
|
|
902
|
+
...rest,
|
|
903
|
+
content,
|
|
904
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
905
|
+
};
|
|
674
906
|
});
|
|
675
907
|
}
|
|
676
908
|
/**
|
|
@@ -751,14 +983,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
751
983
|
).map((part) => part.text) : []
|
|
752
984
|
).join("") : "";
|
|
753
985
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
name
|
|
757
|
-
|
|
758
|
-
|
|
986
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
987
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
988
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
989
|
+
throw invalidProviderResponse(
|
|
990
|
+
"Responses contains an incomplete function call"
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
if (seenCallIds.has(item.call_id))
|
|
994
|
+
throw invalidProviderResponse(
|
|
995
|
+
"Responses contains duplicate function call IDs"
|
|
996
|
+
);
|
|
997
|
+
seenCallIds.add(item.call_id);
|
|
998
|
+
let args;
|
|
999
|
+
try {
|
|
1000
|
+
args = JSON.parse(item.arguments);
|
|
1001
|
+
} catch {
|
|
1002
|
+
throw invalidProviderResponse(
|
|
1003
|
+
"Responses function arguments must be valid JSON"
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
1007
|
+
throw invalidProviderResponse(
|
|
1008
|
+
"Responses function arguments must be a JSON object"
|
|
1009
|
+
);
|
|
1010
|
+
return {
|
|
1011
|
+
id: item.call_id,
|
|
1012
|
+
name: item.name,
|
|
1013
|
+
args
|
|
1014
|
+
};
|
|
1015
|
+
}) : [];
|
|
759
1016
|
return {
|
|
760
1017
|
content,
|
|
761
1018
|
toolCalls,
|
|
1019
|
+
providerMetadata: responseMetadata(response),
|
|
762
1020
|
usage: response?.usage ? {
|
|
763
1021
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
764
1022
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -769,6 +1027,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
769
1027
|
}
|
|
770
1028
|
buildResponsesPayload(request, stream) {
|
|
771
1029
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1030
|
+
const options = resolveOpenAITransport(
|
|
1031
|
+
this.config,
|
|
1032
|
+
request.config?.openai
|
|
1033
|
+
).responses;
|
|
1034
|
+
const parameters = buildOpenAIParameters(
|
|
1035
|
+
request.config?.model || this.model,
|
|
1036
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1037
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1038
|
+
this.parameterOptions(request),
|
|
1039
|
+
"responses"
|
|
1040
|
+
);
|
|
772
1041
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
773
1042
|
request.config?.responseFormat
|
|
774
1043
|
);
|
|
@@ -784,13 +1053,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
784
1053
|
),
|
|
785
1054
|
tool_choice: openaiToolOptions?.toolChoice,
|
|
786
1055
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
787
|
-
...
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
1056
|
+
...parameters,
|
|
1057
|
+
...options?.reasoningSummary ? {
|
|
1058
|
+
reasoning: {
|
|
1059
|
+
...parameters.reasoning,
|
|
1060
|
+
summary: options.reasoningSummary
|
|
1061
|
+
}
|
|
1062
|
+
} : {},
|
|
1063
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1064
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1065
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1066
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1067
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1068
|
+
...options?.store === false || options?.include ? {
|
|
1069
|
+
include: [
|
|
1070
|
+
.../* @__PURE__ */ new Set([
|
|
1071
|
+
...options?.include ?? [],
|
|
1072
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1073
|
+
])
|
|
1074
|
+
]
|
|
1075
|
+
} : {},
|
|
794
1076
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
795
1077
|
stream
|
|
796
1078
|
};
|
|
@@ -849,6 +1131,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
849
1131
|
let usage;
|
|
850
1132
|
const citations = [];
|
|
851
1133
|
let completed = false;
|
|
1134
|
+
let terminalResponse;
|
|
1135
|
+
let responseIdentity = {};
|
|
1136
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1137
|
+
let streamedText = "";
|
|
852
1138
|
function* flushToolCalls() {
|
|
853
1139
|
for (const call of toolCalls.values()) {
|
|
854
1140
|
if (!call.name)
|
|
@@ -865,17 +1151,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
865
1151
|
for await (const event of stream) {
|
|
866
1152
|
throwIfAborted(request.signal);
|
|
867
1153
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1154
|
+
if (completed)
|
|
1155
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
868
1156
|
const index = event?.output_index ?? 0;
|
|
869
1157
|
switch (event?.type) {
|
|
1158
|
+
case "response.created":
|
|
1159
|
+
responseIdentity = {
|
|
1160
|
+
id: event.response?.id,
|
|
1161
|
+
model: event.response?.model
|
|
1162
|
+
};
|
|
1163
|
+
break;
|
|
1164
|
+
case "response.output_item.done":
|
|
1165
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1166
|
+
if (event.item?.type === "function_call") {
|
|
1167
|
+
toolCalls.set(index, {
|
|
1168
|
+
id: event.item.call_id,
|
|
1169
|
+
name: event.item.name,
|
|
1170
|
+
arguments: event.item.arguments
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
break;
|
|
870
1174
|
case "response.output_text.delta":
|
|
871
1175
|
if (event.delta) {
|
|
1176
|
+
streamedText += event.delta;
|
|
872
1177
|
yield { type: "message:delta", content: event.delta };
|
|
873
1178
|
}
|
|
874
1179
|
break;
|
|
875
1180
|
case "response.output_item.added":
|
|
876
1181
|
if (event.item?.type === "function_call") {
|
|
877
1182
|
toolCalls.set(index, {
|
|
878
|
-
id: event.item.call_id ??
|
|
1183
|
+
id: event.item.call_id ?? "",
|
|
879
1184
|
name: event.item.name ?? "",
|
|
880
1185
|
arguments: event.item.arguments ?? ""
|
|
881
1186
|
});
|
|
@@ -894,7 +1199,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
894
1199
|
break;
|
|
895
1200
|
}
|
|
896
1201
|
case "response.completed": {
|
|
1202
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1203
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
897
1204
|
completed = true;
|
|
1205
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
898
1206
|
const found = this.collectResponsesCitations(
|
|
899
1207
|
event.response?.output,
|
|
900
1208
|
citations.length
|
|
@@ -931,12 +1239,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
931
1239
|
throwIfAborted(request.signal);
|
|
932
1240
|
if (!completed)
|
|
933
1241
|
throw new Error("Responses stream ended without a completed response");
|
|
1242
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1243
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1244
|
+
toolCalls.clear();
|
|
1245
|
+
for (const [index, item] of output.entries()) {
|
|
1246
|
+
if (item.type === "function_call") {
|
|
1247
|
+
toolCalls.set(index, {
|
|
1248
|
+
id: item.call_id,
|
|
1249
|
+
name: item.name,
|
|
1250
|
+
arguments: item.arguments
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1255
|
+
for (const call of toolCalls.values()) {
|
|
1256
|
+
if (!call.id || !call.name)
|
|
1257
|
+
throw invalidProviderResponse(
|
|
1258
|
+
"Responses stream contains an incomplete tool call"
|
|
1259
|
+
);
|
|
1260
|
+
uniqueCalls.set(call.id, call);
|
|
1261
|
+
}
|
|
1262
|
+
toolCalls.clear();
|
|
1263
|
+
[...uniqueCalls.values()].forEach(
|
|
1264
|
+
(call, index) => toolCalls.set(index, call)
|
|
1265
|
+
);
|
|
1266
|
+
if (!output.length && toolCalls.size) {
|
|
1267
|
+
for (const call of toolCalls.values())
|
|
1268
|
+
output.push({
|
|
1269
|
+
type: "function_call",
|
|
1270
|
+
call_id: call.id,
|
|
1271
|
+
name: call.name,
|
|
1272
|
+
arguments: call.arguments
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
if (!output.length && streamedText)
|
|
1276
|
+
output.push({
|
|
1277
|
+
type: "message",
|
|
1278
|
+
role: "assistant",
|
|
1279
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1280
|
+
});
|
|
1281
|
+
terminalResponse.output = output;
|
|
1282
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1283
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1284
|
+
yield {
|
|
1285
|
+
type: "message:delta",
|
|
1286
|
+
content: finalText.slice(streamedText.length)
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
934
1289
|
yield* flushToolCalls();
|
|
935
1290
|
if (citations.length > 0) {
|
|
936
1291
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
937
1292
|
}
|
|
938
|
-
yield {
|
|
939
|
-
|
|
1293
|
+
yield {
|
|
1294
|
+
type: "message:end",
|
|
1295
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1296
|
+
};
|
|
1297
|
+
yield {
|
|
1298
|
+
type: "done",
|
|
1299
|
+
usage,
|
|
1300
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1301
|
+
};
|
|
940
1302
|
}
|
|
941
1303
|
async *stream(request) {
|
|
942
1304
|
throwIfAborted(request.signal);
|