@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
|
}
|
|
@@ -464,7 +610,7 @@ function formatMessagesForOpenRouter(messages) {
|
|
|
464
610
|
return {
|
|
465
611
|
role: "tool",
|
|
466
612
|
tool_call_id: msg.toolCallId,
|
|
467
|
-
content: msg.content
|
|
613
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
468
614
|
};
|
|
469
615
|
default:
|
|
470
616
|
return msg;
|
|
@@ -495,6 +641,86 @@ async function searchOpenRouterModels(query, apiKey) {
|
|
|
495
641
|
);
|
|
496
642
|
}
|
|
497
643
|
|
|
644
|
+
// src/core/openai-responses.ts
|
|
645
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
646
|
+
return {
|
|
647
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
648
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
652
|
+
const api = transport.api ?? "auto";
|
|
653
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
654
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
655
|
+
}
|
|
656
|
+
if (api === "chat") {
|
|
657
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
"Responses options and native tool search require the Responses API"
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
let officialEndpoint = !baseURL;
|
|
665
|
+
try {
|
|
666
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
667
|
+
} catch {
|
|
668
|
+
}
|
|
669
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
670
|
+
}
|
|
671
|
+
function responseMetadata(response) {
|
|
672
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
673
|
+
return {
|
|
674
|
+
openai: {
|
|
675
|
+
response: {
|
|
676
|
+
id: response?.id,
|
|
677
|
+
model: response?.model,
|
|
678
|
+
status: response?.status,
|
|
679
|
+
output,
|
|
680
|
+
usage: response?.usage,
|
|
681
|
+
store: response?.store,
|
|
682
|
+
refusals: output.flatMap(
|
|
683
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
684
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
685
|
+
).map((part) => part.refusal) : []
|
|
686
|
+
)
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function getResponseReplay(metadata) {
|
|
692
|
+
const response = metadata?.openai?.response;
|
|
693
|
+
if (response === void 0) return void 0;
|
|
694
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
return response.output;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/core/provider-error.ts
|
|
703
|
+
function providerErrorEvent(error, code) {
|
|
704
|
+
const details = error ?? {};
|
|
705
|
+
const event = {
|
|
706
|
+
type: "error",
|
|
707
|
+
message: error instanceof Error ? error.message : String(error),
|
|
708
|
+
code,
|
|
709
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
710
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
711
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
712
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
713
|
+
};
|
|
714
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
715
|
+
return event;
|
|
716
|
+
}
|
|
717
|
+
function throwIfAborted(signal) {
|
|
718
|
+
if (!signal?.aborted) return;
|
|
719
|
+
const error = new Error("The operation was aborted");
|
|
720
|
+
error.name = "AbortError";
|
|
721
|
+
throw error;
|
|
722
|
+
}
|
|
723
|
+
|
|
498
724
|
// src/core/utils.ts
|
|
499
725
|
function generateId(prefix = "id") {
|
|
500
726
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -526,39 +752,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
526
752
|
const { default: OpenAI } = await import('openai');
|
|
527
753
|
this.client = new OpenAI({
|
|
528
754
|
apiKey: this.config.apiKey,
|
|
529
|
-
baseURL: this.config.baseUrl
|
|
755
|
+
baseURL: this.config.baseUrl,
|
|
756
|
+
organization: this.config.organization,
|
|
757
|
+
defaultHeaders: this.config.headers
|
|
530
758
|
});
|
|
531
759
|
}
|
|
532
760
|
return this.client;
|
|
533
761
|
}
|
|
762
|
+
parameterOptions(request) {
|
|
763
|
+
return {
|
|
764
|
+
...this.config,
|
|
765
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
toolDefinitions(request) {
|
|
769
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
770
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
771
|
+
name: tool.function.name,
|
|
772
|
+
description: tool.function.description ?? "",
|
|
773
|
+
location: "server",
|
|
774
|
+
inputSchema: tool.function.parameters
|
|
775
|
+
}));
|
|
776
|
+
}
|
|
534
777
|
shouldUseResponsesApi(request) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
request.
|
|
778
|
+
return useOpenAIResponses(
|
|
779
|
+
request.config?.model || this.model,
|
|
780
|
+
this.config.baseUrl,
|
|
781
|
+
this.parameterOptions(request),
|
|
782
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
783
|
+
this.toolDefinitions(request).length > 0,
|
|
784
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
785
|
+
(message) => message.providerMetadata?.openai?.response
|
|
786
|
+
),
|
|
787
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
542
788
|
);
|
|
543
789
|
}
|
|
544
|
-
/**
|
|
545
|
-
* Whether this model rejects function tools on /v1/chat/completions.
|
|
546
|
-
*
|
|
547
|
-
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
548
|
-
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
549
|
-
* restriction and may not implement /v1/responses at all.
|
|
550
|
-
*/
|
|
551
|
-
requiresResponsesApiForTools(modelId) {
|
|
552
|
-
if (!modelId) return false;
|
|
553
|
-
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
554
|
-
const baseUrl = this.config.baseUrl;
|
|
555
|
-
if (!baseUrl) return true;
|
|
556
|
-
try {
|
|
557
|
-
return new URL(baseUrl).hostname === "api.openai.com";
|
|
558
|
-
} catch {
|
|
559
|
-
return false;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
790
|
/**
|
|
563
791
|
* Convert one message's content to Responses content parts.
|
|
564
792
|
*
|
|
@@ -589,11 +817,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
589
817
|
});
|
|
590
818
|
}
|
|
591
819
|
buildResponsesInput(request) {
|
|
592
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
820
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
593
821
|
const input = [];
|
|
594
822
|
for (const message of sourceMessages) {
|
|
595
823
|
if (message.role === "system") {
|
|
596
|
-
if (
|
|
824
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
597
825
|
input.push({
|
|
598
826
|
type: "message",
|
|
599
827
|
role: "developer",
|
|
@@ -603,6 +831,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
603
831
|
continue;
|
|
604
832
|
}
|
|
605
833
|
if (message.role === "assistant") {
|
|
834
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
835
|
+
if (replay) {
|
|
836
|
+
input.push(...replay);
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
606
839
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
607
840
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
608
841
|
input.push({
|
|
@@ -626,7 +859,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
626
859
|
input.push({
|
|
627
860
|
type: "function_call_output",
|
|
628
861
|
call_id: message.tool_call_id,
|
|
629
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
862
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
630
863
|
});
|
|
631
864
|
continue;
|
|
632
865
|
}
|
|
@@ -639,6 +872,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
639
872
|
}
|
|
640
873
|
return input;
|
|
641
874
|
}
|
|
875
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
876
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
877
|
+
return messages.map((message) => {
|
|
878
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
879
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
880
|
+
if (preserveProviderMetadata && modelOutput)
|
|
881
|
+
rest.modelOutput = modelOutput;
|
|
882
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
883
|
+
content = null;
|
|
884
|
+
}
|
|
885
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
886
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
887
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
888
|
+
).filter((image) => image !== null);
|
|
889
|
+
if (images.length > 0) {
|
|
890
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
891
|
+
content = [...parts, ...images];
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
...rest,
|
|
896
|
+
content,
|
|
897
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
898
|
+
};
|
|
899
|
+
});
|
|
900
|
+
}
|
|
642
901
|
/**
|
|
643
902
|
* Build the web_search tool for the Responses API, or undefined when web
|
|
644
903
|
* search is off.
|
|
@@ -717,20 +976,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
717
976
|
).map((part) => part.text) : []
|
|
718
977
|
).join("") : "";
|
|
719
978
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
name
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
979
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
980
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
981
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
982
|
+
throw new Error("Responses contains an incomplete function call");
|
|
983
|
+
}
|
|
984
|
+
if (seenCallIds.has(item.call_id))
|
|
985
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
986
|
+
seenCallIds.add(item.call_id);
|
|
987
|
+
const args = JSON.parse(item.arguments);
|
|
988
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
989
|
+
throw new Error(
|
|
990
|
+
"Responses function arguments must be a JSON object"
|
|
991
|
+
);
|
|
992
|
+
return { id: item.call_id, name: item.name, args };
|
|
993
|
+
}) : [];
|
|
731
994
|
return {
|
|
732
995
|
content,
|
|
733
996
|
toolCalls,
|
|
997
|
+
providerMetadata: responseMetadata(response),
|
|
734
998
|
usage: response?.usage ? {
|
|
735
999
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
736
1000
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -741,6 +1005,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
741
1005
|
}
|
|
742
1006
|
buildResponsesPayload(request, stream) {
|
|
743
1007
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1008
|
+
const options = resolveOpenAITransport(
|
|
1009
|
+
this.config,
|
|
1010
|
+
request.config?.openai
|
|
1011
|
+
).responses;
|
|
1012
|
+
const parameters = buildOpenAIParameters(
|
|
1013
|
+
request.config?.model || this.model,
|
|
1014
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1015
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1016
|
+
this.parameterOptions(request),
|
|
1017
|
+
"responses"
|
|
1018
|
+
);
|
|
744
1019
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
745
1020
|
request.config?.responseFormat
|
|
746
1021
|
);
|
|
@@ -750,18 +1025,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
750
1025
|
instructions: request.systemPrompt,
|
|
751
1026
|
input: this.buildResponsesInput(request),
|
|
752
1027
|
tools: this.buildResponsesTools(
|
|
753
|
-
|
|
1028
|
+
this.toolDefinitions(request),
|
|
754
1029
|
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
755
1030
|
this.buildResponsesWebSearchTool(request)
|
|
756
1031
|
),
|
|
757
|
-
tool_choice: openaiToolOptions?.toolChoice
|
|
1032
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
758
1033
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1034
|
+
...parameters,
|
|
1035
|
+
...options?.reasoningSummary ? {
|
|
1036
|
+
reasoning: {
|
|
1037
|
+
...parameters.reasoning,
|
|
1038
|
+
summary: options.reasoningSummary
|
|
1039
|
+
}
|
|
1040
|
+
} : {},
|
|
1041
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1042
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1043
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1044
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1045
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1046
|
+
...options?.store === false || options?.include ? {
|
|
1047
|
+
include: [
|
|
1048
|
+
.../* @__PURE__ */ new Set([
|
|
1049
|
+
...options?.include ?? [],
|
|
1050
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1051
|
+
])
|
|
1052
|
+
]
|
|
1053
|
+
} : {},
|
|
765
1054
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
766
1055
|
stream
|
|
767
1056
|
};
|
|
@@ -769,10 +1058,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
769
1058
|
}
|
|
770
1059
|
async completeWithResponses(request) {
|
|
771
1060
|
const client = await this.getClient();
|
|
1061
|
+
throwIfAborted(request.signal);
|
|
772
1062
|
const payload = this.buildResponsesPayload(request, false);
|
|
773
1063
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
774
|
-
|
|
1064
|
+
if (!client.responses?.create)
|
|
1065
|
+
throw new Error(
|
|
1066
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1067
|
+
);
|
|
1068
|
+
const response = await client.responses.create(payload, {
|
|
1069
|
+
signal: request.signal
|
|
1070
|
+
});
|
|
1071
|
+
throwIfAborted(request.signal);
|
|
775
1072
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1073
|
+
if (response.status && response.status !== "completed") {
|
|
1074
|
+
throw Object.assign(
|
|
1075
|
+
new Error(
|
|
1076
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
1077
|
+
),
|
|
1078
|
+
response.error
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
776
1081
|
return this.parseResponsesResult(response);
|
|
777
1082
|
}
|
|
778
1083
|
/**
|
|
@@ -790,13 +1095,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
790
1095
|
*/
|
|
791
1096
|
async *streamWithResponses(request) {
|
|
792
1097
|
const client = await this.getClient();
|
|
1098
|
+
throwIfAborted(request.signal);
|
|
793
1099
|
const payload = this.buildResponsesPayload(request, true);
|
|
794
1100
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
795
|
-
|
|
1101
|
+
if (!client.responses?.create)
|
|
1102
|
+
throw new Error(
|
|
1103
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1104
|
+
);
|
|
1105
|
+
const stream = await client.responses.create(payload, {
|
|
1106
|
+
signal: request.signal
|
|
1107
|
+
});
|
|
796
1108
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
797
1109
|
let usage;
|
|
798
1110
|
const citations = [];
|
|
1111
|
+
let completed = false;
|
|
1112
|
+
let terminalResponse;
|
|
1113
|
+
let responseIdentity = {};
|
|
1114
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1115
|
+
let streamedText = "";
|
|
799
1116
|
function* flushToolCalls() {
|
|
1117
|
+
for (const call of toolCalls.values()) {
|
|
1118
|
+
if (!call.name)
|
|
1119
|
+
throw new Error("Responses tool call is missing its name");
|
|
1120
|
+
JSON.parse(call.arguments || "{}");
|
|
1121
|
+
}
|
|
800
1122
|
for (const call of toolCalls.values()) {
|
|
801
1123
|
yield { type: "action:start", id: call.id, name: call.name };
|
|
802
1124
|
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
@@ -805,19 +1127,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
805
1127
|
toolCalls.clear();
|
|
806
1128
|
}
|
|
807
1129
|
for await (const event of stream) {
|
|
808
|
-
|
|
1130
|
+
throwIfAborted(request.signal);
|
|
809
1131
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1132
|
+
if (completed)
|
|
1133
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
810
1134
|
const index = event?.output_index ?? 0;
|
|
811
1135
|
switch (event?.type) {
|
|
1136
|
+
case "response.created":
|
|
1137
|
+
responseIdentity = {
|
|
1138
|
+
id: event.response?.id,
|
|
1139
|
+
model: event.response?.model
|
|
1140
|
+
};
|
|
1141
|
+
break;
|
|
1142
|
+
case "response.output_item.done":
|
|
1143
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1144
|
+
if (event.item?.type === "function_call") {
|
|
1145
|
+
toolCalls.set(index, {
|
|
1146
|
+
id: event.item.call_id,
|
|
1147
|
+
name: event.item.name,
|
|
1148
|
+
arguments: event.item.arguments
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
break;
|
|
812
1152
|
case "response.output_text.delta":
|
|
813
1153
|
if (event.delta) {
|
|
1154
|
+
streamedText += event.delta;
|
|
814
1155
|
yield { type: "message:delta", content: event.delta };
|
|
815
1156
|
}
|
|
816
1157
|
break;
|
|
817
1158
|
case "response.output_item.added":
|
|
818
1159
|
if (event.item?.type === "function_call") {
|
|
819
1160
|
toolCalls.set(index, {
|
|
820
|
-
id: event.item.call_id ??
|
|
1161
|
+
id: event.item.call_id ?? "",
|
|
821
1162
|
name: event.item.name ?? "",
|
|
822
1163
|
arguments: event.item.arguments ?? ""
|
|
823
1164
|
});
|
|
@@ -835,8 +1176,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
835
1176
|
}
|
|
836
1177
|
break;
|
|
837
1178
|
}
|
|
838
|
-
case "response.completed":
|
|
839
|
-
|
|
1179
|
+
case "response.completed": {
|
|
1180
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1181
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
1182
|
+
completed = true;
|
|
1183
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
840
1184
|
const found = this.collectResponsesCitations(
|
|
841
1185
|
event.response?.output,
|
|
842
1186
|
citations.length
|
|
@@ -852,21 +1196,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
852
1196
|
}
|
|
853
1197
|
break;
|
|
854
1198
|
}
|
|
855
|
-
case "
|
|
856
|
-
case "response.failed":
|
|
1199
|
+
case "response.incomplete":
|
|
857
1200
|
throw new Error(
|
|
858
|
-
|
|
1201
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
1202
|
+
);
|
|
1203
|
+
case "error":
|
|
1204
|
+
case "response.failed": {
|
|
1205
|
+
const details = event.response?.error ?? event;
|
|
1206
|
+
throw Object.assign(
|
|
1207
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
1208
|
+
{
|
|
1209
|
+
code: details.code,
|
|
1210
|
+
param: details.param,
|
|
1211
|
+
status: details.status
|
|
1212
|
+
}
|
|
859
1213
|
);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
throwIfAborted(request.signal);
|
|
1218
|
+
if (!completed)
|
|
1219
|
+
throw new Error("Responses stream ended without a completed response");
|
|
1220
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1221
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1222
|
+
toolCalls.clear();
|
|
1223
|
+
for (const [index, item] of output.entries()) {
|
|
1224
|
+
if (item.type === "function_call") {
|
|
1225
|
+
toolCalls.set(index, {
|
|
1226
|
+
id: item.call_id,
|
|
1227
|
+
name: item.name,
|
|
1228
|
+
arguments: item.arguments
|
|
1229
|
+
});
|
|
860
1230
|
}
|
|
861
1231
|
}
|
|
1232
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1233
|
+
for (const call of toolCalls.values()) {
|
|
1234
|
+
if (!call.id || !call.name)
|
|
1235
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1236
|
+
uniqueCalls.set(call.id, call);
|
|
1237
|
+
}
|
|
1238
|
+
toolCalls.clear();
|
|
1239
|
+
[...uniqueCalls.values()].forEach(
|
|
1240
|
+
(call, index) => toolCalls.set(index, call)
|
|
1241
|
+
);
|
|
1242
|
+
if (!output.length && toolCalls.size) {
|
|
1243
|
+
for (const call of toolCalls.values())
|
|
1244
|
+
output.push({
|
|
1245
|
+
type: "function_call",
|
|
1246
|
+
call_id: call.id,
|
|
1247
|
+
name: call.name,
|
|
1248
|
+
arguments: call.arguments
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
if (!output.length && streamedText)
|
|
1252
|
+
output.push({
|
|
1253
|
+
type: "message",
|
|
1254
|
+
role: "assistant",
|
|
1255
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1256
|
+
});
|
|
1257
|
+
terminalResponse.output = output;
|
|
1258
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1259
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1260
|
+
yield {
|
|
1261
|
+
type: "message:delta",
|
|
1262
|
+
content: finalText.slice(streamedText.length)
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
862
1265
|
yield* flushToolCalls();
|
|
863
1266
|
if (citations.length > 0) {
|
|
864
1267
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
865
1268
|
}
|
|
866
|
-
yield {
|
|
867
|
-
|
|
1269
|
+
yield {
|
|
1270
|
+
type: "message:end",
|
|
1271
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1272
|
+
};
|
|
1273
|
+
yield {
|
|
1274
|
+
type: "done",
|
|
1275
|
+
usage,
|
|
1276
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1277
|
+
};
|
|
868
1278
|
}
|
|
869
1279
|
async *stream(request) {
|
|
1280
|
+
throwIfAborted(request.signal);
|
|
870
1281
|
if (this.shouldUseResponsesApi(request)) {
|
|
871
1282
|
const messageId2 = generateMessageId();
|
|
872
1283
|
yield { type: "message:start", id: messageId2 };
|
|
@@ -874,47 +1285,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
874
1285
|
yield* this.streamWithResponses(request);
|
|
875
1286
|
return;
|
|
876
1287
|
} catch (error) {
|
|
877
|
-
yield
|
|
878
|
-
type: "error",
|
|
879
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
880
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
881
|
-
};
|
|
1288
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
882
1289
|
return;
|
|
883
1290
|
}
|
|
884
1291
|
}
|
|
885
1292
|
const client = await this.getClient();
|
|
886
1293
|
let messages;
|
|
887
1294
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
888
|
-
const processedMessages = request.rawMessages
|
|
889
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
890
|
-
return { ...msg, content: null };
|
|
891
|
-
}
|
|
892
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
893
|
-
if (hasAttachments) {
|
|
894
|
-
const content = [];
|
|
895
|
-
if (msg.content) {
|
|
896
|
-
content.push({ type: "text", text: msg.content });
|
|
897
|
-
}
|
|
898
|
-
for (const attachment of msg.attachments) {
|
|
899
|
-
if (attachment.type === "image") {
|
|
900
|
-
let imageUrl;
|
|
901
|
-
if (attachment.url) {
|
|
902
|
-
imageUrl = attachment.url;
|
|
903
|
-
} else if (attachment.data) {
|
|
904
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
905
|
-
} else {
|
|
906
|
-
continue;
|
|
907
|
-
}
|
|
908
|
-
content.push({
|
|
909
|
-
type: "image_url",
|
|
910
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
return { ...msg, content, attachments: void 0 };
|
|
915
|
-
}
|
|
916
|
-
return msg;
|
|
917
|
-
});
|
|
1295
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
918
1296
|
if (request.systemPrompt) {
|
|
919
1297
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
920
1298
|
if (!hasSystem) {
|
|
@@ -934,7 +1312,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
934
1312
|
request.systemPrompt
|
|
935
1313
|
);
|
|
936
1314
|
}
|
|
937
|
-
const tools = request.
|
|
1315
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1316
|
+
type: "function",
|
|
1317
|
+
function: {
|
|
1318
|
+
name: tool.name,
|
|
1319
|
+
description: tool.description,
|
|
1320
|
+
parameters: normalizeObjectJsonSchema(
|
|
1321
|
+
tool.inputSchema
|
|
1322
|
+
)
|
|
1323
|
+
}
|
|
1324
|
+
})) : formatTools(request.actions ?? []);
|
|
938
1325
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
939
1326
|
if (webSearchConfig) {
|
|
940
1327
|
const webSearchTool = {
|
|
@@ -963,26 +1350,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
963
1350
|
tools: tools.length > 0 ? tools : void 0,
|
|
964
1351
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
965
1352
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
966
|
-
...
|
|
1353
|
+
...buildOpenAIParameters(
|
|
967
1354
|
modelIdForPayload,
|
|
968
1355
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
969
|
-
request.config?.temperature ?? this.config.temperature
|
|
1356
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1357
|
+
this.parameterOptions(request)
|
|
970
1358
|
),
|
|
971
1359
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
972
1360
|
stream: true,
|
|
973
1361
|
stream_options: { include_usage: true }
|
|
974
1362
|
};
|
|
975
1363
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
976
|
-
const stream = await client.chat.completions.create(payload
|
|
977
|
-
|
|
1364
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1365
|
+
signal: request.signal
|
|
1366
|
+
});
|
|
1367
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1368
|
+
let finishReason;
|
|
978
1369
|
const collectedCitations = [];
|
|
979
1370
|
let citationIndex = 0;
|
|
980
1371
|
let usage;
|
|
981
1372
|
for await (const chunk of stream) {
|
|
982
1373
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
983
|
-
|
|
984
|
-
break;
|
|
985
|
-
}
|
|
1374
|
+
throwIfAborted(request.signal);
|
|
986
1375
|
const delta = chunk.choices[0]?.delta;
|
|
987
1376
|
const choice = chunk.choices[0];
|
|
988
1377
|
if (delta?.content) {
|
|
@@ -1005,38 +1394,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1005
1394
|
}
|
|
1006
1395
|
}
|
|
1007
1396
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
const tcExtraContent = toolCall.extra_content;
|
|
1024
|
-
currentToolCall = {
|
|
1025
|
-
id: toolCall.id,
|
|
1026
|
-
name: toolCall.function?.name || "",
|
|
1027
|
-
arguments: toolCall.function?.arguments || "",
|
|
1028
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
1029
|
-
};
|
|
1030
|
-
yield {
|
|
1031
|
-
type: "action:start",
|
|
1032
|
-
id: currentToolCall.id,
|
|
1033
|
-
name: currentToolCall.name,
|
|
1034
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
1035
|
-
};
|
|
1036
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
1037
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1397
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1398
|
+
const index = part.index ?? 0;
|
|
1399
|
+
const call = toolCalls.get(index) ?? {
|
|
1400
|
+
id: "",
|
|
1401
|
+
name: "",
|
|
1402
|
+
arguments: ""
|
|
1403
|
+
};
|
|
1404
|
+
if (part.id) call.id = part.id;
|
|
1405
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1406
|
+
if (part.function?.arguments)
|
|
1407
|
+
call.arguments += part.function.arguments;
|
|
1408
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1409
|
+
toolCalls.set(index, call);
|
|
1040
1410
|
}
|
|
1041
1411
|
if (chunk.usage) {
|
|
1042
1412
|
usage = {
|
|
@@ -1045,21 +1415,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1045
1415
|
total_tokens: chunk.usage.total_tokens
|
|
1046
1416
|
};
|
|
1047
1417
|
}
|
|
1048
|
-
if (choice?.finish_reason)
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1418
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1419
|
+
}
|
|
1420
|
+
throwIfAborted(request.signal);
|
|
1421
|
+
if (!finishReason)
|
|
1422
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1423
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1424
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1425
|
+
}
|
|
1426
|
+
for (const call of toolCalls.values()) {
|
|
1427
|
+
if (!call.id || !call.name)
|
|
1428
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1429
|
+
JSON.parse(call.arguments || "{}");
|
|
1430
|
+
}
|
|
1431
|
+
for (const call of toolCalls.values()) {
|
|
1432
|
+
yield {
|
|
1433
|
+
type: "action:start",
|
|
1434
|
+
id: call.id,
|
|
1435
|
+
name: call.name,
|
|
1436
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1437
|
+
};
|
|
1438
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1439
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
1063
1440
|
}
|
|
1064
1441
|
if (collectedCitations.length > 0) {
|
|
1065
1442
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -1068,26 +1445,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1068
1445
|
yield { type: "message:end" };
|
|
1069
1446
|
yield { type: "done", usage };
|
|
1070
1447
|
} catch (error) {
|
|
1071
|
-
yield {
|
|
1072
|
-
type: "error",
|
|
1073
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
1074
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
1075
|
-
};
|
|
1448
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
1076
1449
|
}
|
|
1077
1450
|
}
|
|
1078
1451
|
async complete(request) {
|
|
1452
|
+
throwIfAborted(request.signal);
|
|
1079
1453
|
if (this.shouldUseResponsesApi(request)) {
|
|
1080
1454
|
return this.completeWithResponses(request);
|
|
1081
1455
|
}
|
|
1082
1456
|
const client = await this.getClient();
|
|
1083
1457
|
let messages;
|
|
1084
1458
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1085
|
-
const sanitized = request.rawMessages
|
|
1086
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
1087
|
-
return { ...msg, content: null };
|
|
1088
|
-
}
|
|
1089
|
-
return msg;
|
|
1090
|
-
});
|
|
1459
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
1091
1460
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
1092
1461
|
messages = [
|
|
1093
1462
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -1102,7 +1471,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1102
1471
|
request.systemPrompt
|
|
1103
1472
|
);
|
|
1104
1473
|
}
|
|
1105
|
-
const tools = request.
|
|
1474
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1475
|
+
type: "function",
|
|
1476
|
+
function: {
|
|
1477
|
+
name: tool.name,
|
|
1478
|
+
description: tool.description,
|
|
1479
|
+
parameters: normalizeObjectJsonSchema(
|
|
1480
|
+
tool.inputSchema
|
|
1481
|
+
)
|
|
1482
|
+
}
|
|
1483
|
+
})) : formatTools(request.actions ?? []);
|
|
1106
1484
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1107
1485
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
1108
1486
|
type: "function",
|
|
@@ -1117,16 +1495,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1117
1495
|
tools: tools.length > 0 ? tools : void 0,
|
|
1118
1496
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
1119
1497
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
1120
|
-
...
|
|
1498
|
+
...buildOpenAIParameters(
|
|
1121
1499
|
modelIdForCompletePayload,
|
|
1122
1500
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1123
|
-
request.config?.temperature ?? this.config.temperature
|
|
1501
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1502
|
+
this.parameterOptions(request)
|
|
1124
1503
|
),
|
|
1125
1504
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
1126
1505
|
stream: false
|
|
1127
1506
|
};
|
|
1128
1507
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1129
|
-
const response = await client.chat.completions.create(payload
|
|
1508
|
+
const response = await client.chat.completions.create(payload, {
|
|
1509
|
+
signal: request.signal
|
|
1510
|
+
});
|
|
1130
1511
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1131
1512
|
const choice = response.choices?.[0];
|
|
1132
1513
|
const message = choice?.message;
|