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