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