@yourgpt/llm-sdk 2.6.0-beta.0 → 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 +189 -0
- package/dist/adapters/index.d.mts +11 -13
- package/dist/adapters/index.d.ts +11 -13
- package/dist/adapters/index.js +610 -186
- package/dist/adapters/index.mjs +610 -186
- package/dist/audio-BBSwnjo8.d.mts +92 -0
- package/dist/audio-BBSwnjo8.d.ts +92 -0
- package/dist/{base-tNgbBaSo.d.mts → base-BICaQaRS.d.mts} +8 -3
- package/dist/{base-C58Dsr9p.d.ts → base-CpRPB8u8.d.ts} +8 -3
- package/dist/fallback/index.d.mts +4 -4
- package/dist/fallback/index.d.ts +4 -4
- package/dist/fallback/index.js +75 -39
- package/dist/fallback/index.mjs +75 -39
- package/dist/index.d.mts +18 -35
- package/dist/index.d.ts +18 -35
- package/dist/index.js +304 -88
- package/dist/index.mjs +302 -89
- 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 +559 -178
- package/dist/providers/fireworks/index.mjs +559 -178
- package/dist/providers/google/index.d.mts +3 -3
- package/dist/providers/google/index.d.ts +3 -3
- package/dist/providers/google/index.js +559 -178
- package/dist/providers/google/index.mjs +559 -178
- 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 -23
- package/dist/providers/openai/index.d.ts +25 -23
- package/dist/providers/openai/index.js +1397 -481
- package/dist/providers/openai/index.mjs +1396 -482
- package/dist/providers/openrouter/index.d.mts +3 -3
- package/dist/providers/openrouter/index.d.ts +3 -3
- package/dist/providers/openrouter/index.js +559 -178
- package/dist/providers/openrouter/index.mjs +559 -178
- package/dist/providers/togetherai/index.d.mts +3 -3
- package/dist/providers/togetherai/index.d.ts +3 -3
- package/dist/providers/togetherai/index.js +559 -178
- package/dist/providers/togetherai/index.mjs +559 -178
- package/dist/providers/xai/index.d.mts +3 -3
- package/dist/providers/xai/index.d.ts +3 -3
- package/dist/providers/xai/index.js +559 -178
- package/dist/providers/xai/index.mjs +559 -178
- package/dist/{types-B6dhnguR.d.mts → types-C3RgRbEC.d.mts} +11 -4
- package/dist/{types-CCxPmkmK.d.ts → types-DCFqkJAy.d.ts} +11 -4
- package/dist/{types-BQ31QIsA.d.ts → types-DHQEMFHA.d.mts} +3 -11
- package/dist/{types-BkQCSiIt.d.mts → types-Dg1Bcg1k.d.mts} +127 -3
- package/dist/{types-BkQCSiIt.d.ts → types-Dg1Bcg1k.d.ts} +127 -3
- package/dist/{types-BSSiJW2o.d.mts → types-Dikj3En4.d.ts} +3 -11
- 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 +117 -49
- package/LICENSE +0 -21
package/dist/adapters/index.js
CHANGED
|
@@ -1,5 +1,159 @@
|
|
|
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
|
+
|
|
106
|
+
// src/core/openai-parameters.ts
|
|
107
|
+
function capabilities(model) {
|
|
108
|
+
const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
|
|
109
|
+
const major = Number(gpt?.[1] ?? 0);
|
|
110
|
+
const minor = Number(gpt?.[2] ?? 0);
|
|
111
|
+
const chat = gpt?.[3]?.startsWith("chat") ?? false;
|
|
112
|
+
const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
|
|
113
|
+
const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
|
|
114
|
+
return { major, minor, reasoning, supportsNone };
|
|
115
|
+
}
|
|
116
|
+
function requiresOpenAIResponses(model, baseUrl, options = {}) {
|
|
117
|
+
if (options.parameterPolicy === "passthrough") return false;
|
|
118
|
+
const c = capabilities(model);
|
|
119
|
+
if (!c.reasoning || options.reasoningEffort === "none") return false;
|
|
120
|
+
if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
|
|
121
|
+
if (!baseUrl) return true;
|
|
122
|
+
try {
|
|
123
|
+
return new URL(baseUrl).hostname === "api.openai.com";
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
|
|
129
|
+
const c = capabilities(model ?? "");
|
|
130
|
+
const normalize = options.parameterPolicy !== "passthrough";
|
|
131
|
+
const effort = options.reasoningEffort;
|
|
132
|
+
if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
|
|
133
|
+
throw new Error(`${model} does not support reasoningEffort: none`);
|
|
134
|
+
}
|
|
135
|
+
if (normalize && c.major >= 6 && effort === "minimal") {
|
|
136
|
+
throw new Error(`${model} does not support reasoningEffort: minimal`);
|
|
137
|
+
}
|
|
138
|
+
const result = {
|
|
139
|
+
[api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
|
|
140
|
+
};
|
|
141
|
+
if (effort !== void 0) {
|
|
142
|
+
if (api === "responses") result.reasoning = { effort };
|
|
143
|
+
else result.reasoning_effort = effort;
|
|
144
|
+
}
|
|
145
|
+
if (temperature !== void 0) {
|
|
146
|
+
if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
|
|
147
|
+
result.temperature = temperature;
|
|
148
|
+
} else {
|
|
149
|
+
(options.onWarning ?? console.warn)(
|
|
150
|
+
`[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
3
157
|
// src/adapters/base.ts
|
|
4
158
|
function stringifyForDebug(value) {
|
|
5
159
|
return JSON.stringify(
|
|
@@ -119,16 +273,6 @@ function normalizeObjectJsonSchema(schema) {
|
|
|
119
273
|
}
|
|
120
274
|
return normalized;
|
|
121
275
|
}
|
|
122
|
-
function isOpenAIReasoningModel(modelId) {
|
|
123
|
-
if (!modelId) return false;
|
|
124
|
-
return /^(o1|o3|o4|gpt-5)/i.test(modelId);
|
|
125
|
-
}
|
|
126
|
-
function buildOpenAITokenParams(modelId, maxTokens, temperature) {
|
|
127
|
-
if (isOpenAIReasoningModel(modelId)) {
|
|
128
|
-
return { max_completion_tokens: maxTokens };
|
|
129
|
-
}
|
|
130
|
-
return { max_tokens: maxTokens, temperature };
|
|
131
|
-
}
|
|
132
276
|
function stripSchemaKeys(schema, keysToDrop, options = {}) {
|
|
133
277
|
if (Array.isArray(schema)) {
|
|
134
278
|
return schema.map((item) => stripSchemaKeys(item, keysToDrop, options));
|
|
@@ -381,7 +525,27 @@ function messageToOpenAIContent(message) {
|
|
|
381
525
|
}
|
|
382
526
|
return blocks;
|
|
383
527
|
}
|
|
528
|
+
function extractAnthropicSystemPrompt(messages, systemPrompt) {
|
|
529
|
+
const parts = systemPrompt ? [systemPrompt] : [];
|
|
530
|
+
for (const message of messages) {
|
|
531
|
+
if (message.role !== "system") continue;
|
|
532
|
+
if (typeof message.content === "string") {
|
|
533
|
+
if (message.content) parts.push(message.content);
|
|
534
|
+
} else if (Array.isArray(message.content)) {
|
|
535
|
+
for (const block of message.content) {
|
|
536
|
+
if (block?.type !== "text" || typeof block.text !== "string") {
|
|
537
|
+
throw new TypeError("Anthropic system messages must contain text.");
|
|
538
|
+
}
|
|
539
|
+
if (block.text) parts.push(block.text);
|
|
540
|
+
}
|
|
541
|
+
} else {
|
|
542
|
+
throw new TypeError("Anthropic system messages must contain text.");
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return parts.join("\n");
|
|
546
|
+
}
|
|
384
547
|
function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
548
|
+
const system = extractAnthropicSystemPrompt(messages, systemPrompt);
|
|
385
549
|
const formatted = [];
|
|
386
550
|
for (let i = 0; i < messages.length; i++) {
|
|
387
551
|
const msg = messages[i];
|
|
@@ -410,7 +574,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
410
574
|
{
|
|
411
575
|
type: "tool_result",
|
|
412
576
|
tool_use_id: msg.tool_call_id,
|
|
413
|
-
content: msg.content ?? ""
|
|
577
|
+
content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
|
|
414
578
|
}
|
|
415
579
|
];
|
|
416
580
|
while (i + 1 < messages.length && messages[i + 1].role === "tool") {
|
|
@@ -420,7 +584,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
420
584
|
toolResults.push({
|
|
421
585
|
type: "tool_result",
|
|
422
586
|
tool_use_id: nextTool.tool_call_id,
|
|
423
|
-
content: nextTool.content ?? ""
|
|
587
|
+
content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
|
|
424
588
|
});
|
|
425
589
|
}
|
|
426
590
|
}
|
|
@@ -436,11 +600,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
|
|
|
436
600
|
}
|
|
437
601
|
}
|
|
438
602
|
return {
|
|
439
|
-
system
|
|
603
|
+
system,
|
|
440
604
|
messages: formatted
|
|
441
605
|
};
|
|
442
606
|
}
|
|
443
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
607
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
444
608
|
const formatted = [];
|
|
445
609
|
if (systemPrompt) {
|
|
446
610
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -458,7 +622,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
458
622
|
const assistantMsg = {
|
|
459
623
|
role: "assistant",
|
|
460
624
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
461
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
625
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
626
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
462
627
|
};
|
|
463
628
|
if (hasToolCalls) {
|
|
464
629
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -467,7 +632,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
467
632
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
468
633
|
formatted.push({
|
|
469
634
|
role: "tool",
|
|
470
|
-
content: msg.content ?? "",
|
|
635
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
636
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
471
637
|
tool_call_id: msg.tool_call_id
|
|
472
638
|
});
|
|
473
639
|
}
|
|
@@ -475,6 +641,86 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
475
641
|
return formatted;
|
|
476
642
|
}
|
|
477
643
|
|
|
644
|
+
// src/core/openai-responses.ts
|
|
645
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
646
|
+
return {
|
|
647
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
648
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
652
|
+
const api = transport.api ?? "auto";
|
|
653
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
654
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
655
|
+
}
|
|
656
|
+
if (api === "chat") {
|
|
657
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
"Responses options and native tool search require the Responses API"
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
let officialEndpoint = !baseURL;
|
|
665
|
+
try {
|
|
666
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
667
|
+
} catch {
|
|
668
|
+
}
|
|
669
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
670
|
+
}
|
|
671
|
+
function responseMetadata(response) {
|
|
672
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
673
|
+
return {
|
|
674
|
+
openai: {
|
|
675
|
+
response: {
|
|
676
|
+
id: response?.id,
|
|
677
|
+
model: response?.model,
|
|
678
|
+
status: response?.status,
|
|
679
|
+
output,
|
|
680
|
+
usage: response?.usage,
|
|
681
|
+
store: response?.store,
|
|
682
|
+
refusals: output.flatMap(
|
|
683
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
684
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
685
|
+
).map((part) => part.refusal) : []
|
|
686
|
+
)
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
};
|
|
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 response.output;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/core/provider-error.ts
|
|
703
|
+
function providerErrorEvent(error, code) {
|
|
704
|
+
const details = error ?? {};
|
|
705
|
+
const event = {
|
|
706
|
+
type: "error",
|
|
707
|
+
message: error instanceof Error ? error.message : String(error),
|
|
708
|
+
code,
|
|
709
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
710
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
711
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
712
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
713
|
+
};
|
|
714
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
715
|
+
return event;
|
|
716
|
+
}
|
|
717
|
+
function throwIfAborted(signal) {
|
|
718
|
+
if (!signal?.aborted) return;
|
|
719
|
+
const error = new Error("The operation was aborted");
|
|
720
|
+
error.name = "AbortError";
|
|
721
|
+
throw error;
|
|
722
|
+
}
|
|
723
|
+
|
|
478
724
|
// src/core/utils.ts
|
|
479
725
|
function generateId(prefix = "id") {
|
|
480
726
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -506,39 +752,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
506
752
|
const { default: OpenAI } = await import('openai');
|
|
507
753
|
this.client = new OpenAI({
|
|
508
754
|
apiKey: this.config.apiKey,
|
|
509
|
-
baseURL: this.config.baseUrl
|
|
755
|
+
baseURL: this.config.baseUrl,
|
|
756
|
+
organization: this.config.organization,
|
|
757
|
+
defaultHeaders: this.config.headers
|
|
510
758
|
});
|
|
511
759
|
}
|
|
512
760
|
return this.client;
|
|
513
761
|
}
|
|
762
|
+
parameterOptions(request) {
|
|
763
|
+
return {
|
|
764
|
+
...this.config,
|
|
765
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
toolDefinitions(request) {
|
|
769
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
770
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
771
|
+
name: tool.function.name,
|
|
772
|
+
description: tool.function.description ?? "",
|
|
773
|
+
location: "server",
|
|
774
|
+
inputSchema: tool.function.parameters
|
|
775
|
+
}));
|
|
776
|
+
}
|
|
514
777
|
shouldUseResponsesApi(request) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
request.
|
|
778
|
+
return useOpenAIResponses(
|
|
779
|
+
request.config?.model || this.model,
|
|
780
|
+
this.config.baseUrl,
|
|
781
|
+
this.parameterOptions(request),
|
|
782
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
783
|
+
this.toolDefinitions(request).length > 0,
|
|
784
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
785
|
+
(message) => message.providerMetadata?.openai?.response
|
|
786
|
+
),
|
|
787
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
522
788
|
);
|
|
523
789
|
}
|
|
524
|
-
/**
|
|
525
|
-
* Whether this model rejects function tools on /v1/chat/completions.
|
|
526
|
-
*
|
|
527
|
-
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
528
|
-
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
529
|
-
* restriction and may not implement /v1/responses at all.
|
|
530
|
-
*/
|
|
531
|
-
requiresResponsesApiForTools(modelId) {
|
|
532
|
-
if (!modelId) return false;
|
|
533
|
-
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
534
|
-
const baseUrl = this.config.baseUrl;
|
|
535
|
-
if (!baseUrl) return true;
|
|
536
|
-
try {
|
|
537
|
-
return new URL(baseUrl).hostname === "api.openai.com";
|
|
538
|
-
} catch {
|
|
539
|
-
return false;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
790
|
/**
|
|
543
791
|
* Convert one message's content to Responses content parts.
|
|
544
792
|
*
|
|
@@ -569,11 +817,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
569
817
|
});
|
|
570
818
|
}
|
|
571
819
|
buildResponsesInput(request) {
|
|
572
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
820
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
573
821
|
const input = [];
|
|
574
822
|
for (const message of sourceMessages) {
|
|
575
823
|
if (message.role === "system") {
|
|
576
|
-
if (
|
|
824
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
577
825
|
input.push({
|
|
578
826
|
type: "message",
|
|
579
827
|
role: "developer",
|
|
@@ -583,6 +831,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
583
831
|
continue;
|
|
584
832
|
}
|
|
585
833
|
if (message.role === "assistant") {
|
|
834
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
835
|
+
if (replay) {
|
|
836
|
+
input.push(...replay);
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
586
839
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
587
840
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
588
841
|
input.push({
|
|
@@ -606,7 +859,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
606
859
|
input.push({
|
|
607
860
|
type: "function_call_output",
|
|
608
861
|
call_id: message.tool_call_id,
|
|
609
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
862
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
610
863
|
});
|
|
611
864
|
continue;
|
|
612
865
|
}
|
|
@@ -619,6 +872,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
619
872
|
}
|
|
620
873
|
return input;
|
|
621
874
|
}
|
|
875
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
876
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
877
|
+
return messages.map((message) => {
|
|
878
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
879
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
880
|
+
if (preserveProviderMetadata && modelOutput)
|
|
881
|
+
rest.modelOutput = modelOutput;
|
|
882
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
883
|
+
content = null;
|
|
884
|
+
}
|
|
885
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
886
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
887
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
888
|
+
).filter((image) => image !== null);
|
|
889
|
+
if (images.length > 0) {
|
|
890
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
891
|
+
content = [...parts, ...images];
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
...rest,
|
|
896
|
+
content,
|
|
897
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
898
|
+
};
|
|
899
|
+
});
|
|
900
|
+
}
|
|
622
901
|
/**
|
|
623
902
|
* Build the web_search tool for the Responses API, or undefined when web
|
|
624
903
|
* search is off.
|
|
@@ -697,20 +976,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
697
976
|
).map((part) => part.text) : []
|
|
698
977
|
).join("") : "";
|
|
699
978
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
name
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
979
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
980
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
981
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
982
|
+
throw new Error("Responses contains an incomplete function call");
|
|
983
|
+
}
|
|
984
|
+
if (seenCallIds.has(item.call_id))
|
|
985
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
986
|
+
seenCallIds.add(item.call_id);
|
|
987
|
+
const args = JSON.parse(item.arguments);
|
|
988
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
989
|
+
throw new Error(
|
|
990
|
+
"Responses function arguments must be a JSON object"
|
|
991
|
+
);
|
|
992
|
+
return { id: item.call_id, name: item.name, args };
|
|
993
|
+
}) : [];
|
|
711
994
|
return {
|
|
712
995
|
content,
|
|
713
996
|
toolCalls,
|
|
997
|
+
providerMetadata: responseMetadata(response),
|
|
714
998
|
usage: response?.usage ? {
|
|
715
999
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
716
1000
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -721,6 +1005,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
721
1005
|
}
|
|
722
1006
|
buildResponsesPayload(request, stream) {
|
|
723
1007
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1008
|
+
const options = resolveOpenAITransport(
|
|
1009
|
+
this.config,
|
|
1010
|
+
request.config?.openai
|
|
1011
|
+
).responses;
|
|
1012
|
+
const parameters = buildOpenAIParameters(
|
|
1013
|
+
request.config?.model || this.model,
|
|
1014
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1015
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1016
|
+
this.parameterOptions(request),
|
|
1017
|
+
"responses"
|
|
1018
|
+
);
|
|
724
1019
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
725
1020
|
request.config?.responseFormat
|
|
726
1021
|
);
|
|
@@ -730,18 +1025,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
730
1025
|
instructions: request.systemPrompt,
|
|
731
1026
|
input: this.buildResponsesInput(request),
|
|
732
1027
|
tools: this.buildResponsesTools(
|
|
733
|
-
|
|
1028
|
+
this.toolDefinitions(request),
|
|
734
1029
|
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
735
1030
|
this.buildResponsesWebSearchTool(request)
|
|
736
1031
|
),
|
|
737
|
-
tool_choice: openaiToolOptions?.toolChoice
|
|
1032
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
738
1033
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1034
|
+
...parameters,
|
|
1035
|
+
...options?.reasoningSummary ? {
|
|
1036
|
+
reasoning: {
|
|
1037
|
+
...parameters.reasoning,
|
|
1038
|
+
summary: options.reasoningSummary
|
|
1039
|
+
}
|
|
1040
|
+
} : {},
|
|
1041
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1042
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1043
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1044
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1045
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1046
|
+
...options?.store === false || options?.include ? {
|
|
1047
|
+
include: [
|
|
1048
|
+
.../* @__PURE__ */ new Set([
|
|
1049
|
+
...options?.include ?? [],
|
|
1050
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1051
|
+
])
|
|
1052
|
+
]
|
|
1053
|
+
} : {},
|
|
745
1054
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
746
1055
|
stream
|
|
747
1056
|
};
|
|
@@ -749,10 +1058,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
749
1058
|
}
|
|
750
1059
|
async completeWithResponses(request) {
|
|
751
1060
|
const client = await this.getClient();
|
|
1061
|
+
throwIfAborted(request.signal);
|
|
752
1062
|
const payload = this.buildResponsesPayload(request, false);
|
|
753
1063
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
754
|
-
|
|
1064
|
+
if (!client.responses?.create)
|
|
1065
|
+
throw new Error(
|
|
1066
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1067
|
+
);
|
|
1068
|
+
const response = await client.responses.create(payload, {
|
|
1069
|
+
signal: request.signal
|
|
1070
|
+
});
|
|
1071
|
+
throwIfAborted(request.signal);
|
|
755
1072
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1073
|
+
if (response.status && response.status !== "completed") {
|
|
1074
|
+
throw Object.assign(
|
|
1075
|
+
new Error(
|
|
1076
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
1077
|
+
),
|
|
1078
|
+
response.error
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
756
1081
|
return this.parseResponsesResult(response);
|
|
757
1082
|
}
|
|
758
1083
|
/**
|
|
@@ -770,13 +1095,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
770
1095
|
*/
|
|
771
1096
|
async *streamWithResponses(request) {
|
|
772
1097
|
const client = await this.getClient();
|
|
1098
|
+
throwIfAborted(request.signal);
|
|
773
1099
|
const payload = this.buildResponsesPayload(request, true);
|
|
774
1100
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
775
|
-
|
|
1101
|
+
if (!client.responses?.create)
|
|
1102
|
+
throw new Error(
|
|
1103
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1104
|
+
);
|
|
1105
|
+
const stream = await client.responses.create(payload, {
|
|
1106
|
+
signal: request.signal
|
|
1107
|
+
});
|
|
776
1108
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
777
1109
|
let usage;
|
|
778
1110
|
const citations = [];
|
|
1111
|
+
let completed = false;
|
|
1112
|
+
let terminalResponse;
|
|
1113
|
+
let responseIdentity = {};
|
|
1114
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1115
|
+
let streamedText = "";
|
|
779
1116
|
function* flushToolCalls() {
|
|
1117
|
+
for (const call of toolCalls.values()) {
|
|
1118
|
+
if (!call.name)
|
|
1119
|
+
throw new Error("Responses tool call is missing its name");
|
|
1120
|
+
JSON.parse(call.arguments || "{}");
|
|
1121
|
+
}
|
|
780
1122
|
for (const call of toolCalls.values()) {
|
|
781
1123
|
yield { type: "action:start", id: call.id, name: call.name };
|
|
782
1124
|
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
@@ -785,19 +1127,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
785
1127
|
toolCalls.clear();
|
|
786
1128
|
}
|
|
787
1129
|
for await (const event of stream) {
|
|
788
|
-
|
|
1130
|
+
throwIfAborted(request.signal);
|
|
789
1131
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1132
|
+
if (completed)
|
|
1133
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
790
1134
|
const index = event?.output_index ?? 0;
|
|
791
1135
|
switch (event?.type) {
|
|
1136
|
+
case "response.created":
|
|
1137
|
+
responseIdentity = {
|
|
1138
|
+
id: event.response?.id,
|
|
1139
|
+
model: event.response?.model
|
|
1140
|
+
};
|
|
1141
|
+
break;
|
|
1142
|
+
case "response.output_item.done":
|
|
1143
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1144
|
+
if (event.item?.type === "function_call") {
|
|
1145
|
+
toolCalls.set(index, {
|
|
1146
|
+
id: event.item.call_id,
|
|
1147
|
+
name: event.item.name,
|
|
1148
|
+
arguments: event.item.arguments
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
break;
|
|
792
1152
|
case "response.output_text.delta":
|
|
793
1153
|
if (event.delta) {
|
|
1154
|
+
streamedText += event.delta;
|
|
794
1155
|
yield { type: "message:delta", content: event.delta };
|
|
795
1156
|
}
|
|
796
1157
|
break;
|
|
797
1158
|
case "response.output_item.added":
|
|
798
1159
|
if (event.item?.type === "function_call") {
|
|
799
1160
|
toolCalls.set(index, {
|
|
800
|
-
id: event.item.call_id ??
|
|
1161
|
+
id: event.item.call_id ?? "",
|
|
801
1162
|
name: event.item.name ?? "",
|
|
802
1163
|
arguments: event.item.arguments ?? ""
|
|
803
1164
|
});
|
|
@@ -815,8 +1176,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
815
1176
|
}
|
|
816
1177
|
break;
|
|
817
1178
|
}
|
|
818
|
-
case "response.completed":
|
|
819
|
-
|
|
1179
|
+
case "response.completed": {
|
|
1180
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1181
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
1182
|
+
completed = true;
|
|
1183
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
820
1184
|
const found = this.collectResponsesCitations(
|
|
821
1185
|
event.response?.output,
|
|
822
1186
|
citations.length
|
|
@@ -832,21 +1196,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
832
1196
|
}
|
|
833
1197
|
break;
|
|
834
1198
|
}
|
|
835
|
-
case "
|
|
836
|
-
case "response.failed":
|
|
1199
|
+
case "response.incomplete":
|
|
837
1200
|
throw new Error(
|
|
838
|
-
|
|
1201
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
839
1202
|
);
|
|
1203
|
+
case "error":
|
|
1204
|
+
case "response.failed": {
|
|
1205
|
+
const details = event.response?.error ?? event;
|
|
1206
|
+
throw Object.assign(
|
|
1207
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
1208
|
+
{
|
|
1209
|
+
code: details.code,
|
|
1210
|
+
param: details.param,
|
|
1211
|
+
status: details.status
|
|
1212
|
+
}
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
840
1215
|
}
|
|
841
1216
|
}
|
|
1217
|
+
throwIfAborted(request.signal);
|
|
1218
|
+
if (!completed)
|
|
1219
|
+
throw new Error("Responses stream ended without a completed response");
|
|
1220
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1221
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1222
|
+
toolCalls.clear();
|
|
1223
|
+
for (const [index, item] of output.entries()) {
|
|
1224
|
+
if (item.type === "function_call") {
|
|
1225
|
+
toolCalls.set(index, {
|
|
1226
|
+
id: item.call_id,
|
|
1227
|
+
name: item.name,
|
|
1228
|
+
arguments: item.arguments
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1233
|
+
for (const call of toolCalls.values()) {
|
|
1234
|
+
if (!call.id || !call.name)
|
|
1235
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1236
|
+
uniqueCalls.set(call.id, call);
|
|
1237
|
+
}
|
|
1238
|
+
toolCalls.clear();
|
|
1239
|
+
[...uniqueCalls.values()].forEach(
|
|
1240
|
+
(call, index) => toolCalls.set(index, call)
|
|
1241
|
+
);
|
|
1242
|
+
if (!output.length && toolCalls.size) {
|
|
1243
|
+
for (const call of toolCalls.values())
|
|
1244
|
+
output.push({
|
|
1245
|
+
type: "function_call",
|
|
1246
|
+
call_id: call.id,
|
|
1247
|
+
name: call.name,
|
|
1248
|
+
arguments: call.arguments
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
if (!output.length && streamedText)
|
|
1252
|
+
output.push({
|
|
1253
|
+
type: "message",
|
|
1254
|
+
role: "assistant",
|
|
1255
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1256
|
+
});
|
|
1257
|
+
terminalResponse.output = output;
|
|
1258
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1259
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1260
|
+
yield {
|
|
1261
|
+
type: "message:delta",
|
|
1262
|
+
content: finalText.slice(streamedText.length)
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
842
1265
|
yield* flushToolCalls();
|
|
843
1266
|
if (citations.length > 0) {
|
|
844
1267
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
845
1268
|
}
|
|
846
|
-
yield {
|
|
847
|
-
|
|
1269
|
+
yield {
|
|
1270
|
+
type: "message:end",
|
|
1271
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1272
|
+
};
|
|
1273
|
+
yield {
|
|
1274
|
+
type: "done",
|
|
1275
|
+
usage,
|
|
1276
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1277
|
+
};
|
|
848
1278
|
}
|
|
849
1279
|
async *stream(request) {
|
|
1280
|
+
throwIfAborted(request.signal);
|
|
850
1281
|
if (this.shouldUseResponsesApi(request)) {
|
|
851
1282
|
const messageId2 = generateMessageId();
|
|
852
1283
|
yield { type: "message:start", id: messageId2 };
|
|
@@ -854,47 +1285,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
854
1285
|
yield* this.streamWithResponses(request);
|
|
855
1286
|
return;
|
|
856
1287
|
} catch (error) {
|
|
857
|
-
yield
|
|
858
|
-
type: "error",
|
|
859
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
860
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
861
|
-
};
|
|
1288
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
862
1289
|
return;
|
|
863
1290
|
}
|
|
864
1291
|
}
|
|
865
1292
|
const client = await this.getClient();
|
|
866
1293
|
let messages;
|
|
867
1294
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
868
|
-
const processedMessages = request.rawMessages
|
|
869
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
870
|
-
return { ...msg, content: null };
|
|
871
|
-
}
|
|
872
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
873
|
-
if (hasAttachments) {
|
|
874
|
-
const content = [];
|
|
875
|
-
if (msg.content) {
|
|
876
|
-
content.push({ type: "text", text: msg.content });
|
|
877
|
-
}
|
|
878
|
-
for (const attachment of msg.attachments) {
|
|
879
|
-
if (attachment.type === "image") {
|
|
880
|
-
let imageUrl;
|
|
881
|
-
if (attachment.url) {
|
|
882
|
-
imageUrl = attachment.url;
|
|
883
|
-
} else if (attachment.data) {
|
|
884
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
885
|
-
} else {
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
content.push({
|
|
889
|
-
type: "image_url",
|
|
890
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
return { ...msg, content, attachments: void 0 };
|
|
895
|
-
}
|
|
896
|
-
return msg;
|
|
897
|
-
});
|
|
1295
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
898
1296
|
if (request.systemPrompt) {
|
|
899
1297
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
900
1298
|
if (!hasSystem) {
|
|
@@ -914,7 +1312,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
914
1312
|
request.systemPrompt
|
|
915
1313
|
);
|
|
916
1314
|
}
|
|
917
|
-
const tools = request.
|
|
1315
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1316
|
+
type: "function",
|
|
1317
|
+
function: {
|
|
1318
|
+
name: tool.name,
|
|
1319
|
+
description: tool.description,
|
|
1320
|
+
parameters: normalizeObjectJsonSchema(
|
|
1321
|
+
tool.inputSchema
|
|
1322
|
+
)
|
|
1323
|
+
}
|
|
1324
|
+
})) : formatTools(request.actions ?? []);
|
|
918
1325
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
919
1326
|
if (webSearchConfig) {
|
|
920
1327
|
const webSearchTool = {
|
|
@@ -943,26 +1350,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
943
1350
|
tools: tools.length > 0 ? tools : void 0,
|
|
944
1351
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
945
1352
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
946
|
-
...
|
|
1353
|
+
...buildOpenAIParameters(
|
|
947
1354
|
modelIdForPayload,
|
|
948
1355
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
949
|
-
request.config?.temperature ?? this.config.temperature
|
|
1356
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1357
|
+
this.parameterOptions(request)
|
|
950
1358
|
),
|
|
951
1359
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
952
1360
|
stream: true,
|
|
953
1361
|
stream_options: { include_usage: true }
|
|
954
1362
|
};
|
|
955
1363
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
956
|
-
const stream = await client.chat.completions.create(payload
|
|
957
|
-
|
|
1364
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1365
|
+
signal: request.signal
|
|
1366
|
+
});
|
|
1367
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1368
|
+
let finishReason;
|
|
958
1369
|
const collectedCitations = [];
|
|
959
1370
|
let citationIndex = 0;
|
|
960
1371
|
let usage;
|
|
961
1372
|
for await (const chunk of stream) {
|
|
962
1373
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
963
|
-
|
|
964
|
-
break;
|
|
965
|
-
}
|
|
1374
|
+
throwIfAborted(request.signal);
|
|
966
1375
|
const delta = chunk.choices[0]?.delta;
|
|
967
1376
|
const choice = chunk.choices[0];
|
|
968
1377
|
if (delta?.content) {
|
|
@@ -985,38 +1394,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
985
1394
|
}
|
|
986
1395
|
}
|
|
987
1396
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
const tcExtraContent = toolCall.extra_content;
|
|
1004
|
-
currentToolCall = {
|
|
1005
|
-
id: toolCall.id,
|
|
1006
|
-
name: toolCall.function?.name || "",
|
|
1007
|
-
arguments: toolCall.function?.arguments || "",
|
|
1008
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
1009
|
-
};
|
|
1010
|
-
yield {
|
|
1011
|
-
type: "action:start",
|
|
1012
|
-
id: currentToolCall.id,
|
|
1013
|
-
name: currentToolCall.name,
|
|
1014
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
1015
|
-
};
|
|
1016
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
1017
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1397
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1398
|
+
const index = part.index ?? 0;
|
|
1399
|
+
const call = toolCalls.get(index) ?? {
|
|
1400
|
+
id: "",
|
|
1401
|
+
name: "",
|
|
1402
|
+
arguments: ""
|
|
1403
|
+
};
|
|
1404
|
+
if (part.id) call.id = part.id;
|
|
1405
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1406
|
+
if (part.function?.arguments)
|
|
1407
|
+
call.arguments += part.function.arguments;
|
|
1408
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1409
|
+
toolCalls.set(index, call);
|
|
1020
1410
|
}
|
|
1021
1411
|
if (chunk.usage) {
|
|
1022
1412
|
usage = {
|
|
@@ -1025,21 +1415,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1025
1415
|
total_tokens: chunk.usage.total_tokens
|
|
1026
1416
|
};
|
|
1027
1417
|
}
|
|
1028
|
-
if (choice?.finish_reason)
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1418
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1419
|
+
}
|
|
1420
|
+
throwIfAborted(request.signal);
|
|
1421
|
+
if (!finishReason)
|
|
1422
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1423
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1424
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1425
|
+
}
|
|
1426
|
+
for (const call of toolCalls.values()) {
|
|
1427
|
+
if (!call.id || !call.name)
|
|
1428
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1429
|
+
JSON.parse(call.arguments || "{}");
|
|
1430
|
+
}
|
|
1431
|
+
for (const call of toolCalls.values()) {
|
|
1432
|
+
yield {
|
|
1433
|
+
type: "action:start",
|
|
1434
|
+
id: call.id,
|
|
1435
|
+
name: call.name,
|
|
1436
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1437
|
+
};
|
|
1438
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1439
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
1043
1440
|
}
|
|
1044
1441
|
if (collectedCitations.length > 0) {
|
|
1045
1442
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -1048,26 +1445,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1048
1445
|
yield { type: "message:end" };
|
|
1049
1446
|
yield { type: "done", usage };
|
|
1050
1447
|
} catch (error) {
|
|
1051
|
-
yield {
|
|
1052
|
-
type: "error",
|
|
1053
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
1054
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
1055
|
-
};
|
|
1448
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
1056
1449
|
}
|
|
1057
1450
|
}
|
|
1058
1451
|
async complete(request) {
|
|
1452
|
+
throwIfAborted(request.signal);
|
|
1059
1453
|
if (this.shouldUseResponsesApi(request)) {
|
|
1060
1454
|
return this.completeWithResponses(request);
|
|
1061
1455
|
}
|
|
1062
1456
|
const client = await this.getClient();
|
|
1063
1457
|
let messages;
|
|
1064
1458
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1065
|
-
const sanitized = request.rawMessages
|
|
1066
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
1067
|
-
return { ...msg, content: null };
|
|
1068
|
-
}
|
|
1069
|
-
return msg;
|
|
1070
|
-
});
|
|
1459
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
1071
1460
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
1072
1461
|
messages = [
|
|
1073
1462
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -1082,7 +1471,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1082
1471
|
request.systemPrompt
|
|
1083
1472
|
);
|
|
1084
1473
|
}
|
|
1085
|
-
const tools = request.
|
|
1474
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1475
|
+
type: "function",
|
|
1476
|
+
function: {
|
|
1477
|
+
name: tool.name,
|
|
1478
|
+
description: tool.description,
|
|
1479
|
+
parameters: normalizeObjectJsonSchema(
|
|
1480
|
+
tool.inputSchema
|
|
1481
|
+
)
|
|
1482
|
+
}
|
|
1483
|
+
})) : formatTools(request.actions ?? []);
|
|
1086
1484
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1087
1485
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
1088
1486
|
type: "function",
|
|
@@ -1097,16 +1495,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1097
1495
|
tools: tools.length > 0 ? tools : void 0,
|
|
1098
1496
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
1099
1497
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
1100
|
-
...
|
|
1498
|
+
...buildOpenAIParameters(
|
|
1101
1499
|
modelIdForCompletePayload,
|
|
1102
1500
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1103
|
-
request.config?.temperature ?? this.config.temperature
|
|
1501
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1502
|
+
this.parameterOptions(request)
|
|
1104
1503
|
),
|
|
1105
1504
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
1106
1505
|
stream: false
|
|
1107
1506
|
};
|
|
1108
1507
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1109
|
-
const response = await client.chat.completions.create(payload
|
|
1508
|
+
const response = await client.chat.completions.create(payload, {
|
|
1509
|
+
signal: request.signal
|
|
1510
|
+
});
|
|
1110
1511
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1111
1512
|
const choice = response.choices?.[0];
|
|
1112
1513
|
const message = choice?.message;
|
|
@@ -1259,7 +1660,7 @@ var AnthropicAdapter = class {
|
|
|
1259
1660
|
}
|
|
1260
1661
|
pendingToolResults.push({
|
|
1261
1662
|
tool_use_id: toolCallId,
|
|
1262
|
-
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1663
|
+
content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1263
1664
|
});
|
|
1264
1665
|
} else if (msg.role === "user") {
|
|
1265
1666
|
if (pendingToolResults.length > 0) {
|
|
@@ -1380,14 +1781,23 @@ var AnthropicAdapter = class {
|
|
|
1380
1781
|
buildRequestOptions(request) {
|
|
1381
1782
|
const responseFormat = request.config?.responseFormat;
|
|
1382
1783
|
const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
|
|
1383
|
-
|
|
1784
|
+
let systemMessage;
|
|
1384
1785
|
let messages;
|
|
1385
1786
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1787
|
+
systemMessage = extractAnthropicSystemPrompt(
|
|
1788
|
+
request.rawMessages,
|
|
1789
|
+
request.systemPrompt
|
|
1790
|
+
);
|
|
1386
1791
|
messages = this.convertToAnthropicMessages(request.rawMessages);
|
|
1387
1792
|
} else {
|
|
1388
|
-
const formatted = formatMessagesForAnthropic(
|
|
1793
|
+
const formatted = formatMessagesForAnthropic(
|
|
1794
|
+
request.messages,
|
|
1795
|
+
request.systemPrompt
|
|
1796
|
+
);
|
|
1797
|
+
systemMessage = formatted.system;
|
|
1389
1798
|
messages = formatted.messages;
|
|
1390
1799
|
}
|
|
1800
|
+
systemMessage += jsonObjectSuffix;
|
|
1391
1801
|
const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
|
|
1392
1802
|
const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
|
|
1393
1803
|
request.toolDefinitions ?? [],
|
|
@@ -1730,7 +2140,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
|
|
|
1730
2140
|
for (const msg of messages) {
|
|
1731
2141
|
const baseMessage = {
|
|
1732
2142
|
role: msg.role,
|
|
1733
|
-
content: msg.content ?? ""
|
|
2143
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
|
|
1734
2144
|
};
|
|
1735
2145
|
if (msg.role === "user" && msg.metadata?.attachments) {
|
|
1736
2146
|
const images = await extractImagesFromAttachments(
|
|
@@ -1767,7 +2177,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
|
|
|
1767
2177
|
for (const msg of rawMessages) {
|
|
1768
2178
|
const baseMessage = {
|
|
1769
2179
|
role: msg.role,
|
|
1770
|
-
content: msg.content ?? ""
|
|
2180
|
+
content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
|
|
1771
2181
|
};
|
|
1772
2182
|
if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
|
|
1773
2183
|
const attachments = msg.attachments;
|
|
@@ -1977,6 +2387,10 @@ function messageToGeminiContent(msg) {
|
|
|
1977
2387
|
if (msg.role === "system") return null;
|
|
1978
2388
|
const parts = [];
|
|
1979
2389
|
if (msg.role === "tool" && msg.tool_call_id) {
|
|
2390
|
+
msg = {
|
|
2391
|
+
...msg,
|
|
2392
|
+
content: textToolOutput(msg.modelOutput, msg.content ?? "")
|
|
2393
|
+
};
|
|
1980
2394
|
let responseData;
|
|
1981
2395
|
try {
|
|
1982
2396
|
responseData = JSON.parse(msg.content || "{}");
|
|
@@ -2437,6 +2851,13 @@ var AzureAdapter = class {
|
|
|
2437
2851
|
let messages;
|
|
2438
2852
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2439
2853
|
const processedMessages = request.rawMessages.map((msg) => {
|
|
2854
|
+
const { modelOutput, ...rest } = msg;
|
|
2855
|
+
msg = {
|
|
2856
|
+
...rest,
|
|
2857
|
+
...msg.role === "tool" ? {
|
|
2858
|
+
content: textToolOutput(modelOutput, String(msg.content ?? ""))
|
|
2859
|
+
} : {}
|
|
2860
|
+
};
|
|
2440
2861
|
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
2441
2862
|
if (hasAttachments) {
|
|
2442
2863
|
const content = [];
|
|
@@ -2567,7 +2988,10 @@ var AzureAdapter = class {
|
|
|
2567
2988
|
const client = await this.getClient();
|
|
2568
2989
|
let messages;
|
|
2569
2990
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
2570
|
-
messages = request.rawMessages
|
|
2991
|
+
messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
|
|
2992
|
+
...msg,
|
|
2993
|
+
...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
|
|
2994
|
+
}));
|
|
2571
2995
|
if (request.systemPrompt) {
|
|
2572
2996
|
const hasSystem = messages.some((m) => m.role === "system");
|
|
2573
2997
|
if (!hasSystem) {
|