@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
|
}
|
|
@@ -514,7 +660,7 @@ function formatMessagesForGoogle(messages) {
|
|
|
514
660
|
return {
|
|
515
661
|
role: "tool",
|
|
516
662
|
tool_call_id: msg.toolCallId,
|
|
517
|
-
content: msg.content
|
|
663
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
518
664
|
};
|
|
519
665
|
default:
|
|
520
666
|
return msg;
|
|
@@ -522,6 +668,86 @@ function formatMessagesForGoogle(messages) {
|
|
|
522
668
|
});
|
|
523
669
|
}
|
|
524
670
|
|
|
671
|
+
// src/core/openai-responses.ts
|
|
672
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
673
|
+
return {
|
|
674
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
675
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
679
|
+
const api = transport.api ?? "auto";
|
|
680
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
681
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
682
|
+
}
|
|
683
|
+
if (api === "chat") {
|
|
684
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
685
|
+
throw new Error(
|
|
686
|
+
"Responses options and native tool search require the Responses API"
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
let officialEndpoint = !baseURL;
|
|
692
|
+
try {
|
|
693
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
694
|
+
} catch {
|
|
695
|
+
}
|
|
696
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
697
|
+
}
|
|
698
|
+
function responseMetadata(response) {
|
|
699
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
700
|
+
return {
|
|
701
|
+
openai: {
|
|
702
|
+
response: {
|
|
703
|
+
id: response?.id,
|
|
704
|
+
model: response?.model,
|
|
705
|
+
status: response?.status,
|
|
706
|
+
output,
|
|
707
|
+
usage: response?.usage,
|
|
708
|
+
store: response?.store,
|
|
709
|
+
refusals: output.flatMap(
|
|
710
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
711
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
712
|
+
).map((part) => part.refusal) : []
|
|
713
|
+
)
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
function getResponseReplay(metadata) {
|
|
719
|
+
const response = metadata?.openai?.response;
|
|
720
|
+
if (response === void 0) return void 0;
|
|
721
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
722
|
+
throw new Error(
|
|
723
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
return response.output;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/core/provider-error.ts
|
|
730
|
+
function providerErrorEvent(error, code) {
|
|
731
|
+
const details = error ?? {};
|
|
732
|
+
const event = {
|
|
733
|
+
type: "error",
|
|
734
|
+
message: error instanceof Error ? error.message : String(error),
|
|
735
|
+
code,
|
|
736
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
737
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
738
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
739
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
740
|
+
};
|
|
741
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
742
|
+
return event;
|
|
743
|
+
}
|
|
744
|
+
function throwIfAborted(signal) {
|
|
745
|
+
if (!signal?.aborted) return;
|
|
746
|
+
const error = new Error("The operation was aborted");
|
|
747
|
+
error.name = "AbortError";
|
|
748
|
+
throw error;
|
|
749
|
+
}
|
|
750
|
+
|
|
525
751
|
// src/core/utils.ts
|
|
526
752
|
function generateId(prefix = "id") {
|
|
527
753
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -553,39 +779,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
553
779
|
const { default: OpenAI } = await import('openai');
|
|
554
780
|
this.client = new OpenAI({
|
|
555
781
|
apiKey: this.config.apiKey,
|
|
556
|
-
baseURL: this.config.baseUrl
|
|
782
|
+
baseURL: this.config.baseUrl,
|
|
783
|
+
organization: this.config.organization,
|
|
784
|
+
defaultHeaders: this.config.headers
|
|
557
785
|
});
|
|
558
786
|
}
|
|
559
787
|
return this.client;
|
|
560
788
|
}
|
|
789
|
+
parameterOptions(request) {
|
|
790
|
+
return {
|
|
791
|
+
...this.config,
|
|
792
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
toolDefinitions(request) {
|
|
796
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
797
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
798
|
+
name: tool.function.name,
|
|
799
|
+
description: tool.function.description ?? "",
|
|
800
|
+
location: "server",
|
|
801
|
+
inputSchema: tool.function.parameters
|
|
802
|
+
}));
|
|
803
|
+
}
|
|
561
804
|
shouldUseResponsesApi(request) {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
request.
|
|
805
|
+
return useOpenAIResponses(
|
|
806
|
+
request.config?.model || this.model,
|
|
807
|
+
this.config.baseUrl,
|
|
808
|
+
this.parameterOptions(request),
|
|
809
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
810
|
+
this.toolDefinitions(request).length > 0,
|
|
811
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
812
|
+
(message) => message.providerMetadata?.openai?.response
|
|
813
|
+
),
|
|
814
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
569
815
|
);
|
|
570
816
|
}
|
|
571
|
-
/**
|
|
572
|
-
* Whether this model rejects function tools on /v1/chat/completions.
|
|
573
|
-
*
|
|
574
|
-
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
575
|
-
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
576
|
-
* restriction and may not implement /v1/responses at all.
|
|
577
|
-
*/
|
|
578
|
-
requiresResponsesApiForTools(modelId) {
|
|
579
|
-
if (!modelId) return false;
|
|
580
|
-
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
581
|
-
const baseUrl = this.config.baseUrl;
|
|
582
|
-
if (!baseUrl) return true;
|
|
583
|
-
try {
|
|
584
|
-
return new URL(baseUrl).hostname === "api.openai.com";
|
|
585
|
-
} catch {
|
|
586
|
-
return false;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
817
|
/**
|
|
590
818
|
* Convert one message's content to Responses content parts.
|
|
591
819
|
*
|
|
@@ -616,11 +844,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
616
844
|
});
|
|
617
845
|
}
|
|
618
846
|
buildResponsesInput(request) {
|
|
619
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
847
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
620
848
|
const input = [];
|
|
621
849
|
for (const message of sourceMessages) {
|
|
622
850
|
if (message.role === "system") {
|
|
623
|
-
if (
|
|
851
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
624
852
|
input.push({
|
|
625
853
|
type: "message",
|
|
626
854
|
role: "developer",
|
|
@@ -630,6 +858,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
630
858
|
continue;
|
|
631
859
|
}
|
|
632
860
|
if (message.role === "assistant") {
|
|
861
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
862
|
+
if (replay) {
|
|
863
|
+
input.push(...replay);
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
633
866
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
634
867
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
635
868
|
input.push({
|
|
@@ -653,7 +886,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
653
886
|
input.push({
|
|
654
887
|
type: "function_call_output",
|
|
655
888
|
call_id: message.tool_call_id,
|
|
656
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
889
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
657
890
|
});
|
|
658
891
|
continue;
|
|
659
892
|
}
|
|
@@ -666,6 +899,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
666
899
|
}
|
|
667
900
|
return input;
|
|
668
901
|
}
|
|
902
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
903
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
904
|
+
return messages.map((message) => {
|
|
905
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
906
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
907
|
+
if (preserveProviderMetadata && modelOutput)
|
|
908
|
+
rest.modelOutput = modelOutput;
|
|
909
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
910
|
+
content = null;
|
|
911
|
+
}
|
|
912
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
913
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
914
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
915
|
+
).filter((image) => image !== null);
|
|
916
|
+
if (images.length > 0) {
|
|
917
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
918
|
+
content = [...parts, ...images];
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return {
|
|
922
|
+
...rest,
|
|
923
|
+
content,
|
|
924
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
925
|
+
};
|
|
926
|
+
});
|
|
927
|
+
}
|
|
669
928
|
/**
|
|
670
929
|
* Build the web_search tool for the Responses API, or undefined when web
|
|
671
930
|
* search is off.
|
|
@@ -744,20 +1003,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
744
1003
|
).map((part) => part.text) : []
|
|
745
1004
|
).join("") : "";
|
|
746
1005
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
name
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1006
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
1007
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
1008
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
1009
|
+
throw new Error("Responses contains an incomplete function call");
|
|
1010
|
+
}
|
|
1011
|
+
if (seenCallIds.has(item.call_id))
|
|
1012
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
1013
|
+
seenCallIds.add(item.call_id);
|
|
1014
|
+
const args = JSON.parse(item.arguments);
|
|
1015
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
1016
|
+
throw new Error(
|
|
1017
|
+
"Responses function arguments must be a JSON object"
|
|
1018
|
+
);
|
|
1019
|
+
return { id: item.call_id, name: item.name, args };
|
|
1020
|
+
}) : [];
|
|
758
1021
|
return {
|
|
759
1022
|
content,
|
|
760
1023
|
toolCalls,
|
|
1024
|
+
providerMetadata: responseMetadata(response),
|
|
761
1025
|
usage: response?.usage ? {
|
|
762
1026
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
763
1027
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -768,6 +1032,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
768
1032
|
}
|
|
769
1033
|
buildResponsesPayload(request, stream) {
|
|
770
1034
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1035
|
+
const options = resolveOpenAITransport(
|
|
1036
|
+
this.config,
|
|
1037
|
+
request.config?.openai
|
|
1038
|
+
).responses;
|
|
1039
|
+
const parameters = buildOpenAIParameters(
|
|
1040
|
+
request.config?.model || this.model,
|
|
1041
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1042
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1043
|
+
this.parameterOptions(request),
|
|
1044
|
+
"responses"
|
|
1045
|
+
);
|
|
771
1046
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
772
1047
|
request.config?.responseFormat
|
|
773
1048
|
);
|
|
@@ -777,18 +1052,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
777
1052
|
instructions: request.systemPrompt,
|
|
778
1053
|
input: this.buildResponsesInput(request),
|
|
779
1054
|
tools: this.buildResponsesTools(
|
|
780
|
-
|
|
1055
|
+
this.toolDefinitions(request),
|
|
781
1056
|
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
782
1057
|
this.buildResponsesWebSearchTool(request)
|
|
783
1058
|
),
|
|
784
|
-
tool_choice: openaiToolOptions?.toolChoice
|
|
1059
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
785
1060
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1061
|
+
...parameters,
|
|
1062
|
+
...options?.reasoningSummary ? {
|
|
1063
|
+
reasoning: {
|
|
1064
|
+
...parameters.reasoning,
|
|
1065
|
+
summary: options.reasoningSummary
|
|
1066
|
+
}
|
|
1067
|
+
} : {},
|
|
1068
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1069
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1070
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1071
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1072
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1073
|
+
...options?.store === false || options?.include ? {
|
|
1074
|
+
include: [
|
|
1075
|
+
.../* @__PURE__ */ new Set([
|
|
1076
|
+
...options?.include ?? [],
|
|
1077
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1078
|
+
])
|
|
1079
|
+
]
|
|
1080
|
+
} : {},
|
|
792
1081
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
793
1082
|
stream
|
|
794
1083
|
};
|
|
@@ -796,10 +1085,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
796
1085
|
}
|
|
797
1086
|
async completeWithResponses(request) {
|
|
798
1087
|
const client = await this.getClient();
|
|
1088
|
+
throwIfAborted(request.signal);
|
|
799
1089
|
const payload = this.buildResponsesPayload(request, false);
|
|
800
1090
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
801
|
-
|
|
1091
|
+
if (!client.responses?.create)
|
|
1092
|
+
throw new Error(
|
|
1093
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1094
|
+
);
|
|
1095
|
+
const response = await client.responses.create(payload, {
|
|
1096
|
+
signal: request.signal
|
|
1097
|
+
});
|
|
1098
|
+
throwIfAborted(request.signal);
|
|
802
1099
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1100
|
+
if (response.status && response.status !== "completed") {
|
|
1101
|
+
throw Object.assign(
|
|
1102
|
+
new Error(
|
|
1103
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
1104
|
+
),
|
|
1105
|
+
response.error
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
803
1108
|
return this.parseResponsesResult(response);
|
|
804
1109
|
}
|
|
805
1110
|
/**
|
|
@@ -817,13 +1122,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
817
1122
|
*/
|
|
818
1123
|
async *streamWithResponses(request) {
|
|
819
1124
|
const client = await this.getClient();
|
|
1125
|
+
throwIfAborted(request.signal);
|
|
820
1126
|
const payload = this.buildResponsesPayload(request, true);
|
|
821
1127
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
822
|
-
|
|
1128
|
+
if (!client.responses?.create)
|
|
1129
|
+
throw new Error(
|
|
1130
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1131
|
+
);
|
|
1132
|
+
const stream = await client.responses.create(payload, {
|
|
1133
|
+
signal: request.signal
|
|
1134
|
+
});
|
|
823
1135
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
824
1136
|
let usage;
|
|
825
1137
|
const citations = [];
|
|
1138
|
+
let completed = false;
|
|
1139
|
+
let terminalResponse;
|
|
1140
|
+
let responseIdentity = {};
|
|
1141
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1142
|
+
let streamedText = "";
|
|
826
1143
|
function* flushToolCalls() {
|
|
1144
|
+
for (const call of toolCalls.values()) {
|
|
1145
|
+
if (!call.name)
|
|
1146
|
+
throw new Error("Responses tool call is missing its name");
|
|
1147
|
+
JSON.parse(call.arguments || "{}");
|
|
1148
|
+
}
|
|
827
1149
|
for (const call of toolCalls.values()) {
|
|
828
1150
|
yield { type: "action:start", id: call.id, name: call.name };
|
|
829
1151
|
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
@@ -832,19 +1154,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
832
1154
|
toolCalls.clear();
|
|
833
1155
|
}
|
|
834
1156
|
for await (const event of stream) {
|
|
835
|
-
|
|
1157
|
+
throwIfAborted(request.signal);
|
|
836
1158
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1159
|
+
if (completed)
|
|
1160
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
837
1161
|
const index = event?.output_index ?? 0;
|
|
838
1162
|
switch (event?.type) {
|
|
1163
|
+
case "response.created":
|
|
1164
|
+
responseIdentity = {
|
|
1165
|
+
id: event.response?.id,
|
|
1166
|
+
model: event.response?.model
|
|
1167
|
+
};
|
|
1168
|
+
break;
|
|
1169
|
+
case "response.output_item.done":
|
|
1170
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1171
|
+
if (event.item?.type === "function_call") {
|
|
1172
|
+
toolCalls.set(index, {
|
|
1173
|
+
id: event.item.call_id,
|
|
1174
|
+
name: event.item.name,
|
|
1175
|
+
arguments: event.item.arguments
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
break;
|
|
839
1179
|
case "response.output_text.delta":
|
|
840
1180
|
if (event.delta) {
|
|
1181
|
+
streamedText += event.delta;
|
|
841
1182
|
yield { type: "message:delta", content: event.delta };
|
|
842
1183
|
}
|
|
843
1184
|
break;
|
|
844
1185
|
case "response.output_item.added":
|
|
845
1186
|
if (event.item?.type === "function_call") {
|
|
846
1187
|
toolCalls.set(index, {
|
|
847
|
-
id: event.item.call_id ??
|
|
1188
|
+
id: event.item.call_id ?? "",
|
|
848
1189
|
name: event.item.name ?? "",
|
|
849
1190
|
arguments: event.item.arguments ?? ""
|
|
850
1191
|
});
|
|
@@ -862,8 +1203,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
862
1203
|
}
|
|
863
1204
|
break;
|
|
864
1205
|
}
|
|
865
|
-
case "response.completed":
|
|
866
|
-
|
|
1206
|
+
case "response.completed": {
|
|
1207
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1208
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
1209
|
+
completed = true;
|
|
1210
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
867
1211
|
const found = this.collectResponsesCitations(
|
|
868
1212
|
event.response?.output,
|
|
869
1213
|
citations.length
|
|
@@ -879,21 +1223,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
879
1223
|
}
|
|
880
1224
|
break;
|
|
881
1225
|
}
|
|
882
|
-
case "
|
|
883
|
-
case "response.failed":
|
|
1226
|
+
case "response.incomplete":
|
|
884
1227
|
throw new Error(
|
|
885
|
-
|
|
1228
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
1229
|
+
);
|
|
1230
|
+
case "error":
|
|
1231
|
+
case "response.failed": {
|
|
1232
|
+
const details = event.response?.error ?? event;
|
|
1233
|
+
throw Object.assign(
|
|
1234
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
1235
|
+
{
|
|
1236
|
+
code: details.code,
|
|
1237
|
+
param: details.param,
|
|
1238
|
+
status: details.status
|
|
1239
|
+
}
|
|
886
1240
|
);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
throwIfAborted(request.signal);
|
|
1245
|
+
if (!completed)
|
|
1246
|
+
throw new Error("Responses stream ended without a completed response");
|
|
1247
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1248
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1249
|
+
toolCalls.clear();
|
|
1250
|
+
for (const [index, item] of output.entries()) {
|
|
1251
|
+
if (item.type === "function_call") {
|
|
1252
|
+
toolCalls.set(index, {
|
|
1253
|
+
id: item.call_id,
|
|
1254
|
+
name: item.name,
|
|
1255
|
+
arguments: item.arguments
|
|
1256
|
+
});
|
|
887
1257
|
}
|
|
888
1258
|
}
|
|
1259
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1260
|
+
for (const call of toolCalls.values()) {
|
|
1261
|
+
if (!call.id || !call.name)
|
|
1262
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1263
|
+
uniqueCalls.set(call.id, call);
|
|
1264
|
+
}
|
|
1265
|
+
toolCalls.clear();
|
|
1266
|
+
[...uniqueCalls.values()].forEach(
|
|
1267
|
+
(call, index) => toolCalls.set(index, call)
|
|
1268
|
+
);
|
|
1269
|
+
if (!output.length && toolCalls.size) {
|
|
1270
|
+
for (const call of toolCalls.values())
|
|
1271
|
+
output.push({
|
|
1272
|
+
type: "function_call",
|
|
1273
|
+
call_id: call.id,
|
|
1274
|
+
name: call.name,
|
|
1275
|
+
arguments: call.arguments
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
if (!output.length && streamedText)
|
|
1279
|
+
output.push({
|
|
1280
|
+
type: "message",
|
|
1281
|
+
role: "assistant",
|
|
1282
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1283
|
+
});
|
|
1284
|
+
terminalResponse.output = output;
|
|
1285
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1286
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1287
|
+
yield {
|
|
1288
|
+
type: "message:delta",
|
|
1289
|
+
content: finalText.slice(streamedText.length)
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
889
1292
|
yield* flushToolCalls();
|
|
890
1293
|
if (citations.length > 0) {
|
|
891
1294
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
892
1295
|
}
|
|
893
|
-
yield {
|
|
894
|
-
|
|
1296
|
+
yield {
|
|
1297
|
+
type: "message:end",
|
|
1298
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1299
|
+
};
|
|
1300
|
+
yield {
|
|
1301
|
+
type: "done",
|
|
1302
|
+
usage,
|
|
1303
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1304
|
+
};
|
|
895
1305
|
}
|
|
896
1306
|
async *stream(request) {
|
|
1307
|
+
throwIfAborted(request.signal);
|
|
897
1308
|
if (this.shouldUseResponsesApi(request)) {
|
|
898
1309
|
const messageId2 = generateMessageId();
|
|
899
1310
|
yield { type: "message:start", id: messageId2 };
|
|
@@ -901,47 +1312,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
901
1312
|
yield* this.streamWithResponses(request);
|
|
902
1313
|
return;
|
|
903
1314
|
} catch (error) {
|
|
904
|
-
yield
|
|
905
|
-
type: "error",
|
|
906
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
907
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
908
|
-
};
|
|
1315
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
909
1316
|
return;
|
|
910
1317
|
}
|
|
911
1318
|
}
|
|
912
1319
|
const client = await this.getClient();
|
|
913
1320
|
let messages;
|
|
914
1321
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
915
|
-
const processedMessages = request.rawMessages
|
|
916
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
917
|
-
return { ...msg, content: null };
|
|
918
|
-
}
|
|
919
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
920
|
-
if (hasAttachments) {
|
|
921
|
-
const content = [];
|
|
922
|
-
if (msg.content) {
|
|
923
|
-
content.push({ type: "text", text: msg.content });
|
|
924
|
-
}
|
|
925
|
-
for (const attachment of msg.attachments) {
|
|
926
|
-
if (attachment.type === "image") {
|
|
927
|
-
let imageUrl;
|
|
928
|
-
if (attachment.url) {
|
|
929
|
-
imageUrl = attachment.url;
|
|
930
|
-
} else if (attachment.data) {
|
|
931
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
932
|
-
} else {
|
|
933
|
-
continue;
|
|
934
|
-
}
|
|
935
|
-
content.push({
|
|
936
|
-
type: "image_url",
|
|
937
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
938
|
-
});
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
return { ...msg, content, attachments: void 0 };
|
|
942
|
-
}
|
|
943
|
-
return msg;
|
|
944
|
-
});
|
|
1322
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
945
1323
|
if (request.systemPrompt) {
|
|
946
1324
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
947
1325
|
if (!hasSystem) {
|
|
@@ -961,7 +1339,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
961
1339
|
request.systemPrompt
|
|
962
1340
|
);
|
|
963
1341
|
}
|
|
964
|
-
const tools = request.
|
|
1342
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1343
|
+
type: "function",
|
|
1344
|
+
function: {
|
|
1345
|
+
name: tool.name,
|
|
1346
|
+
description: tool.description,
|
|
1347
|
+
parameters: normalizeObjectJsonSchema(
|
|
1348
|
+
tool.inputSchema
|
|
1349
|
+
)
|
|
1350
|
+
}
|
|
1351
|
+
})) : formatTools(request.actions ?? []);
|
|
965
1352
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
966
1353
|
if (webSearchConfig) {
|
|
967
1354
|
const webSearchTool = {
|
|
@@ -990,26 +1377,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
990
1377
|
tools: tools.length > 0 ? tools : void 0,
|
|
991
1378
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
992
1379
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
993
|
-
...
|
|
1380
|
+
...buildOpenAIParameters(
|
|
994
1381
|
modelIdForPayload,
|
|
995
1382
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
996
|
-
request.config?.temperature ?? this.config.temperature
|
|
1383
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1384
|
+
this.parameterOptions(request)
|
|
997
1385
|
),
|
|
998
1386
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
999
1387
|
stream: true,
|
|
1000
1388
|
stream_options: { include_usage: true }
|
|
1001
1389
|
};
|
|
1002
1390
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1003
|
-
const stream = await client.chat.completions.create(payload
|
|
1004
|
-
|
|
1391
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1392
|
+
signal: request.signal
|
|
1393
|
+
});
|
|
1394
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1395
|
+
let finishReason;
|
|
1005
1396
|
const collectedCitations = [];
|
|
1006
1397
|
let citationIndex = 0;
|
|
1007
1398
|
let usage;
|
|
1008
1399
|
for await (const chunk of stream) {
|
|
1009
1400
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
1010
|
-
|
|
1011
|
-
break;
|
|
1012
|
-
}
|
|
1401
|
+
throwIfAborted(request.signal);
|
|
1013
1402
|
const delta = chunk.choices[0]?.delta;
|
|
1014
1403
|
const choice = chunk.choices[0];
|
|
1015
1404
|
if (delta?.content) {
|
|
@@ -1032,38 +1421,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1032
1421
|
}
|
|
1033
1422
|
}
|
|
1034
1423
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
const tcExtraContent = toolCall.extra_content;
|
|
1051
|
-
currentToolCall = {
|
|
1052
|
-
id: toolCall.id,
|
|
1053
|
-
name: toolCall.function?.name || "",
|
|
1054
|
-
arguments: toolCall.function?.arguments || "",
|
|
1055
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
1056
|
-
};
|
|
1057
|
-
yield {
|
|
1058
|
-
type: "action:start",
|
|
1059
|
-
id: currentToolCall.id,
|
|
1060
|
-
name: currentToolCall.name,
|
|
1061
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
1062
|
-
};
|
|
1063
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
1064
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1424
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1425
|
+
const index = part.index ?? 0;
|
|
1426
|
+
const call = toolCalls.get(index) ?? {
|
|
1427
|
+
id: "",
|
|
1428
|
+
name: "",
|
|
1429
|
+
arguments: ""
|
|
1430
|
+
};
|
|
1431
|
+
if (part.id) call.id = part.id;
|
|
1432
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1433
|
+
if (part.function?.arguments)
|
|
1434
|
+
call.arguments += part.function.arguments;
|
|
1435
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1436
|
+
toolCalls.set(index, call);
|
|
1067
1437
|
}
|
|
1068
1438
|
if (chunk.usage) {
|
|
1069
1439
|
usage = {
|
|
@@ -1072,21 +1442,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1072
1442
|
total_tokens: chunk.usage.total_tokens
|
|
1073
1443
|
};
|
|
1074
1444
|
}
|
|
1075
|
-
if (choice?.finish_reason)
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1445
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1446
|
+
}
|
|
1447
|
+
throwIfAborted(request.signal);
|
|
1448
|
+
if (!finishReason)
|
|
1449
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1450
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1451
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1452
|
+
}
|
|
1453
|
+
for (const call of toolCalls.values()) {
|
|
1454
|
+
if (!call.id || !call.name)
|
|
1455
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1456
|
+
JSON.parse(call.arguments || "{}");
|
|
1457
|
+
}
|
|
1458
|
+
for (const call of toolCalls.values()) {
|
|
1459
|
+
yield {
|
|
1460
|
+
type: "action:start",
|
|
1461
|
+
id: call.id,
|
|
1462
|
+
name: call.name,
|
|
1463
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1464
|
+
};
|
|
1465
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1466
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
1090
1467
|
}
|
|
1091
1468
|
if (collectedCitations.length > 0) {
|
|
1092
1469
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -1095,26 +1472,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1095
1472
|
yield { type: "message:end" };
|
|
1096
1473
|
yield { type: "done", usage };
|
|
1097
1474
|
} catch (error) {
|
|
1098
|
-
yield {
|
|
1099
|
-
type: "error",
|
|
1100
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
1101
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
1102
|
-
};
|
|
1475
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
1103
1476
|
}
|
|
1104
1477
|
}
|
|
1105
1478
|
async complete(request) {
|
|
1479
|
+
throwIfAborted(request.signal);
|
|
1106
1480
|
if (this.shouldUseResponsesApi(request)) {
|
|
1107
1481
|
return this.completeWithResponses(request);
|
|
1108
1482
|
}
|
|
1109
1483
|
const client = await this.getClient();
|
|
1110
1484
|
let messages;
|
|
1111
1485
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1112
|
-
const sanitized = request.rawMessages
|
|
1113
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
1114
|
-
return { ...msg, content: null };
|
|
1115
|
-
}
|
|
1116
|
-
return msg;
|
|
1117
|
-
});
|
|
1486
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
1118
1487
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
1119
1488
|
messages = [
|
|
1120
1489
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -1129,7 +1498,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1129
1498
|
request.systemPrompt
|
|
1130
1499
|
);
|
|
1131
1500
|
}
|
|
1132
|
-
const tools = request.
|
|
1501
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1502
|
+
type: "function",
|
|
1503
|
+
function: {
|
|
1504
|
+
name: tool.name,
|
|
1505
|
+
description: tool.description,
|
|
1506
|
+
parameters: normalizeObjectJsonSchema(
|
|
1507
|
+
tool.inputSchema
|
|
1508
|
+
)
|
|
1509
|
+
}
|
|
1510
|
+
})) : formatTools(request.actions ?? []);
|
|
1133
1511
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1134
1512
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
1135
1513
|
type: "function",
|
|
@@ -1144,16 +1522,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1144
1522
|
tools: tools.length > 0 ? tools : void 0,
|
|
1145
1523
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
1146
1524
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
1147
|
-
...
|
|
1525
|
+
...buildOpenAIParameters(
|
|
1148
1526
|
modelIdForCompletePayload,
|
|
1149
1527
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1150
|
-
request.config?.temperature ?? this.config.temperature
|
|
1528
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1529
|
+
this.parameterOptions(request)
|
|
1151
1530
|
),
|
|
1152
1531
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
1153
1532
|
stream: false
|
|
1154
1533
|
};
|
|
1155
1534
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1156
|
-
const response = await client.chat.completions.create(payload
|
|
1535
|
+
const response = await client.chat.completions.create(payload, {
|
|
1536
|
+
signal: request.signal
|
|
1537
|
+
});
|
|
1157
1538
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1158
1539
|
const choice = response.choices?.[0];
|
|
1159
1540
|
const message = choice?.message;
|