@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
|
}
|
|
@@ -463,7 +609,7 @@ function formatMessagesForXAI(messages) {
|
|
|
463
609
|
return {
|
|
464
610
|
role: "tool",
|
|
465
611
|
tool_call_id: msg.toolCallId,
|
|
466
|
-
content: msg.content
|
|
612
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
467
613
|
};
|
|
468
614
|
default:
|
|
469
615
|
return msg;
|
|
@@ -471,6 +617,86 @@ function formatMessagesForXAI(messages) {
|
|
|
471
617
|
});
|
|
472
618
|
}
|
|
473
619
|
|
|
620
|
+
// src/core/openai-responses.ts
|
|
621
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
622
|
+
return {
|
|
623
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
624
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
628
|
+
const api = transport.api ?? "auto";
|
|
629
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
630
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
631
|
+
}
|
|
632
|
+
if (api === "chat") {
|
|
633
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
634
|
+
throw new Error(
|
|
635
|
+
"Responses options and native tool search require the Responses API"
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
let officialEndpoint = !baseURL;
|
|
641
|
+
try {
|
|
642
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
643
|
+
} catch {
|
|
644
|
+
}
|
|
645
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
646
|
+
}
|
|
647
|
+
function responseMetadata(response) {
|
|
648
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
649
|
+
return {
|
|
650
|
+
openai: {
|
|
651
|
+
response: {
|
|
652
|
+
id: response?.id,
|
|
653
|
+
model: response?.model,
|
|
654
|
+
status: response?.status,
|
|
655
|
+
output,
|
|
656
|
+
usage: response?.usage,
|
|
657
|
+
store: response?.store,
|
|
658
|
+
refusals: output.flatMap(
|
|
659
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
660
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
661
|
+
).map((part) => part.refusal) : []
|
|
662
|
+
)
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function getResponseReplay(metadata) {
|
|
668
|
+
const response = metadata?.openai?.response;
|
|
669
|
+
if (response === void 0) return void 0;
|
|
670
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
671
|
+
throw new Error(
|
|
672
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
return response.output;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// src/core/provider-error.ts
|
|
679
|
+
function providerErrorEvent(error, code) {
|
|
680
|
+
const details = error ?? {};
|
|
681
|
+
const event = {
|
|
682
|
+
type: "error",
|
|
683
|
+
message: error instanceof Error ? error.message : String(error),
|
|
684
|
+
code,
|
|
685
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
686
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
687
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
688
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
689
|
+
};
|
|
690
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
691
|
+
return event;
|
|
692
|
+
}
|
|
693
|
+
function throwIfAborted(signal) {
|
|
694
|
+
if (!signal?.aborted) return;
|
|
695
|
+
const error = new Error("The operation was aborted");
|
|
696
|
+
error.name = "AbortError";
|
|
697
|
+
throw error;
|
|
698
|
+
}
|
|
699
|
+
|
|
474
700
|
// src/core/utils.ts
|
|
475
701
|
function generateId(prefix = "id") {
|
|
476
702
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -502,39 +728,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
502
728
|
const { default: OpenAI } = await import('openai');
|
|
503
729
|
this.client = new OpenAI({
|
|
504
730
|
apiKey: this.config.apiKey,
|
|
505
|
-
baseURL: this.config.baseUrl
|
|
731
|
+
baseURL: this.config.baseUrl,
|
|
732
|
+
organization: this.config.organization,
|
|
733
|
+
defaultHeaders: this.config.headers
|
|
506
734
|
});
|
|
507
735
|
}
|
|
508
736
|
return this.client;
|
|
509
737
|
}
|
|
738
|
+
parameterOptions(request) {
|
|
739
|
+
return {
|
|
740
|
+
...this.config,
|
|
741
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
toolDefinitions(request) {
|
|
745
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
746
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
747
|
+
name: tool.function.name,
|
|
748
|
+
description: tool.function.description ?? "",
|
|
749
|
+
location: "server",
|
|
750
|
+
inputSchema: tool.function.parameters
|
|
751
|
+
}));
|
|
752
|
+
}
|
|
510
753
|
shouldUseResponsesApi(request) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
request.
|
|
754
|
+
return useOpenAIResponses(
|
|
755
|
+
request.config?.model || this.model,
|
|
756
|
+
this.config.baseUrl,
|
|
757
|
+
this.parameterOptions(request),
|
|
758
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
759
|
+
this.toolDefinitions(request).length > 0,
|
|
760
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
761
|
+
(message) => message.providerMetadata?.openai?.response
|
|
762
|
+
),
|
|
763
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
518
764
|
);
|
|
519
765
|
}
|
|
520
|
-
/**
|
|
521
|
-
* Whether this model rejects function tools on /v1/chat/completions.
|
|
522
|
-
*
|
|
523
|
-
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
524
|
-
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
525
|
-
* restriction and may not implement /v1/responses at all.
|
|
526
|
-
*/
|
|
527
|
-
requiresResponsesApiForTools(modelId) {
|
|
528
|
-
if (!modelId) return false;
|
|
529
|
-
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
530
|
-
const baseUrl = this.config.baseUrl;
|
|
531
|
-
if (!baseUrl) return true;
|
|
532
|
-
try {
|
|
533
|
-
return new URL(baseUrl).hostname === "api.openai.com";
|
|
534
|
-
} catch {
|
|
535
|
-
return false;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
766
|
/**
|
|
539
767
|
* Convert one message's content to Responses content parts.
|
|
540
768
|
*
|
|
@@ -565,11 +793,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
565
793
|
});
|
|
566
794
|
}
|
|
567
795
|
buildResponsesInput(request) {
|
|
568
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
796
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
569
797
|
const input = [];
|
|
570
798
|
for (const message of sourceMessages) {
|
|
571
799
|
if (message.role === "system") {
|
|
572
|
-
if (
|
|
800
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
573
801
|
input.push({
|
|
574
802
|
type: "message",
|
|
575
803
|
role: "developer",
|
|
@@ -579,6 +807,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
579
807
|
continue;
|
|
580
808
|
}
|
|
581
809
|
if (message.role === "assistant") {
|
|
810
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
811
|
+
if (replay) {
|
|
812
|
+
input.push(...replay);
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
582
815
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
583
816
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
584
817
|
input.push({
|
|
@@ -602,7 +835,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
602
835
|
input.push({
|
|
603
836
|
type: "function_call_output",
|
|
604
837
|
call_id: message.tool_call_id,
|
|
605
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
838
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
606
839
|
});
|
|
607
840
|
continue;
|
|
608
841
|
}
|
|
@@ -615,6 +848,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
615
848
|
}
|
|
616
849
|
return input;
|
|
617
850
|
}
|
|
851
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
852
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
853
|
+
return messages.map((message) => {
|
|
854
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
855
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
856
|
+
if (preserveProviderMetadata && modelOutput)
|
|
857
|
+
rest.modelOutput = modelOutput;
|
|
858
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
859
|
+
content = null;
|
|
860
|
+
}
|
|
861
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
862
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
863
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
864
|
+
).filter((image) => image !== null);
|
|
865
|
+
if (images.length > 0) {
|
|
866
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
867
|
+
content = [...parts, ...images];
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
...rest,
|
|
872
|
+
content,
|
|
873
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
874
|
+
};
|
|
875
|
+
});
|
|
876
|
+
}
|
|
618
877
|
/**
|
|
619
878
|
* Build the web_search tool for the Responses API, or undefined when web
|
|
620
879
|
* search is off.
|
|
@@ -693,20 +952,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
693
952
|
).map((part) => part.text) : []
|
|
694
953
|
).join("") : "";
|
|
695
954
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
name
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
955
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
956
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
957
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
958
|
+
throw new Error("Responses contains an incomplete function call");
|
|
959
|
+
}
|
|
960
|
+
if (seenCallIds.has(item.call_id))
|
|
961
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
962
|
+
seenCallIds.add(item.call_id);
|
|
963
|
+
const args = JSON.parse(item.arguments);
|
|
964
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
965
|
+
throw new Error(
|
|
966
|
+
"Responses function arguments must be a JSON object"
|
|
967
|
+
);
|
|
968
|
+
return { id: item.call_id, name: item.name, args };
|
|
969
|
+
}) : [];
|
|
707
970
|
return {
|
|
708
971
|
content,
|
|
709
972
|
toolCalls,
|
|
973
|
+
providerMetadata: responseMetadata(response),
|
|
710
974
|
usage: response?.usage ? {
|
|
711
975
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
712
976
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -717,6 +981,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
717
981
|
}
|
|
718
982
|
buildResponsesPayload(request, stream) {
|
|
719
983
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
984
|
+
const options = resolveOpenAITransport(
|
|
985
|
+
this.config,
|
|
986
|
+
request.config?.openai
|
|
987
|
+
).responses;
|
|
988
|
+
const parameters = buildOpenAIParameters(
|
|
989
|
+
request.config?.model || this.model,
|
|
990
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
991
|
+
request.config?.temperature ?? this.config.temperature,
|
|
992
|
+
this.parameterOptions(request),
|
|
993
|
+
"responses"
|
|
994
|
+
);
|
|
720
995
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
721
996
|
request.config?.responseFormat
|
|
722
997
|
);
|
|
@@ -726,18 +1001,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
726
1001
|
instructions: request.systemPrompt,
|
|
727
1002
|
input: this.buildResponsesInput(request),
|
|
728
1003
|
tools: this.buildResponsesTools(
|
|
729
|
-
|
|
1004
|
+
this.toolDefinitions(request),
|
|
730
1005
|
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
731
1006
|
this.buildResponsesWebSearchTool(request)
|
|
732
1007
|
),
|
|
733
|
-
tool_choice: openaiToolOptions?.toolChoice
|
|
1008
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
734
1009
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
1010
|
+
...parameters,
|
|
1011
|
+
...options?.reasoningSummary ? {
|
|
1012
|
+
reasoning: {
|
|
1013
|
+
...parameters.reasoning,
|
|
1014
|
+
summary: options.reasoningSummary
|
|
1015
|
+
}
|
|
1016
|
+
} : {},
|
|
1017
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1018
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1019
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1020
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1021
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1022
|
+
...options?.store === false || options?.include ? {
|
|
1023
|
+
include: [
|
|
1024
|
+
.../* @__PURE__ */ new Set([
|
|
1025
|
+
...options?.include ?? [],
|
|
1026
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1027
|
+
])
|
|
1028
|
+
]
|
|
1029
|
+
} : {},
|
|
741
1030
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
742
1031
|
stream
|
|
743
1032
|
};
|
|
@@ -745,10 +1034,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
745
1034
|
}
|
|
746
1035
|
async completeWithResponses(request) {
|
|
747
1036
|
const client = await this.getClient();
|
|
1037
|
+
throwIfAborted(request.signal);
|
|
748
1038
|
const payload = this.buildResponsesPayload(request, false);
|
|
749
1039
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
750
|
-
|
|
1040
|
+
if (!client.responses?.create)
|
|
1041
|
+
throw new Error(
|
|
1042
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1043
|
+
);
|
|
1044
|
+
const response = await client.responses.create(payload, {
|
|
1045
|
+
signal: request.signal
|
|
1046
|
+
});
|
|
1047
|
+
throwIfAborted(request.signal);
|
|
751
1048
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1049
|
+
if (response.status && response.status !== "completed") {
|
|
1050
|
+
throw Object.assign(
|
|
1051
|
+
new Error(
|
|
1052
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
1053
|
+
),
|
|
1054
|
+
response.error
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
752
1057
|
return this.parseResponsesResult(response);
|
|
753
1058
|
}
|
|
754
1059
|
/**
|
|
@@ -766,13 +1071,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
766
1071
|
*/
|
|
767
1072
|
async *streamWithResponses(request) {
|
|
768
1073
|
const client = await this.getClient();
|
|
1074
|
+
throwIfAborted(request.signal);
|
|
769
1075
|
const payload = this.buildResponsesPayload(request, true);
|
|
770
1076
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
771
|
-
|
|
1077
|
+
if (!client.responses?.create)
|
|
1078
|
+
throw new Error(
|
|
1079
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1080
|
+
);
|
|
1081
|
+
const stream = await client.responses.create(payload, {
|
|
1082
|
+
signal: request.signal
|
|
1083
|
+
});
|
|
772
1084
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
773
1085
|
let usage;
|
|
774
1086
|
const citations = [];
|
|
1087
|
+
let completed = false;
|
|
1088
|
+
let terminalResponse;
|
|
1089
|
+
let responseIdentity = {};
|
|
1090
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1091
|
+
let streamedText = "";
|
|
775
1092
|
function* flushToolCalls() {
|
|
1093
|
+
for (const call of toolCalls.values()) {
|
|
1094
|
+
if (!call.name)
|
|
1095
|
+
throw new Error("Responses tool call is missing its name");
|
|
1096
|
+
JSON.parse(call.arguments || "{}");
|
|
1097
|
+
}
|
|
776
1098
|
for (const call of toolCalls.values()) {
|
|
777
1099
|
yield { type: "action:start", id: call.id, name: call.name };
|
|
778
1100
|
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
@@ -781,19 +1103,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
781
1103
|
toolCalls.clear();
|
|
782
1104
|
}
|
|
783
1105
|
for await (const event of stream) {
|
|
784
|
-
|
|
1106
|
+
throwIfAborted(request.signal);
|
|
785
1107
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1108
|
+
if (completed)
|
|
1109
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
786
1110
|
const index = event?.output_index ?? 0;
|
|
787
1111
|
switch (event?.type) {
|
|
1112
|
+
case "response.created":
|
|
1113
|
+
responseIdentity = {
|
|
1114
|
+
id: event.response?.id,
|
|
1115
|
+
model: event.response?.model
|
|
1116
|
+
};
|
|
1117
|
+
break;
|
|
1118
|
+
case "response.output_item.done":
|
|
1119
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1120
|
+
if (event.item?.type === "function_call") {
|
|
1121
|
+
toolCalls.set(index, {
|
|
1122
|
+
id: event.item.call_id,
|
|
1123
|
+
name: event.item.name,
|
|
1124
|
+
arguments: event.item.arguments
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
break;
|
|
788
1128
|
case "response.output_text.delta":
|
|
789
1129
|
if (event.delta) {
|
|
1130
|
+
streamedText += event.delta;
|
|
790
1131
|
yield { type: "message:delta", content: event.delta };
|
|
791
1132
|
}
|
|
792
1133
|
break;
|
|
793
1134
|
case "response.output_item.added":
|
|
794
1135
|
if (event.item?.type === "function_call") {
|
|
795
1136
|
toolCalls.set(index, {
|
|
796
|
-
id: event.item.call_id ??
|
|
1137
|
+
id: event.item.call_id ?? "",
|
|
797
1138
|
name: event.item.name ?? "",
|
|
798
1139
|
arguments: event.item.arguments ?? ""
|
|
799
1140
|
});
|
|
@@ -811,8 +1152,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
811
1152
|
}
|
|
812
1153
|
break;
|
|
813
1154
|
}
|
|
814
|
-
case "response.completed":
|
|
815
|
-
|
|
1155
|
+
case "response.completed": {
|
|
1156
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1157
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
1158
|
+
completed = true;
|
|
1159
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
816
1160
|
const found = this.collectResponsesCitations(
|
|
817
1161
|
event.response?.output,
|
|
818
1162
|
citations.length
|
|
@@ -828,21 +1172,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
828
1172
|
}
|
|
829
1173
|
break;
|
|
830
1174
|
}
|
|
831
|
-
case "
|
|
832
|
-
case "response.failed":
|
|
1175
|
+
case "response.incomplete":
|
|
833
1176
|
throw new Error(
|
|
834
|
-
|
|
1177
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
1178
|
+
);
|
|
1179
|
+
case "error":
|
|
1180
|
+
case "response.failed": {
|
|
1181
|
+
const details = event.response?.error ?? event;
|
|
1182
|
+
throw Object.assign(
|
|
1183
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
1184
|
+
{
|
|
1185
|
+
code: details.code,
|
|
1186
|
+
param: details.param,
|
|
1187
|
+
status: details.status
|
|
1188
|
+
}
|
|
835
1189
|
);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
throwIfAborted(request.signal);
|
|
1194
|
+
if (!completed)
|
|
1195
|
+
throw new Error("Responses stream ended without a completed response");
|
|
1196
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1197
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1198
|
+
toolCalls.clear();
|
|
1199
|
+
for (const [index, item] of output.entries()) {
|
|
1200
|
+
if (item.type === "function_call") {
|
|
1201
|
+
toolCalls.set(index, {
|
|
1202
|
+
id: item.call_id,
|
|
1203
|
+
name: item.name,
|
|
1204
|
+
arguments: item.arguments
|
|
1205
|
+
});
|
|
836
1206
|
}
|
|
837
1207
|
}
|
|
1208
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1209
|
+
for (const call of toolCalls.values()) {
|
|
1210
|
+
if (!call.id || !call.name)
|
|
1211
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1212
|
+
uniqueCalls.set(call.id, call);
|
|
1213
|
+
}
|
|
1214
|
+
toolCalls.clear();
|
|
1215
|
+
[...uniqueCalls.values()].forEach(
|
|
1216
|
+
(call, index) => toolCalls.set(index, call)
|
|
1217
|
+
);
|
|
1218
|
+
if (!output.length && toolCalls.size) {
|
|
1219
|
+
for (const call of toolCalls.values())
|
|
1220
|
+
output.push({
|
|
1221
|
+
type: "function_call",
|
|
1222
|
+
call_id: call.id,
|
|
1223
|
+
name: call.name,
|
|
1224
|
+
arguments: call.arguments
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
if (!output.length && streamedText)
|
|
1228
|
+
output.push({
|
|
1229
|
+
type: "message",
|
|
1230
|
+
role: "assistant",
|
|
1231
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1232
|
+
});
|
|
1233
|
+
terminalResponse.output = output;
|
|
1234
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1235
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1236
|
+
yield {
|
|
1237
|
+
type: "message:delta",
|
|
1238
|
+
content: finalText.slice(streamedText.length)
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
838
1241
|
yield* flushToolCalls();
|
|
839
1242
|
if (citations.length > 0) {
|
|
840
1243
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
841
1244
|
}
|
|
842
|
-
yield {
|
|
843
|
-
|
|
1245
|
+
yield {
|
|
1246
|
+
type: "message:end",
|
|
1247
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1248
|
+
};
|
|
1249
|
+
yield {
|
|
1250
|
+
type: "done",
|
|
1251
|
+
usage,
|
|
1252
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1253
|
+
};
|
|
844
1254
|
}
|
|
845
1255
|
async *stream(request) {
|
|
1256
|
+
throwIfAborted(request.signal);
|
|
846
1257
|
if (this.shouldUseResponsesApi(request)) {
|
|
847
1258
|
const messageId2 = generateMessageId();
|
|
848
1259
|
yield { type: "message:start", id: messageId2 };
|
|
@@ -850,47 +1261,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
850
1261
|
yield* this.streamWithResponses(request);
|
|
851
1262
|
return;
|
|
852
1263
|
} catch (error) {
|
|
853
|
-
yield
|
|
854
|
-
type: "error",
|
|
855
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
856
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
857
|
-
};
|
|
1264
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
858
1265
|
return;
|
|
859
1266
|
}
|
|
860
1267
|
}
|
|
861
1268
|
const client = await this.getClient();
|
|
862
1269
|
let messages;
|
|
863
1270
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
864
|
-
const processedMessages = request.rawMessages
|
|
865
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
866
|
-
return { ...msg, content: null };
|
|
867
|
-
}
|
|
868
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
869
|
-
if (hasAttachments) {
|
|
870
|
-
const content = [];
|
|
871
|
-
if (msg.content) {
|
|
872
|
-
content.push({ type: "text", text: msg.content });
|
|
873
|
-
}
|
|
874
|
-
for (const attachment of msg.attachments) {
|
|
875
|
-
if (attachment.type === "image") {
|
|
876
|
-
let imageUrl;
|
|
877
|
-
if (attachment.url) {
|
|
878
|
-
imageUrl = attachment.url;
|
|
879
|
-
} else if (attachment.data) {
|
|
880
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
881
|
-
} else {
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
content.push({
|
|
885
|
-
type: "image_url",
|
|
886
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
return { ...msg, content, attachments: void 0 };
|
|
891
|
-
}
|
|
892
|
-
return msg;
|
|
893
|
-
});
|
|
1271
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
894
1272
|
if (request.systemPrompt) {
|
|
895
1273
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
896
1274
|
if (!hasSystem) {
|
|
@@ -910,7 +1288,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
910
1288
|
request.systemPrompt
|
|
911
1289
|
);
|
|
912
1290
|
}
|
|
913
|
-
const tools = request.
|
|
1291
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1292
|
+
type: "function",
|
|
1293
|
+
function: {
|
|
1294
|
+
name: tool.name,
|
|
1295
|
+
description: tool.description,
|
|
1296
|
+
parameters: normalizeObjectJsonSchema(
|
|
1297
|
+
tool.inputSchema
|
|
1298
|
+
)
|
|
1299
|
+
}
|
|
1300
|
+
})) : formatTools(request.actions ?? []);
|
|
914
1301
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
915
1302
|
if (webSearchConfig) {
|
|
916
1303
|
const webSearchTool = {
|
|
@@ -939,26 +1326,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
939
1326
|
tools: tools.length > 0 ? tools : void 0,
|
|
940
1327
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
941
1328
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
942
|
-
...
|
|
1329
|
+
...buildOpenAIParameters(
|
|
943
1330
|
modelIdForPayload,
|
|
944
1331
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
945
|
-
request.config?.temperature ?? this.config.temperature
|
|
1332
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1333
|
+
this.parameterOptions(request)
|
|
946
1334
|
),
|
|
947
1335
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
948
1336
|
stream: true,
|
|
949
1337
|
stream_options: { include_usage: true }
|
|
950
1338
|
};
|
|
951
1339
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
952
|
-
const stream = await client.chat.completions.create(payload
|
|
953
|
-
|
|
1340
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1341
|
+
signal: request.signal
|
|
1342
|
+
});
|
|
1343
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1344
|
+
let finishReason;
|
|
954
1345
|
const collectedCitations = [];
|
|
955
1346
|
let citationIndex = 0;
|
|
956
1347
|
let usage;
|
|
957
1348
|
for await (const chunk of stream) {
|
|
958
1349
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
959
|
-
|
|
960
|
-
break;
|
|
961
|
-
}
|
|
1350
|
+
throwIfAborted(request.signal);
|
|
962
1351
|
const delta = chunk.choices[0]?.delta;
|
|
963
1352
|
const choice = chunk.choices[0];
|
|
964
1353
|
if (delta?.content) {
|
|
@@ -981,38 +1370,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
981
1370
|
}
|
|
982
1371
|
}
|
|
983
1372
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
};
|
|
998
|
-
}
|
|
999
|
-
const tcExtraContent = toolCall.extra_content;
|
|
1000
|
-
currentToolCall = {
|
|
1001
|
-
id: toolCall.id,
|
|
1002
|
-
name: toolCall.function?.name || "",
|
|
1003
|
-
arguments: toolCall.function?.arguments || "",
|
|
1004
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
1005
|
-
};
|
|
1006
|
-
yield {
|
|
1007
|
-
type: "action:start",
|
|
1008
|
-
id: currentToolCall.id,
|
|
1009
|
-
name: currentToolCall.name,
|
|
1010
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
1011
|
-
};
|
|
1012
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
1013
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1373
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1374
|
+
const index = part.index ?? 0;
|
|
1375
|
+
const call = toolCalls.get(index) ?? {
|
|
1376
|
+
id: "",
|
|
1377
|
+
name: "",
|
|
1378
|
+
arguments: ""
|
|
1379
|
+
};
|
|
1380
|
+
if (part.id) call.id = part.id;
|
|
1381
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1382
|
+
if (part.function?.arguments)
|
|
1383
|
+
call.arguments += part.function.arguments;
|
|
1384
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1385
|
+
toolCalls.set(index, call);
|
|
1016
1386
|
}
|
|
1017
1387
|
if (chunk.usage) {
|
|
1018
1388
|
usage = {
|
|
@@ -1021,21 +1391,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1021
1391
|
total_tokens: chunk.usage.total_tokens
|
|
1022
1392
|
};
|
|
1023
1393
|
}
|
|
1024
|
-
if (choice?.finish_reason)
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1394
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1395
|
+
}
|
|
1396
|
+
throwIfAborted(request.signal);
|
|
1397
|
+
if (!finishReason)
|
|
1398
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1399
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1400
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1401
|
+
}
|
|
1402
|
+
for (const call of toolCalls.values()) {
|
|
1403
|
+
if (!call.id || !call.name)
|
|
1404
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1405
|
+
JSON.parse(call.arguments || "{}");
|
|
1406
|
+
}
|
|
1407
|
+
for (const call of toolCalls.values()) {
|
|
1408
|
+
yield {
|
|
1409
|
+
type: "action:start",
|
|
1410
|
+
id: call.id,
|
|
1411
|
+
name: call.name,
|
|
1412
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1413
|
+
};
|
|
1414
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1415
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
1039
1416
|
}
|
|
1040
1417
|
if (collectedCitations.length > 0) {
|
|
1041
1418
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -1044,26 +1421,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1044
1421
|
yield { type: "message:end" };
|
|
1045
1422
|
yield { type: "done", usage };
|
|
1046
1423
|
} catch (error) {
|
|
1047
|
-
yield {
|
|
1048
|
-
type: "error",
|
|
1049
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
1050
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
1051
|
-
};
|
|
1424
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
1052
1425
|
}
|
|
1053
1426
|
}
|
|
1054
1427
|
async complete(request) {
|
|
1428
|
+
throwIfAborted(request.signal);
|
|
1055
1429
|
if (this.shouldUseResponsesApi(request)) {
|
|
1056
1430
|
return this.completeWithResponses(request);
|
|
1057
1431
|
}
|
|
1058
1432
|
const client = await this.getClient();
|
|
1059
1433
|
let messages;
|
|
1060
1434
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1061
|
-
const sanitized = request.rawMessages
|
|
1062
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
1063
|
-
return { ...msg, content: null };
|
|
1064
|
-
}
|
|
1065
|
-
return msg;
|
|
1066
|
-
});
|
|
1435
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
1067
1436
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
1068
1437
|
messages = [
|
|
1069
1438
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -1078,7 +1447,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1078
1447
|
request.systemPrompt
|
|
1079
1448
|
);
|
|
1080
1449
|
}
|
|
1081
|
-
const tools = request.
|
|
1450
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1451
|
+
type: "function",
|
|
1452
|
+
function: {
|
|
1453
|
+
name: tool.name,
|
|
1454
|
+
description: tool.description,
|
|
1455
|
+
parameters: normalizeObjectJsonSchema(
|
|
1456
|
+
tool.inputSchema
|
|
1457
|
+
)
|
|
1458
|
+
}
|
|
1459
|
+
})) : formatTools(request.actions ?? []);
|
|
1082
1460
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1083
1461
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
1084
1462
|
type: "function",
|
|
@@ -1093,16 +1471,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1093
1471
|
tools: tools.length > 0 ? tools : void 0,
|
|
1094
1472
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
1095
1473
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
1096
|
-
...
|
|
1474
|
+
...buildOpenAIParameters(
|
|
1097
1475
|
modelIdForCompletePayload,
|
|
1098
1476
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1099
|
-
request.config?.temperature ?? this.config.temperature
|
|
1477
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1478
|
+
this.parameterOptions(request)
|
|
1100
1479
|
),
|
|
1101
1480
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
1102
1481
|
stream: false
|
|
1103
1482
|
};
|
|
1104
1483
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1105
|
-
const response = await client.chat.completions.create(payload
|
|
1484
|
+
const response = await client.chat.completions.create(payload, {
|
|
1485
|
+
signal: request.signal
|
|
1486
|
+
});
|
|
1106
1487
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1107
1488
|
const choice = response.choices?.[0];
|
|
1108
1489
|
const message = choice?.message;
|