@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.mjs
CHANGED
|
@@ -1,3 +1,106 @@
|
|
|
1
|
+
// src/core/tool-output.ts
|
|
2
|
+
var imageTypes = /* @__PURE__ */ new Set([
|
|
3
|
+
"image/png",
|
|
4
|
+
"image/jpeg",
|
|
5
|
+
"image/webp",
|
|
6
|
+
"image/gif"
|
|
7
|
+
]);
|
|
8
|
+
function normalizeToolOutput(value) {
|
|
9
|
+
const output = value;
|
|
10
|
+
if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
|
|
11
|
+
throw new Error("Tool model output must contain a nonempty content array");
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
type: "content",
|
|
15
|
+
content: output.content.map((part) => {
|
|
16
|
+
if (part?.type === "text" && typeof part.text === "string")
|
|
17
|
+
return { type: "text", text: part.text };
|
|
18
|
+
if (part?.type !== "image")
|
|
19
|
+
throw new Error("Unsupported tool output content type");
|
|
20
|
+
const sources = ["data", "url", "fileId"].filter((key) => key in part);
|
|
21
|
+
if (sources.length !== 1)
|
|
22
|
+
throw new Error(
|
|
23
|
+
"Tool image requires exactly one of data, url, or fileId"
|
|
24
|
+
);
|
|
25
|
+
if ("fileId" in part) {
|
|
26
|
+
if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Tool image file references must name the openai provider and fileId"
|
|
29
|
+
);
|
|
30
|
+
return { type: "image", fileId: part.fileId, provider: "openai" };
|
|
31
|
+
}
|
|
32
|
+
if ("url" in part) {
|
|
33
|
+
let url;
|
|
34
|
+
try {
|
|
35
|
+
url = new URL(part.url);
|
|
36
|
+
} catch {
|
|
37
|
+
throw new Error("Invalid tool image URL");
|
|
38
|
+
}
|
|
39
|
+
if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
|
|
40
|
+
throw new Error(
|
|
41
|
+
"Tool image URL must use HTTP(S) without credentials"
|
|
42
|
+
);
|
|
43
|
+
return { type: "image", url: url.href };
|
|
44
|
+
}
|
|
45
|
+
if (!("data" in part) || !imageTypes.has(part.mediaType))
|
|
46
|
+
throw new Error("Unsupported tool image media type");
|
|
47
|
+
let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
|
|
48
|
+
if (typeof data !== "string")
|
|
49
|
+
throw new Error("Tool image data must be base64 or bytes");
|
|
50
|
+
if (data.startsWith("data:")) {
|
|
51
|
+
const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
|
|
52
|
+
if (!match || match[1] !== part.mediaType)
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Tool image data URL media type does not match mediaType"
|
|
55
|
+
);
|
|
56
|
+
data = match[2];
|
|
57
|
+
}
|
|
58
|
+
if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
|
|
59
|
+
throw new Error("Invalid tool image base64 data");
|
|
60
|
+
if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
|
|
61
|
+
throw new Error("Tool image exceeds 20 MiB");
|
|
62
|
+
if (Buffer.from(data, "base64").toString("base64") !== data)
|
|
63
|
+
throw new Error("Invalid tool image base64 data");
|
|
64
|
+
return { type: "image", data, mediaType: part.mediaType };
|
|
65
|
+
})
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function toolOutputForProvider(value, provider) {
|
|
69
|
+
const output = normalizeToolOutput(value);
|
|
70
|
+
return output.content.map((part) => {
|
|
71
|
+
if (part.type === "text")
|
|
72
|
+
return {
|
|
73
|
+
type: provider === "responses" ? "input_text" : "text",
|
|
74
|
+
text: part.text
|
|
75
|
+
};
|
|
76
|
+
if (provider === "unsupported")
|
|
77
|
+
throw new Error(
|
|
78
|
+
"Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
|
|
79
|
+
);
|
|
80
|
+
if ("fileId" in part) {
|
|
81
|
+
if (provider !== "responses")
|
|
82
|
+
throw new Error(
|
|
83
|
+
"OpenAI image file references cannot be sent to another provider"
|
|
84
|
+
);
|
|
85
|
+
return { type: "input_image", file_id: part.fileId, detail: "auto" };
|
|
86
|
+
}
|
|
87
|
+
if (provider === "responses")
|
|
88
|
+
return {
|
|
89
|
+
type: "input_image",
|
|
90
|
+
image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
|
|
91
|
+
detail: "auto"
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
type: "image",
|
|
95
|
+
source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function textToolOutput(value, fallback) {
|
|
100
|
+
if (value === void 0) return fallback;
|
|
101
|
+
return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
|
|
102
|
+
}
|
|
103
|
+
|
|
1
104
|
// src/core/openai-parameters.ts
|
|
2
105
|
function capabilities(model) {
|
|
3
106
|
const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
|
|
@@ -420,7 +523,20 @@ function messageToOpenAIContent(message) {
|
|
|
420
523
|
}
|
|
421
524
|
return blocks;
|
|
422
525
|
}
|
|
526
|
+
function extractAnthropicSystemPrompt(messages, systemPrompt) {
|
|
527
|
+
const explicit = typeof systemPrompt === "string" ? systemPrompt : "";
|
|
528
|
+
const parts = explicit ? [explicit] : [];
|
|
529
|
+
for (const message of messages) {
|
|
530
|
+
if (message.role !== "system") continue;
|
|
531
|
+
const text = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.filter(
|
|
532
|
+
(block) => block?.type === "text" && typeof block.text === "string"
|
|
533
|
+
).map((block) => block.text).filter(Boolean).join("\n") : "";
|
|
534
|
+
if (text && text !== explicit) parts.push(text);
|
|
535
|
+
}
|
|
536
|
+
return parts.join("\n");
|
|
537
|
+
}
|
|
423
538
|
function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
539
|
+
const system = extractAnthropicSystemPrompt(messages, systemPrompt);
|
|
424
540
|
const formatted = [];
|
|
425
541
|
for (let i = 0; i < messages.length; i++) {
|
|
426
542
|
const msg = messages[i];
|
|
@@ -449,7 +565,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
449
565
|
{
|
|
450
566
|
type: "tool_result",
|
|
451
567
|
tool_use_id: msg.tool_call_id,
|
|
452
|
-
content: msg.content ?? ""
|
|
568
|
+
content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
|
|
453
569
|
}
|
|
454
570
|
];
|
|
455
571
|
while (i + 1 < messages.length && messages[i + 1].role === "tool") {
|
|
@@ -459,7 +575,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
459
575
|
toolResults.push({
|
|
460
576
|
type: "tool_result",
|
|
461
577
|
tool_use_id: nextTool.tool_call_id,
|
|
462
|
-
content: nextTool.content ?? ""
|
|
578
|
+
content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
|
|
463
579
|
});
|
|
464
580
|
}
|
|
465
581
|
}
|
|
@@ -475,11 +591,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
475
591
|
}
|
|
476
592
|
}
|
|
477
593
|
return {
|
|
478
|
-
system
|
|
594
|
+
system,
|
|
479
595
|
messages: formatted
|
|
480
596
|
};
|
|
481
597
|
}
|
|
482
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
598
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
483
599
|
const formatted = [];
|
|
484
600
|
if (systemPrompt) {
|
|
485
601
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -497,7 +613,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
497
613
|
const assistantMsg = {
|
|
498
614
|
role: "assistant",
|
|
499
615
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
500
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
616
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
617
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
501
618
|
};
|
|
502
619
|
if (hasToolCalls) {
|
|
503
620
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -506,7 +623,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
506
623
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
507
624
|
formatted.push({
|
|
508
625
|
role: "tool",
|
|
509
|
-
content: msg.content ?? "",
|
|
626
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
627
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
510
628
|
tool_call_id: msg.tool_call_id
|
|
511
629
|
});
|
|
512
630
|
}
|
|
@@ -514,7 +632,120 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
514
632
|
return formatted;
|
|
515
633
|
}
|
|
516
634
|
|
|
635
|
+
// src/core/openai-responses.ts
|
|
636
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
637
|
+
return {
|
|
638
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
639
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
643
|
+
const api = transport.api ?? "auto";
|
|
644
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
645
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
646
|
+
}
|
|
647
|
+
if (api === "chat") {
|
|
648
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
649
|
+
throw new Error(
|
|
650
|
+
"Responses options and native tool search require the Responses API"
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
let officialEndpoint = !baseURL;
|
|
656
|
+
try {
|
|
657
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
658
|
+
} catch {
|
|
659
|
+
}
|
|
660
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
661
|
+
}
|
|
662
|
+
function responseMetadata(response) {
|
|
663
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
664
|
+
return {
|
|
665
|
+
openai: {
|
|
666
|
+
response: {
|
|
667
|
+
id: response?.id,
|
|
668
|
+
model: response?.model,
|
|
669
|
+
status: response?.status,
|
|
670
|
+
output,
|
|
671
|
+
usage: response?.usage,
|
|
672
|
+
store: response?.store,
|
|
673
|
+
refusals: output.flatMap(
|
|
674
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
675
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
676
|
+
).map((part) => part.refusal) : []
|
|
677
|
+
)
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
function responseReplayMetadata(metadata) {
|
|
683
|
+
const response = metadata?.openai?.response;
|
|
684
|
+
if (!response) return metadata;
|
|
685
|
+
const output = response.output.map((item) => {
|
|
686
|
+
const fields = ["type", "id", "status"];
|
|
687
|
+
switch (item.type) {
|
|
688
|
+
case "reasoning":
|
|
689
|
+
fields.push("encrypted_content");
|
|
690
|
+
break;
|
|
691
|
+
case "message":
|
|
692
|
+
fields.push("role", "phase");
|
|
693
|
+
break;
|
|
694
|
+
case "function_call":
|
|
695
|
+
fields.push("call_id", "name", "namespace", "arguments");
|
|
696
|
+
break;
|
|
697
|
+
case "tool_search_call":
|
|
698
|
+
fields.push("call_id", "execution", "arguments");
|
|
699
|
+
break;
|
|
700
|
+
case "tool_search_output":
|
|
701
|
+
fields.push("call_id", "tools");
|
|
702
|
+
break;
|
|
703
|
+
case "web_search_call":
|
|
704
|
+
fields.push("action");
|
|
705
|
+
break;
|
|
706
|
+
default:
|
|
707
|
+
return stripItemDiagnostics(item);
|
|
708
|
+
}
|
|
709
|
+
const replay = Object.fromEntries(
|
|
710
|
+
fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
|
|
711
|
+
);
|
|
712
|
+
if (item.type === "reasoning") replay.summary = [];
|
|
713
|
+
if (item.type === "message")
|
|
714
|
+
replay.content = Array.isArray(item.content) ? item.content.flatMap(
|
|
715
|
+
(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 }] : []
|
|
716
|
+
) : [];
|
|
717
|
+
return stripItemDiagnostics(replay);
|
|
718
|
+
});
|
|
719
|
+
return { openai: { response: { output, refusals: [] } } };
|
|
720
|
+
}
|
|
721
|
+
function stripItemDiagnostics(item) {
|
|
722
|
+
const {
|
|
723
|
+
usage: _usage,
|
|
724
|
+
logprobs: _logprobs,
|
|
725
|
+
metadata: _metadata,
|
|
726
|
+
...state
|
|
727
|
+
} = item;
|
|
728
|
+
return state;
|
|
729
|
+
}
|
|
730
|
+
function getResponseReplay(metadata) {
|
|
731
|
+
const response = metadata?.openai?.response;
|
|
732
|
+
if (response === void 0) return void 0;
|
|
733
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
734
|
+
throw new Error(
|
|
735
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
return responseReplayMetadata(metadata)?.openai?.response?.output;
|
|
739
|
+
}
|
|
740
|
+
|
|
517
741
|
// src/core/provider-error.ts
|
|
742
|
+
function invalidProviderResponse(message) {
|
|
743
|
+
return Object.assign(new Error(message), {
|
|
744
|
+
name: "InvalidProviderResponseError",
|
|
745
|
+
status: 502,
|
|
746
|
+
code: "INVALID_PROVIDER_RESPONSE"
|
|
747
|
+
});
|
|
748
|
+
}
|
|
518
749
|
function providerErrorEvent(error, code) {
|
|
519
750
|
const details = error ?? {};
|
|
520
751
|
const event = {
|
|
@@ -590,13 +821,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
590
821
|
}));
|
|
591
822
|
}
|
|
592
823
|
shouldUseResponsesApi(request) {
|
|
593
|
-
|
|
594
|
-
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
|
|
595
|
-
return true;
|
|
596
|
-
return requiresOpenAIResponses(
|
|
824
|
+
return useOpenAIResponses(
|
|
597
825
|
request.config?.model || this.model,
|
|
598
826
|
this.config.baseUrl,
|
|
599
|
-
this.parameterOptions(request)
|
|
827
|
+
this.parameterOptions(request),
|
|
828
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
829
|
+
this.toolDefinitions(request).length > 0,
|
|
830
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
831
|
+
(message) => message.providerMetadata?.openai?.response
|
|
832
|
+
),
|
|
833
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
600
834
|
);
|
|
601
835
|
}
|
|
602
836
|
/**
|
|
@@ -629,11 +863,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
629
863
|
});
|
|
630
864
|
}
|
|
631
865
|
buildResponsesInput(request) {
|
|
632
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
|
|
866
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
633
867
|
const input = [];
|
|
634
868
|
for (const message of sourceMessages) {
|
|
635
869
|
if (message.role === "system") {
|
|
636
|
-
if (
|
|
870
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
637
871
|
input.push({
|
|
638
872
|
type: "message",
|
|
639
873
|
role: "developer",
|
|
@@ -643,6 +877,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
643
877
|
continue;
|
|
644
878
|
}
|
|
645
879
|
if (message.role === "assistant") {
|
|
880
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
881
|
+
if (replay) {
|
|
882
|
+
input.push(...replay);
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
646
885
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
647
886
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
648
887
|
input.push({
|
|
@@ -666,7 +905,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
666
905
|
input.push({
|
|
667
906
|
type: "function_call_output",
|
|
668
907
|
call_id: message.tool_call_id,
|
|
669
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
908
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
670
909
|
});
|
|
671
910
|
continue;
|
|
672
911
|
}
|
|
@@ -680,10 +919,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
680
919
|
return input;
|
|
681
920
|
}
|
|
682
921
|
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
683
|
-
formatRawMessages(messages) {
|
|
922
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
684
923
|
return messages.map((message) => {
|
|
685
|
-
const { attachments, ...rest } = message;
|
|
686
|
-
let content = message.content;
|
|
924
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
925
|
+
let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
|
|
926
|
+
if (preserveProviderMetadata && modelOutput)
|
|
927
|
+
rest.modelOutput = modelOutput;
|
|
687
928
|
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
688
929
|
content = null;
|
|
689
930
|
}
|
|
@@ -696,7 +937,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
696
937
|
content = [...parts, ...images];
|
|
697
938
|
}
|
|
698
939
|
}
|
|
699
|
-
return {
|
|
940
|
+
return {
|
|
941
|
+
...rest,
|
|
942
|
+
content,
|
|
943
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
944
|
+
};
|
|
700
945
|
});
|
|
701
946
|
}
|
|
702
947
|
/**
|
|
@@ -777,14 +1022,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
777
1022
|
).map((part) => part.text) : []
|
|
778
1023
|
).join("") : "";
|
|
779
1024
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
name
|
|
783
|
-
|
|
784
|
-
|
|
1025
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
1026
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
1027
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
1028
|
+
throw invalidProviderResponse(
|
|
1029
|
+
"Responses contains an incomplete function call"
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
if (seenCallIds.has(item.call_id))
|
|
1033
|
+
throw invalidProviderResponse(
|
|
1034
|
+
"Responses contains duplicate function call IDs"
|
|
1035
|
+
);
|
|
1036
|
+
seenCallIds.add(item.call_id);
|
|
1037
|
+
let args;
|
|
1038
|
+
try {
|
|
1039
|
+
args = JSON.parse(item.arguments);
|
|
1040
|
+
} catch {
|
|
1041
|
+
throw invalidProviderResponse(
|
|
1042
|
+
"Responses function arguments must be valid JSON"
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
1046
|
+
throw invalidProviderResponse(
|
|
1047
|
+
"Responses function arguments must be a JSON object"
|
|
1048
|
+
);
|
|
1049
|
+
return {
|
|
1050
|
+
id: item.call_id,
|
|
1051
|
+
name: item.name,
|
|
1052
|
+
args
|
|
1053
|
+
};
|
|
1054
|
+
}) : [];
|
|
785
1055
|
return {
|
|
786
1056
|
content,
|
|
787
1057
|
toolCalls,
|
|
1058
|
+
providerMetadata: responseMetadata(response),
|
|
788
1059
|
usage: response?.usage ? {
|
|
789
1060
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
790
1061
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -795,6 +1066,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
795
1066
|
}
|
|
796
1067
|
buildResponsesPayload(request, stream) {
|
|
797
1068
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1069
|
+
const options = resolveOpenAITransport(
|
|
1070
|
+
this.config,
|
|
1071
|
+
request.config?.openai
|
|
1072
|
+
).responses;
|
|
1073
|
+
const parameters = buildOpenAIParameters(
|
|
1074
|
+
request.config?.model || this.model,
|
|
1075
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1076
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1077
|
+
this.parameterOptions(request),
|
|
1078
|
+
"responses"
|
|
1079
|
+
);
|
|
798
1080
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
799
1081
|
request.config?.responseFormat
|
|
800
1082
|
);
|
|
@@ -810,13 +1092,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
810
1092
|
),
|
|
811
1093
|
tool_choice: openaiToolOptions?.toolChoice,
|
|
812
1094
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
813
|
-
...
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1095
|
+
...parameters,
|
|
1096
|
+
...options?.reasoningSummary ? {
|
|
1097
|
+
reasoning: {
|
|
1098
|
+
...parameters.reasoning,
|
|
1099
|
+
summary: options.reasoningSummary
|
|
1100
|
+
}
|
|
1101
|
+
} : {},
|
|
1102
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1103
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1104
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1105
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1106
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1107
|
+
...options?.store === false || options?.include ? {
|
|
1108
|
+
include: [
|
|
1109
|
+
.../* @__PURE__ */ new Set([
|
|
1110
|
+
...options?.include ?? [],
|
|
1111
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1112
|
+
])
|
|
1113
|
+
]
|
|
1114
|
+
} : {},
|
|
820
1115
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
821
1116
|
stream
|
|
822
1117
|
};
|
|
@@ -875,6 +1170,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
875
1170
|
let usage;
|
|
876
1171
|
const citations = [];
|
|
877
1172
|
let completed = false;
|
|
1173
|
+
let terminalResponse;
|
|
1174
|
+
let responseIdentity = {};
|
|
1175
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1176
|
+
let streamedText = "";
|
|
878
1177
|
function* flushToolCalls() {
|
|
879
1178
|
for (const call of toolCalls.values()) {
|
|
880
1179
|
if (!call.name)
|
|
@@ -891,17 +1190,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
891
1190
|
for await (const event of stream) {
|
|
892
1191
|
throwIfAborted(request.signal);
|
|
893
1192
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1193
|
+
if (completed)
|
|
1194
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
894
1195
|
const index = event?.output_index ?? 0;
|
|
895
1196
|
switch (event?.type) {
|
|
1197
|
+
case "response.created":
|
|
1198
|
+
responseIdentity = {
|
|
1199
|
+
id: event.response?.id,
|
|
1200
|
+
model: event.response?.model
|
|
1201
|
+
};
|
|
1202
|
+
break;
|
|
1203
|
+
case "response.output_item.done":
|
|
1204
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1205
|
+
if (event.item?.type === "function_call") {
|
|
1206
|
+
toolCalls.set(index, {
|
|
1207
|
+
id: event.item.call_id,
|
|
1208
|
+
name: event.item.name,
|
|
1209
|
+
arguments: event.item.arguments
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
break;
|
|
896
1213
|
case "response.output_text.delta":
|
|
897
1214
|
if (event.delta) {
|
|
1215
|
+
streamedText += event.delta;
|
|
898
1216
|
yield { type: "message:delta", content: event.delta };
|
|
899
1217
|
}
|
|
900
1218
|
break;
|
|
901
1219
|
case "response.output_item.added":
|
|
902
1220
|
if (event.item?.type === "function_call") {
|
|
903
1221
|
toolCalls.set(index, {
|
|
904
|
-
id: event.item.call_id ??
|
|
1222
|
+
id: event.item.call_id ?? "",
|
|
905
1223
|
name: event.item.name ?? "",
|
|
906
1224
|
arguments: event.item.arguments ?? ""
|
|
907
1225
|
});
|
|
@@ -920,7 +1238,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
920
1238
|
break;
|
|
921
1239
|
}
|
|
922
1240
|
case "response.completed": {
|
|
1241
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1242
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
923
1243
|
completed = true;
|
|
1244
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
924
1245
|
const found = this.collectResponsesCitations(
|
|
925
1246
|
event.response?.output,
|
|
926
1247
|
citations.length
|
|
@@ -957,12 +1278,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
957
1278
|
throwIfAborted(request.signal);
|
|
958
1279
|
if (!completed)
|
|
959
1280
|
throw new Error("Responses stream ended without a completed response");
|
|
1281
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1282
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1283
|
+
toolCalls.clear();
|
|
1284
|
+
for (const [index, item] of output.entries()) {
|
|
1285
|
+
if (item.type === "function_call") {
|
|
1286
|
+
toolCalls.set(index, {
|
|
1287
|
+
id: item.call_id,
|
|
1288
|
+
name: item.name,
|
|
1289
|
+
arguments: item.arguments
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1294
|
+
for (const call of toolCalls.values()) {
|
|
1295
|
+
if (!call.id || !call.name)
|
|
1296
|
+
throw invalidProviderResponse(
|
|
1297
|
+
"Responses stream contains an incomplete tool call"
|
|
1298
|
+
);
|
|
1299
|
+
uniqueCalls.set(call.id, call);
|
|
1300
|
+
}
|
|
1301
|
+
toolCalls.clear();
|
|
1302
|
+
[...uniqueCalls.values()].forEach(
|
|
1303
|
+
(call, index) => toolCalls.set(index, call)
|
|
1304
|
+
);
|
|
1305
|
+
if (!output.length && toolCalls.size) {
|
|
1306
|
+
for (const call of toolCalls.values())
|
|
1307
|
+
output.push({
|
|
1308
|
+
type: "function_call",
|
|
1309
|
+
call_id: call.id,
|
|
1310
|
+
name: call.name,
|
|
1311
|
+
arguments: call.arguments
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
if (!output.length && streamedText)
|
|
1315
|
+
output.push({
|
|
1316
|
+
type: "message",
|
|
1317
|
+
role: "assistant",
|
|
1318
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1319
|
+
});
|
|
1320
|
+
terminalResponse.output = output;
|
|
1321
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1322
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1323
|
+
yield {
|
|
1324
|
+
type: "message:delta",
|
|
1325
|
+
content: finalText.slice(streamedText.length)
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
960
1328
|
yield* flushToolCalls();
|
|
961
1329
|
if (citations.length > 0) {
|
|
962
1330
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
963
1331
|
}
|
|
964
|
-
yield {
|
|
965
|
-
|
|
1332
|
+
yield {
|
|
1333
|
+
type: "message:end",
|
|
1334
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1335
|
+
};
|
|
1336
|
+
yield {
|
|
1337
|
+
type: "done",
|
|
1338
|
+
usage,
|
|
1339
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1340
|
+
};
|
|
966
1341
|
}
|
|
967
1342
|
async *stream(request) {
|
|
968
1343
|
throwIfAborted(request.signal);
|
|
@@ -1348,7 +1723,7 @@ var AnthropicAdapter = class {
|
|
|
1348
1723
|
}
|
|
1349
1724
|
pendingToolResults.push({
|
|
1350
1725
|
tool_use_id: toolCallId,
|
|
1351
|
-
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1726
|
+
content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1352
1727
|
});
|
|
1353
1728
|
} else if (msg.role === "user") {
|
|
1354
1729
|
if (pendingToolResults.length > 0) {
|
|
@@ -1469,14 +1844,23 @@ var AnthropicAdapter = class {
|
|
|
1469
1844
|
buildRequestOptions(request) {
|
|
1470
1845
|
const responseFormat = request.config?.responseFormat;
|
|
1471
1846
|
const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
|
|
1472
|
-
|
|
1847
|
+
let systemMessage;
|
|
1473
1848
|
let messages;
|
|
1474
1849
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1850
|
+
systemMessage = extractAnthropicSystemPrompt(
|
|
1851
|
+
request.rawMessages,
|
|
1852
|
+
request.systemPrompt
|
|
1853
|
+
);
|
|
1475
1854
|
messages = this.convertToAnthropicMessages(request.rawMessages);
|
|
1476
1855
|
} else {
|
|
1477
|
-
const formatted = formatMessagesForAnthropic(
|
|
1856
|
+
const formatted = formatMessagesForAnthropic(
|
|
1857
|
+
request.messages,
|
|
1858
|
+
request.systemPrompt
|
|
1859
|
+
);
|
|
1860
|
+
systemMessage = formatted.system;
|
|
1478
1861
|
messages = formatted.messages;
|
|
1479
1862
|
}
|
|
1863
|
+
systemMessage += jsonObjectSuffix;
|
|
1480
1864
|
const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
|
|
1481
1865
|
const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
|
|
1482
1866
|
request.toolDefinitions ?? [],
|
|
@@ -1819,7 +2203,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
|
|
|
1819
2203
|
for (const msg of messages) {
|
|
1820
2204
|
const baseMessage = {
|
|
1821
2205
|
role: msg.role,
|
|
1822
|
-
content: msg.content ?? ""
|
|
2206
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
|
|
1823
2207
|
};
|
|
1824
2208
|
if (msg.role === "user" && msg.metadata?.attachments) {
|
|
1825
2209
|
const images = await extractImagesFromAttachments(
|
|
@@ -1856,7 +2240,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
|
|
|
1856
2240
|
for (const msg of rawMessages) {
|
|
1857
2241
|
const baseMessage = {
|
|
1858
2242
|
role: msg.role,
|
|
1859
|
-
content: msg.content ?? ""
|
|
2243
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
|
|
1860
2244
|
};
|
|
1861
2245
|
if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
|
|
1862
2246
|
const attachments = msg.attachments;
|
|
@@ -2066,6 +2450,10 @@ function messageToGeminiContent(msg) {
|
|
|
2066
2450
|
if (msg.role === "system") return null;
|
|
2067
2451
|
const parts = [];
|
|
2068
2452
|
if (msg.role === "tool" && msg.tool_call_id) {
|
|
2453
|
+
msg = {
|
|
2454
|
+
...msg,
|
|
2455
|
+
content: textToolOutput(msg.modelOutput, msg.content ?? "")
|
|
2456
|
+
};
|
|
2069
2457
|
let responseData;
|
|
2070
2458
|
try {
|
|
2071
2459
|
responseData = JSON.parse(msg.content || "{}");
|
|
@@ -2526,6 +2914,13 @@ var AzureAdapter = class {
|
|
|
2526
2914
|
let messages;
|
|
2527
2915
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2528
2916
|
const processedMessages = request.rawMessages.map((msg) => {
|
|
2917
|
+
const { modelOutput, ...rest } = msg;
|
|
2918
|
+
msg = {
|
|
2919
|
+
...rest,
|
|
2920
|
+
...msg.role === "tool" ? {
|
|
2921
|
+
content: textToolOutput(modelOutput, String(msg.content ?? ""))
|
|
2922
|
+
} : {}
|
|
2923
|
+
};
|
|
2529
2924
|
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
2530
2925
|
if (hasAttachments) {
|
|
2531
2926
|
const content = [];
|
|
@@ -2656,7 +3051,10 @@ var AzureAdapter = class {
|
|
|
2656
3051
|
const client = await this.getClient();
|
|
2657
3052
|
let messages;
|
|
2658
3053
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2659
|
-
messages = request.rawMessages
|
|
3054
|
+
messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
|
|
3055
|
+
...msg,
|
|
3056
|
+
...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
|
|
3057
|
+
}));
|
|
2660
3058
|
if (request.systemPrompt) {
|
|
2661
3059
|
const hasSystem = messages.some((m) => m.role === "system");
|
|
2662
3060
|
if (!hasSystem) {
|