@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
|
@@ -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(
|
|
@@ -106,16 +260,6 @@ function normalizeObjectJsonSchema(schema) {
|
|
|
106
260
|
}
|
|
107
261
|
return normalized;
|
|
108
262
|
}
|
|
109
|
-
function isOpenAIReasoningModel(modelId) {
|
|
110
|
-
if (!modelId) return false;
|
|
111
|
-
return /^(o1|o3|o4|gpt-5)/i.test(modelId);
|
|
112
|
-
}
|
|
113
|
-
function buildOpenAITokenParams(modelId, maxTokens, temperature) {
|
|
114
|
-
if (isOpenAIReasoningModel(modelId)) {
|
|
115
|
-
return { max_completion_tokens: maxTokens };
|
|
116
|
-
}
|
|
117
|
-
return { max_tokens: maxTokens, temperature };
|
|
118
|
-
}
|
|
119
263
|
function toOpenAIResponseFormat(rf) {
|
|
120
264
|
if (!rf) return void 0;
|
|
121
265
|
if (rf.type === "json_object") return { type: "json_object" };
|
|
@@ -200,7 +344,7 @@ function messageToOpenAIContent(message) {
|
|
|
200
344
|
}
|
|
201
345
|
return blocks;
|
|
202
346
|
}
|
|
203
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
347
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
204
348
|
const formatted = [];
|
|
205
349
|
if (systemPrompt) {
|
|
206
350
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -218,7 +362,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
218
362
|
const assistantMsg = {
|
|
219
363
|
role: "assistant",
|
|
220
364
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
221
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
365
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
366
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
222
367
|
};
|
|
223
368
|
if (hasToolCalls) {
|
|
224
369
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -227,7 +372,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
227
372
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
228
373
|
formatted.push({
|
|
229
374
|
role: "tool",
|
|
230
|
-
content: msg.content ?? "",
|
|
375
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
376
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
231
377
|
tool_call_id: msg.tool_call_id
|
|
232
378
|
});
|
|
233
379
|
}
|
|
@@ -439,7 +585,7 @@ function formatMessages(messages) {
|
|
|
439
585
|
return {
|
|
440
586
|
role: "tool",
|
|
441
587
|
tool_call_id: msg.toolCallId,
|
|
442
|
-
content: msg.content
|
|
588
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
443
589
|
};
|
|
444
590
|
default:
|
|
445
591
|
return msg;
|
|
@@ -447,6 +593,86 @@ function formatMessages(messages) {
|
|
|
447
593
|
});
|
|
448
594
|
}
|
|
449
595
|
|
|
596
|
+
// src/core/openai-responses.ts
|
|
597
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
598
|
+
return {
|
|
599
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
600
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
604
|
+
const api = transport.api ?? "auto";
|
|
605
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
606
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
607
|
+
}
|
|
608
|
+
if (api === "chat") {
|
|
609
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
610
|
+
throw new Error(
|
|
611
|
+
"Responses options and native tool search require the Responses API"
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
let officialEndpoint = !baseURL;
|
|
617
|
+
try {
|
|
618
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
619
|
+
} catch {
|
|
620
|
+
}
|
|
621
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
622
|
+
}
|
|
623
|
+
function responseMetadata(response) {
|
|
624
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
625
|
+
return {
|
|
626
|
+
openai: {
|
|
627
|
+
response: {
|
|
628
|
+
id: response?.id,
|
|
629
|
+
model: response?.model,
|
|
630
|
+
status: response?.status,
|
|
631
|
+
output,
|
|
632
|
+
usage: response?.usage,
|
|
633
|
+
store: response?.store,
|
|
634
|
+
refusals: output.flatMap(
|
|
635
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
636
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
637
|
+
).map((part) => part.refusal) : []
|
|
638
|
+
)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function getResponseReplay(metadata) {
|
|
644
|
+
const response = metadata?.openai?.response;
|
|
645
|
+
if (response === void 0) return void 0;
|
|
646
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
647
|
+
throw new Error(
|
|
648
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
return response.output;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// src/core/provider-error.ts
|
|
655
|
+
function providerErrorEvent(error, code) {
|
|
656
|
+
const details = error ?? {};
|
|
657
|
+
const event = {
|
|
658
|
+
type: "error",
|
|
659
|
+
message: error instanceof Error ? error.message : String(error),
|
|
660
|
+
code,
|
|
661
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
662
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
663
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
664
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
665
|
+
};
|
|
666
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
667
|
+
return event;
|
|
668
|
+
}
|
|
669
|
+
function throwIfAborted(signal) {
|
|
670
|
+
if (!signal?.aborted) return;
|
|
671
|
+
const error = new Error("The operation was aborted");
|
|
672
|
+
error.name = "AbortError";
|
|
673
|
+
throw error;
|
|
674
|
+
}
|
|
675
|
+
|
|
450
676
|
// src/core/utils.ts
|
|
451
677
|
function generateId(prefix = "id") {
|
|
452
678
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -478,39 +704,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
478
704
|
const { default: OpenAI } = await import('openai');
|
|
479
705
|
this.client = new OpenAI({
|
|
480
706
|
apiKey: this.config.apiKey,
|
|
481
|
-
baseURL: this.config.baseUrl
|
|
707
|
+
baseURL: this.config.baseUrl,
|
|
708
|
+
organization: this.config.organization,
|
|
709
|
+
defaultHeaders: this.config.headers
|
|
482
710
|
});
|
|
483
711
|
}
|
|
484
712
|
return this.client;
|
|
485
713
|
}
|
|
714
|
+
parameterOptions(request) {
|
|
715
|
+
return {
|
|
716
|
+
...this.config,
|
|
717
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
toolDefinitions(request) {
|
|
721
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
722
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
723
|
+
name: tool.function.name,
|
|
724
|
+
description: tool.function.description ?? "",
|
|
725
|
+
location: "server",
|
|
726
|
+
inputSchema: tool.function.parameters
|
|
727
|
+
}));
|
|
728
|
+
}
|
|
486
729
|
shouldUseResponsesApi(request) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
request.
|
|
730
|
+
return useOpenAIResponses(
|
|
731
|
+
request.config?.model || this.model,
|
|
732
|
+
this.config.baseUrl,
|
|
733
|
+
this.parameterOptions(request),
|
|
734
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
735
|
+
this.toolDefinitions(request).length > 0,
|
|
736
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
737
|
+
(message) => message.providerMetadata?.openai?.response
|
|
738
|
+
),
|
|
739
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
494
740
|
);
|
|
495
741
|
}
|
|
496
|
-
/**
|
|
497
|
-
* Whether this model rejects function tools on /v1/chat/completions.
|
|
498
|
-
*
|
|
499
|
-
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
500
|
-
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
501
|
-
* restriction and may not implement /v1/responses at all.
|
|
502
|
-
*/
|
|
503
|
-
requiresResponsesApiForTools(modelId) {
|
|
504
|
-
if (!modelId) return false;
|
|
505
|
-
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
506
|
-
const baseUrl = this.config.baseUrl;
|
|
507
|
-
if (!baseUrl) return true;
|
|
508
|
-
try {
|
|
509
|
-
return new URL(baseUrl).hostname === "api.openai.com";
|
|
510
|
-
} catch {
|
|
511
|
-
return false;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
742
|
/**
|
|
515
743
|
* Convert one message's content to Responses content parts.
|
|
516
744
|
*
|
|
@@ -541,11 +769,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
541
769
|
});
|
|
542
770
|
}
|
|
543
771
|
buildResponsesInput(request) {
|
|
544
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
772
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
545
773
|
const input = [];
|
|
546
774
|
for (const message of sourceMessages) {
|
|
547
775
|
if (message.role === "system") {
|
|
548
|
-
if (
|
|
776
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
549
777
|
input.push({
|
|
550
778
|
type: "message",
|
|
551
779
|
role: "developer",
|
|
@@ -555,6 +783,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
555
783
|
continue;
|
|
556
784
|
}
|
|
557
785
|
if (message.role === "assistant") {
|
|
786
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
787
|
+
if (replay) {
|
|
788
|
+
input.push(...replay);
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
558
791
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
559
792
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
560
793
|
input.push({
|
|
@@ -578,7 +811,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
578
811
|
input.push({
|
|
579
812
|
type: "function_call_output",
|
|
580
813
|
call_id: message.tool_call_id,
|
|
581
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
814
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
582
815
|
});
|
|
583
816
|
continue;
|
|
584
817
|
}
|
|
@@ -591,6 +824,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
591
824
|
}
|
|
592
825
|
return input;
|
|
593
826
|
}
|
|
827
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
828
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
829
|
+
return messages.map((message) => {
|
|
830
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
831
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
832
|
+
if (preserveProviderMetadata && modelOutput)
|
|
833
|
+
rest.modelOutput = modelOutput;
|
|
834
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
835
|
+
content = null;
|
|
836
|
+
}
|
|
837
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
838
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
839
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
840
|
+
).filter((image) => image !== null);
|
|
841
|
+
if (images.length > 0) {
|
|
842
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
843
|
+
content = [...parts, ...images];
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return {
|
|
847
|
+
...rest,
|
|
848
|
+
content,
|
|
849
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
850
|
+
};
|
|
851
|
+
});
|
|
852
|
+
}
|
|
594
853
|
/**
|
|
595
854
|
* Build the web_search tool for the Responses API, or undefined when web
|
|
596
855
|
* search is off.
|
|
@@ -669,20 +928,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
669
928
|
).map((part) => part.text) : []
|
|
670
929
|
).join("") : "";
|
|
671
930
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
name
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
931
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
932
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
933
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
934
|
+
throw new Error("Responses contains an incomplete function call");
|
|
935
|
+
}
|
|
936
|
+
if (seenCallIds.has(item.call_id))
|
|
937
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
938
|
+
seenCallIds.add(item.call_id);
|
|
939
|
+
const args = JSON.parse(item.arguments);
|
|
940
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
941
|
+
throw new Error(
|
|
942
|
+
"Responses function arguments must be a JSON object"
|
|
943
|
+
);
|
|
944
|
+
return { id: item.call_id, name: item.name, args };
|
|
945
|
+
}) : [];
|
|
683
946
|
return {
|
|
684
947
|
content,
|
|
685
948
|
toolCalls,
|
|
949
|
+
providerMetadata: responseMetadata(response),
|
|
686
950
|
usage: response?.usage ? {
|
|
687
951
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
688
952
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -693,6 +957,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
693
957
|
}
|
|
694
958
|
buildResponsesPayload(request, stream) {
|
|
695
959
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
960
|
+
const options = resolveOpenAITransport(
|
|
961
|
+
this.config,
|
|
962
|
+
request.config?.openai
|
|
963
|
+
).responses;
|
|
964
|
+
const parameters = buildOpenAIParameters(
|
|
965
|
+
request.config?.model || this.model,
|
|
966
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
967
|
+
request.config?.temperature ?? this.config.temperature,
|
|
968
|
+
this.parameterOptions(request),
|
|
969
|
+
"responses"
|
|
970
|
+
);
|
|
696
971
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
697
972
|
request.config?.responseFormat
|
|
698
973
|
);
|
|
@@ -702,18 +977,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
702
977
|
instructions: request.systemPrompt,
|
|
703
978
|
input: this.buildResponsesInput(request),
|
|
704
979
|
tools: this.buildResponsesTools(
|
|
705
|
-
|
|
980
|
+
this.toolDefinitions(request),
|
|
706
981
|
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
707
982
|
this.buildResponsesWebSearchTool(request)
|
|
708
983
|
),
|
|
709
|
-
tool_choice: openaiToolOptions?.toolChoice
|
|
984
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
710
985
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
986
|
+
...parameters,
|
|
987
|
+
...options?.reasoningSummary ? {
|
|
988
|
+
reasoning: {
|
|
989
|
+
...parameters.reasoning,
|
|
990
|
+
summary: options.reasoningSummary
|
|
991
|
+
}
|
|
992
|
+
} : {},
|
|
993
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
994
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
995
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
996
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
997
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
998
|
+
...options?.store === false || options?.include ? {
|
|
999
|
+
include: [
|
|
1000
|
+
.../* @__PURE__ */ new Set([
|
|
1001
|
+
...options?.include ?? [],
|
|
1002
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1003
|
+
])
|
|
1004
|
+
]
|
|
1005
|
+
} : {},
|
|
717
1006
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
718
1007
|
stream
|
|
719
1008
|
};
|
|
@@ -721,10 +1010,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
721
1010
|
}
|
|
722
1011
|
async completeWithResponses(request) {
|
|
723
1012
|
const client = await this.getClient();
|
|
1013
|
+
throwIfAborted(request.signal);
|
|
724
1014
|
const payload = this.buildResponsesPayload(request, false);
|
|
725
1015
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
726
|
-
|
|
1016
|
+
if (!client.responses?.create)
|
|
1017
|
+
throw new Error(
|
|
1018
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1019
|
+
);
|
|
1020
|
+
const response = await client.responses.create(payload, {
|
|
1021
|
+
signal: request.signal
|
|
1022
|
+
});
|
|
1023
|
+
throwIfAborted(request.signal);
|
|
727
1024
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1025
|
+
if (response.status && response.status !== "completed") {
|
|
1026
|
+
throw Object.assign(
|
|
1027
|
+
new Error(
|
|
1028
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
1029
|
+
),
|
|
1030
|
+
response.error
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
728
1033
|
return this.parseResponsesResult(response);
|
|
729
1034
|
}
|
|
730
1035
|
/**
|
|
@@ -742,13 +1047,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
742
1047
|
*/
|
|
743
1048
|
async *streamWithResponses(request) {
|
|
744
1049
|
const client = await this.getClient();
|
|
1050
|
+
throwIfAborted(request.signal);
|
|
745
1051
|
const payload = this.buildResponsesPayload(request, true);
|
|
746
1052
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
747
|
-
|
|
1053
|
+
if (!client.responses?.create)
|
|
1054
|
+
throw new Error(
|
|
1055
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1056
|
+
);
|
|
1057
|
+
const stream = await client.responses.create(payload, {
|
|
1058
|
+
signal: request.signal
|
|
1059
|
+
});
|
|
748
1060
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
749
1061
|
let usage;
|
|
750
1062
|
const citations = [];
|
|
1063
|
+
let completed = false;
|
|
1064
|
+
let terminalResponse;
|
|
1065
|
+
let responseIdentity = {};
|
|
1066
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1067
|
+
let streamedText = "";
|
|
751
1068
|
function* flushToolCalls() {
|
|
1069
|
+
for (const call of toolCalls.values()) {
|
|
1070
|
+
if (!call.name)
|
|
1071
|
+
throw new Error("Responses tool call is missing its name");
|
|
1072
|
+
JSON.parse(call.arguments || "{}");
|
|
1073
|
+
}
|
|
752
1074
|
for (const call of toolCalls.values()) {
|
|
753
1075
|
yield { type: "action:start", id: call.id, name: call.name };
|
|
754
1076
|
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
@@ -757,19 +1079,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
757
1079
|
toolCalls.clear();
|
|
758
1080
|
}
|
|
759
1081
|
for await (const event of stream) {
|
|
760
|
-
|
|
1082
|
+
throwIfAborted(request.signal);
|
|
761
1083
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1084
|
+
if (completed)
|
|
1085
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
762
1086
|
const index = event?.output_index ?? 0;
|
|
763
1087
|
switch (event?.type) {
|
|
1088
|
+
case "response.created":
|
|
1089
|
+
responseIdentity = {
|
|
1090
|
+
id: event.response?.id,
|
|
1091
|
+
model: event.response?.model
|
|
1092
|
+
};
|
|
1093
|
+
break;
|
|
1094
|
+
case "response.output_item.done":
|
|
1095
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1096
|
+
if (event.item?.type === "function_call") {
|
|
1097
|
+
toolCalls.set(index, {
|
|
1098
|
+
id: event.item.call_id,
|
|
1099
|
+
name: event.item.name,
|
|
1100
|
+
arguments: event.item.arguments
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
break;
|
|
764
1104
|
case "response.output_text.delta":
|
|
765
1105
|
if (event.delta) {
|
|
1106
|
+
streamedText += event.delta;
|
|
766
1107
|
yield { type: "message:delta", content: event.delta };
|
|
767
1108
|
}
|
|
768
1109
|
break;
|
|
769
1110
|
case "response.output_item.added":
|
|
770
1111
|
if (event.item?.type === "function_call") {
|
|
771
1112
|
toolCalls.set(index, {
|
|
772
|
-
id: event.item.call_id ??
|
|
1113
|
+
id: event.item.call_id ?? "",
|
|
773
1114
|
name: event.item.name ?? "",
|
|
774
1115
|
arguments: event.item.arguments ?? ""
|
|
775
1116
|
});
|
|
@@ -787,8 +1128,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
787
1128
|
}
|
|
788
1129
|
break;
|
|
789
1130
|
}
|
|
790
|
-
case "response.completed":
|
|
791
|
-
|
|
1131
|
+
case "response.completed": {
|
|
1132
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1133
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
1134
|
+
completed = true;
|
|
1135
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
792
1136
|
const found = this.collectResponsesCitations(
|
|
793
1137
|
event.response?.output,
|
|
794
1138
|
citations.length
|
|
@@ -804,21 +1148,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
804
1148
|
}
|
|
805
1149
|
break;
|
|
806
1150
|
}
|
|
807
|
-
case "
|
|
808
|
-
case "response.failed":
|
|
1151
|
+
case "response.incomplete":
|
|
809
1152
|
throw new Error(
|
|
810
|
-
|
|
1153
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
811
1154
|
);
|
|
1155
|
+
case "error":
|
|
1156
|
+
case "response.failed": {
|
|
1157
|
+
const details = event.response?.error ?? event;
|
|
1158
|
+
throw Object.assign(
|
|
1159
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
1160
|
+
{
|
|
1161
|
+
code: details.code,
|
|
1162
|
+
param: details.param,
|
|
1163
|
+
status: details.status
|
|
1164
|
+
}
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
throwIfAborted(request.signal);
|
|
1170
|
+
if (!completed)
|
|
1171
|
+
throw new Error("Responses stream ended without a completed response");
|
|
1172
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1173
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1174
|
+
toolCalls.clear();
|
|
1175
|
+
for (const [index, item] of output.entries()) {
|
|
1176
|
+
if (item.type === "function_call") {
|
|
1177
|
+
toolCalls.set(index, {
|
|
1178
|
+
id: item.call_id,
|
|
1179
|
+
name: item.name,
|
|
1180
|
+
arguments: item.arguments
|
|
1181
|
+
});
|
|
812
1182
|
}
|
|
813
1183
|
}
|
|
1184
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1185
|
+
for (const call of toolCalls.values()) {
|
|
1186
|
+
if (!call.id || !call.name)
|
|
1187
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1188
|
+
uniqueCalls.set(call.id, call);
|
|
1189
|
+
}
|
|
1190
|
+
toolCalls.clear();
|
|
1191
|
+
[...uniqueCalls.values()].forEach(
|
|
1192
|
+
(call, index) => toolCalls.set(index, call)
|
|
1193
|
+
);
|
|
1194
|
+
if (!output.length && toolCalls.size) {
|
|
1195
|
+
for (const call of toolCalls.values())
|
|
1196
|
+
output.push({
|
|
1197
|
+
type: "function_call",
|
|
1198
|
+
call_id: call.id,
|
|
1199
|
+
name: call.name,
|
|
1200
|
+
arguments: call.arguments
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
if (!output.length && streamedText)
|
|
1204
|
+
output.push({
|
|
1205
|
+
type: "message",
|
|
1206
|
+
role: "assistant",
|
|
1207
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1208
|
+
});
|
|
1209
|
+
terminalResponse.output = output;
|
|
1210
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1211
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1212
|
+
yield {
|
|
1213
|
+
type: "message:delta",
|
|
1214
|
+
content: finalText.slice(streamedText.length)
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
814
1217
|
yield* flushToolCalls();
|
|
815
1218
|
if (citations.length > 0) {
|
|
816
1219
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
817
1220
|
}
|
|
818
|
-
yield {
|
|
819
|
-
|
|
1221
|
+
yield {
|
|
1222
|
+
type: "message:end",
|
|
1223
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1224
|
+
};
|
|
1225
|
+
yield {
|
|
1226
|
+
type: "done",
|
|
1227
|
+
usage,
|
|
1228
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1229
|
+
};
|
|
820
1230
|
}
|
|
821
1231
|
async *stream(request) {
|
|
1232
|
+
throwIfAborted(request.signal);
|
|
822
1233
|
if (this.shouldUseResponsesApi(request)) {
|
|
823
1234
|
const messageId2 = generateMessageId();
|
|
824
1235
|
yield { type: "message:start", id: messageId2 };
|
|
@@ -826,47 +1237,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
826
1237
|
yield* this.streamWithResponses(request);
|
|
827
1238
|
return;
|
|
828
1239
|
} catch (error) {
|
|
829
|
-
yield
|
|
830
|
-
type: "error",
|
|
831
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
832
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
833
|
-
};
|
|
1240
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
834
1241
|
return;
|
|
835
1242
|
}
|
|
836
1243
|
}
|
|
837
1244
|
const client = await this.getClient();
|
|
838
1245
|
let messages;
|
|
839
1246
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
840
|
-
const processedMessages = request.rawMessages
|
|
841
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
842
|
-
return { ...msg, content: null };
|
|
843
|
-
}
|
|
844
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
845
|
-
if (hasAttachments) {
|
|
846
|
-
const content = [];
|
|
847
|
-
if (msg.content) {
|
|
848
|
-
content.push({ type: "text", text: msg.content });
|
|
849
|
-
}
|
|
850
|
-
for (const attachment of msg.attachments) {
|
|
851
|
-
if (attachment.type === "image") {
|
|
852
|
-
let imageUrl;
|
|
853
|
-
if (attachment.url) {
|
|
854
|
-
imageUrl = attachment.url;
|
|
855
|
-
} else if (attachment.data) {
|
|
856
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
857
|
-
} else {
|
|
858
|
-
continue;
|
|
859
|
-
}
|
|
860
|
-
content.push({
|
|
861
|
-
type: "image_url",
|
|
862
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
return { ...msg, content, attachments: void 0 };
|
|
867
|
-
}
|
|
868
|
-
return msg;
|
|
869
|
-
});
|
|
1247
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
870
1248
|
if (request.systemPrompt) {
|
|
871
1249
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
872
1250
|
if (!hasSystem) {
|
|
@@ -886,7 +1264,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
886
1264
|
request.systemPrompt
|
|
887
1265
|
);
|
|
888
1266
|
}
|
|
889
|
-
const tools = request.
|
|
1267
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1268
|
+
type: "function",
|
|
1269
|
+
function: {
|
|
1270
|
+
name: tool.name,
|
|
1271
|
+
description: tool.description,
|
|
1272
|
+
parameters: normalizeObjectJsonSchema(
|
|
1273
|
+
tool.inputSchema
|
|
1274
|
+
)
|
|
1275
|
+
}
|
|
1276
|
+
})) : formatTools(request.actions ?? []);
|
|
890
1277
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
891
1278
|
if (webSearchConfig) {
|
|
892
1279
|
const webSearchTool = {
|
|
@@ -915,26 +1302,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
915
1302
|
tools: tools.length > 0 ? tools : void 0,
|
|
916
1303
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
917
1304
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
918
|
-
...
|
|
1305
|
+
...buildOpenAIParameters(
|
|
919
1306
|
modelIdForPayload,
|
|
920
1307
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
921
|
-
request.config?.temperature ?? this.config.temperature
|
|
1308
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1309
|
+
this.parameterOptions(request)
|
|
922
1310
|
),
|
|
923
1311
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
924
1312
|
stream: true,
|
|
925
1313
|
stream_options: { include_usage: true }
|
|
926
1314
|
};
|
|
927
1315
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
928
|
-
const stream = await client.chat.completions.create(payload
|
|
929
|
-
|
|
1316
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1317
|
+
signal: request.signal
|
|
1318
|
+
});
|
|
1319
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1320
|
+
let finishReason;
|
|
930
1321
|
const collectedCitations = [];
|
|
931
1322
|
let citationIndex = 0;
|
|
932
1323
|
let usage;
|
|
933
1324
|
for await (const chunk of stream) {
|
|
934
1325
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
935
|
-
|
|
936
|
-
break;
|
|
937
|
-
}
|
|
1326
|
+
throwIfAborted(request.signal);
|
|
938
1327
|
const delta = chunk.choices[0]?.delta;
|
|
939
1328
|
const choice = chunk.choices[0];
|
|
940
1329
|
if (delta?.content) {
|
|
@@ -957,38 +1346,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
957
1346
|
}
|
|
958
1347
|
}
|
|
959
1348
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
|
-
const tcExtraContent = toolCall.extra_content;
|
|
976
|
-
currentToolCall = {
|
|
977
|
-
id: toolCall.id,
|
|
978
|
-
name: toolCall.function?.name || "",
|
|
979
|
-
arguments: toolCall.function?.arguments || "",
|
|
980
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
981
|
-
};
|
|
982
|
-
yield {
|
|
983
|
-
type: "action:start",
|
|
984
|
-
id: currentToolCall.id,
|
|
985
|
-
name: currentToolCall.name,
|
|
986
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
987
|
-
};
|
|
988
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
989
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
1349
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1350
|
+
const index = part.index ?? 0;
|
|
1351
|
+
const call = toolCalls.get(index) ?? {
|
|
1352
|
+
id: "",
|
|
1353
|
+
name: "",
|
|
1354
|
+
arguments: ""
|
|
1355
|
+
};
|
|
1356
|
+
if (part.id) call.id = part.id;
|
|
1357
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1358
|
+
if (part.function?.arguments)
|
|
1359
|
+
call.arguments += part.function.arguments;
|
|
1360
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1361
|
+
toolCalls.set(index, call);
|
|
992
1362
|
}
|
|
993
1363
|
if (chunk.usage) {
|
|
994
1364
|
usage = {
|
|
@@ -997,21 +1367,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
997
1367
|
total_tokens: chunk.usage.total_tokens
|
|
998
1368
|
};
|
|
999
1369
|
}
|
|
1000
|
-
if (choice?.finish_reason)
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1370
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1371
|
+
}
|
|
1372
|
+
throwIfAborted(request.signal);
|
|
1373
|
+
if (!finishReason)
|
|
1374
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1375
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1376
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1377
|
+
}
|
|
1378
|
+
for (const call of toolCalls.values()) {
|
|
1379
|
+
if (!call.id || !call.name)
|
|
1380
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1381
|
+
JSON.parse(call.arguments || "{}");
|
|
1382
|
+
}
|
|
1383
|
+
for (const call of toolCalls.values()) {
|
|
1384
|
+
yield {
|
|
1385
|
+
type: "action:start",
|
|
1386
|
+
id: call.id,
|
|
1387
|
+
name: call.name,
|
|
1388
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1389
|
+
};
|
|
1390
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1391
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
1015
1392
|
}
|
|
1016
1393
|
if (collectedCitations.length > 0) {
|
|
1017
1394
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -1020,26 +1397,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1020
1397
|
yield { type: "message:end" };
|
|
1021
1398
|
yield { type: "done", usage };
|
|
1022
1399
|
} catch (error) {
|
|
1023
|
-
yield {
|
|
1024
|
-
type: "error",
|
|
1025
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
1026
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
1027
|
-
};
|
|
1400
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
1028
1401
|
}
|
|
1029
1402
|
}
|
|
1030
1403
|
async complete(request) {
|
|
1404
|
+
throwIfAborted(request.signal);
|
|
1031
1405
|
if (this.shouldUseResponsesApi(request)) {
|
|
1032
1406
|
return this.completeWithResponses(request);
|
|
1033
1407
|
}
|
|
1034
1408
|
const client = await this.getClient();
|
|
1035
1409
|
let messages;
|
|
1036
1410
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1037
|
-
const sanitized = request.rawMessages
|
|
1038
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
1039
|
-
return { ...msg, content: null };
|
|
1040
|
-
}
|
|
1041
|
-
return msg;
|
|
1042
|
-
});
|
|
1411
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
1043
1412
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
1044
1413
|
messages = [
|
|
1045
1414
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -1054,7 +1423,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1054
1423
|
request.systemPrompt
|
|
1055
1424
|
);
|
|
1056
1425
|
}
|
|
1057
|
-
const tools = request.
|
|
1426
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1427
|
+
type: "function",
|
|
1428
|
+
function: {
|
|
1429
|
+
name: tool.name,
|
|
1430
|
+
description: tool.description,
|
|
1431
|
+
parameters: normalizeObjectJsonSchema(
|
|
1432
|
+
tool.inputSchema
|
|
1433
|
+
)
|
|
1434
|
+
}
|
|
1435
|
+
})) : formatTools(request.actions ?? []);
|
|
1058
1436
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1059
1437
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
1060
1438
|
type: "function",
|
|
@@ -1069,16 +1447,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1069
1447
|
tools: tools.length > 0 ? tools : void 0,
|
|
1070
1448
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
1071
1449
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
1072
|
-
...
|
|
1450
|
+
...buildOpenAIParameters(
|
|
1073
1451
|
modelIdForCompletePayload,
|
|
1074
1452
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1075
|
-
request.config?.temperature ?? this.config.temperature
|
|
1453
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1454
|
+
this.parameterOptions(request)
|
|
1076
1455
|
),
|
|
1077
1456
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
1078
1457
|
stream: false
|
|
1079
1458
|
};
|
|
1080
1459
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1081
|
-
const response = await client.chat.completions.create(payload
|
|
1460
|
+
const response = await client.chat.completions.create(payload, {
|
|
1461
|
+
signal: request.signal
|
|
1462
|
+
});
|
|
1082
1463
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1083
1464
|
const choice = response.choices?.[0];
|
|
1084
1465
|
const message = choice?.message;
|