@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,3 +1,157 @@
|
|
|
1
|
+
// src/core/tool-output.ts
|
|
2
|
+
var imageTypes = /* @__PURE__ */ new Set([
|
|
3
|
+
"image/png",
|
|
4
|
+
"image/jpeg",
|
|
5
|
+
"image/webp",
|
|
6
|
+
"image/gif"
|
|
7
|
+
]);
|
|
8
|
+
function normalizeToolOutput(value) {
|
|
9
|
+
const output = value;
|
|
10
|
+
if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
|
|
11
|
+
throw new Error("Tool model output must contain a nonempty content array");
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
type: "content",
|
|
15
|
+
content: output.content.map((part) => {
|
|
16
|
+
if (part?.type === "text" && typeof part.text === "string")
|
|
17
|
+
return { type: "text", text: part.text };
|
|
18
|
+
if (part?.type !== "image")
|
|
19
|
+
throw new Error("Unsupported tool output content type");
|
|
20
|
+
const sources = ["data", "url", "fileId"].filter((key) => key in part);
|
|
21
|
+
if (sources.length !== 1)
|
|
22
|
+
throw new Error(
|
|
23
|
+
"Tool image requires exactly one of data, url, or fileId"
|
|
24
|
+
);
|
|
25
|
+
if ("fileId" in part) {
|
|
26
|
+
if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Tool image file references must name the openai provider and fileId"
|
|
29
|
+
);
|
|
30
|
+
return { type: "image", fileId: part.fileId, provider: "openai" };
|
|
31
|
+
}
|
|
32
|
+
if ("url" in part) {
|
|
33
|
+
let url;
|
|
34
|
+
try {
|
|
35
|
+
url = new URL(part.url);
|
|
36
|
+
} catch {
|
|
37
|
+
throw new Error("Invalid tool image URL");
|
|
38
|
+
}
|
|
39
|
+
if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
|
|
40
|
+
throw new Error(
|
|
41
|
+
"Tool image URL must use HTTP(S) without credentials"
|
|
42
|
+
);
|
|
43
|
+
return { type: "image", url: url.href };
|
|
44
|
+
}
|
|
45
|
+
if (!("data" in part) || !imageTypes.has(part.mediaType))
|
|
46
|
+
throw new Error("Unsupported tool image media type");
|
|
47
|
+
let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
|
|
48
|
+
if (typeof data !== "string")
|
|
49
|
+
throw new Error("Tool image data must be base64 or bytes");
|
|
50
|
+
if (data.startsWith("data:")) {
|
|
51
|
+
const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
|
|
52
|
+
if (!match || match[1] !== part.mediaType)
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Tool image data URL media type does not match mediaType"
|
|
55
|
+
);
|
|
56
|
+
data = match[2];
|
|
57
|
+
}
|
|
58
|
+
if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
|
|
59
|
+
throw new Error("Invalid tool image base64 data");
|
|
60
|
+
if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
|
|
61
|
+
throw new Error("Tool image exceeds 20 MiB");
|
|
62
|
+
if (Buffer.from(data, "base64").toString("base64") !== data)
|
|
63
|
+
throw new Error("Invalid tool image base64 data");
|
|
64
|
+
return { type: "image", data, mediaType: part.mediaType };
|
|
65
|
+
})
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function toolOutputForProvider(value, provider) {
|
|
69
|
+
const output = normalizeToolOutput(value);
|
|
70
|
+
return output.content.map((part) => {
|
|
71
|
+
if (part.type === "text")
|
|
72
|
+
return {
|
|
73
|
+
type: provider === "responses" ? "input_text" : "text",
|
|
74
|
+
text: part.text
|
|
75
|
+
};
|
|
76
|
+
if (provider === "unsupported")
|
|
77
|
+
throw new Error(
|
|
78
|
+
"Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
|
|
79
|
+
);
|
|
80
|
+
if ("fileId" in part) {
|
|
81
|
+
if (provider !== "responses")
|
|
82
|
+
throw new Error(
|
|
83
|
+
"OpenAI image file references cannot be sent to another provider"
|
|
84
|
+
);
|
|
85
|
+
return { type: "input_image", file_id: part.fileId, detail: "auto" };
|
|
86
|
+
}
|
|
87
|
+
if (provider === "responses")
|
|
88
|
+
return {
|
|
89
|
+
type: "input_image",
|
|
90
|
+
image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
|
|
91
|
+
detail: "auto"
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
type: "image",
|
|
95
|
+
source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function textToolOutput(value, fallback) {
|
|
100
|
+
if (value === void 0) return fallback;
|
|
101
|
+
return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/core/openai-parameters.ts
|
|
105
|
+
function capabilities(model) {
|
|
106
|
+
const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
|
|
107
|
+
const major = Number(gpt?.[1] ?? 0);
|
|
108
|
+
const minor = Number(gpt?.[2] ?? 0);
|
|
109
|
+
const chat = gpt?.[3]?.startsWith("chat") ?? false;
|
|
110
|
+
const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
|
|
111
|
+
const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
|
|
112
|
+
return { major, minor, reasoning, supportsNone };
|
|
113
|
+
}
|
|
114
|
+
function requiresOpenAIResponses(model, baseUrl, options = {}) {
|
|
115
|
+
if (options.parameterPolicy === "passthrough") return false;
|
|
116
|
+
const c = capabilities(model);
|
|
117
|
+
if (!c.reasoning || options.reasoningEffort === "none") return false;
|
|
118
|
+
if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
|
|
119
|
+
if (!baseUrl) return true;
|
|
120
|
+
try {
|
|
121
|
+
return new URL(baseUrl).hostname === "api.openai.com";
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
|
|
127
|
+
const c = capabilities(model ?? "");
|
|
128
|
+
const normalize = options.parameterPolicy !== "passthrough";
|
|
129
|
+
const effort = options.reasoningEffort;
|
|
130
|
+
if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
|
|
131
|
+
throw new Error(`${model} does not support reasoningEffort: none`);
|
|
132
|
+
}
|
|
133
|
+
if (normalize && c.major >= 6 && effort === "minimal") {
|
|
134
|
+
throw new Error(`${model} does not support reasoningEffort: minimal`);
|
|
135
|
+
}
|
|
136
|
+
const result = {
|
|
137
|
+
[api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
|
|
138
|
+
};
|
|
139
|
+
if (effort !== void 0) {
|
|
140
|
+
if (api === "responses") result.reasoning = { effort };
|
|
141
|
+
else result.reasoning_effort = effort;
|
|
142
|
+
}
|
|
143
|
+
if (temperature !== void 0) {
|
|
144
|
+
if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
|
|
145
|
+
result.temperature = temperature;
|
|
146
|
+
} else {
|
|
147
|
+
(options.onWarning ?? console.warn)(
|
|
148
|
+
`[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
|
|
1
155
|
// src/adapters/base.ts
|
|
2
156
|
function stringifyForDebug(value) {
|
|
3
157
|
return JSON.stringify(
|
|
@@ -104,16 +258,6 @@ function normalizeObjectJsonSchema(schema) {
|
|
|
104
258
|
}
|
|
105
259
|
return normalized;
|
|
106
260
|
}
|
|
107
|
-
function isOpenAIReasoningModel(modelId) {
|
|
108
|
-
if (!modelId) return false;
|
|
109
|
-
return /^(o1|o3|o4|gpt-5)/i.test(modelId);
|
|
110
|
-
}
|
|
111
|
-
function buildOpenAITokenParams(modelId, maxTokens, temperature) {
|
|
112
|
-
if (isOpenAIReasoningModel(modelId)) {
|
|
113
|
-
return { max_completion_tokens: maxTokens };
|
|
114
|
-
}
|
|
115
|
-
return { max_tokens: maxTokens, temperature };
|
|
116
|
-
}
|
|
117
261
|
function toOpenAIResponseFormat(rf) {
|
|
118
262
|
if (!rf) return void 0;
|
|
119
263
|
if (rf.type === "json_object") return { type: "json_object" };
|
|
@@ -198,7 +342,7 @@ function messageToOpenAIContent(message) {
|
|
|
198
342
|
}
|
|
199
343
|
return blocks;
|
|
200
344
|
}
|
|
201
|
-
function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
345
|
+
function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
|
|
202
346
|
const formatted = [];
|
|
203
347
|
if (systemPrompt) {
|
|
204
348
|
formatted.push({ role: "system", content: systemPrompt });
|
|
@@ -216,7 +360,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
216
360
|
const assistantMsg = {
|
|
217
361
|
role: "assistant",
|
|
218
362
|
// Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
|
|
219
|
-
content: hasToolCalls ? msg.content || null : msg.content
|
|
363
|
+
content: hasToolCalls ? msg.content || null : msg.content,
|
|
364
|
+
...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
|
|
220
365
|
};
|
|
221
366
|
if (hasToolCalls) {
|
|
222
367
|
assistantMsg.tool_calls = msg.tool_calls;
|
|
@@ -225,7 +370,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
|
|
|
225
370
|
} else if (msg.role === "tool" && msg.tool_call_id) {
|
|
226
371
|
formatted.push({
|
|
227
372
|
role: "tool",
|
|
228
|
-
content: msg.content ?? "",
|
|
373
|
+
content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
|
|
374
|
+
...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
|
|
229
375
|
tool_call_id: msg.tool_call_id
|
|
230
376
|
});
|
|
231
377
|
}
|
|
@@ -461,7 +607,7 @@ function formatMessagesForXAI(messages) {
|
|
|
461
607
|
return {
|
|
462
608
|
role: "tool",
|
|
463
609
|
tool_call_id: msg.toolCallId,
|
|
464
|
-
content: msg.content
|
|
610
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
465
611
|
};
|
|
466
612
|
default:
|
|
467
613
|
return msg;
|
|
@@ -469,6 +615,86 @@ function formatMessagesForXAI(messages) {
|
|
|
469
615
|
});
|
|
470
616
|
}
|
|
471
617
|
|
|
618
|
+
// src/core/openai-responses.ts
|
|
619
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
620
|
+
return {
|
|
621
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
622
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
626
|
+
const api = transport.api ?? "auto";
|
|
627
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
628
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
629
|
+
}
|
|
630
|
+
if (api === "chat") {
|
|
631
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
"Responses options and native tool search require the Responses API"
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
return false;
|
|
637
|
+
}
|
|
638
|
+
let officialEndpoint = !baseURL;
|
|
639
|
+
try {
|
|
640
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
641
|
+
} catch {
|
|
642
|
+
}
|
|
643
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
644
|
+
}
|
|
645
|
+
function responseMetadata(response) {
|
|
646
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
647
|
+
return {
|
|
648
|
+
openai: {
|
|
649
|
+
response: {
|
|
650
|
+
id: response?.id,
|
|
651
|
+
model: response?.model,
|
|
652
|
+
status: response?.status,
|
|
653
|
+
output,
|
|
654
|
+
usage: response?.usage,
|
|
655
|
+
store: response?.store,
|
|
656
|
+
refusals: output.flatMap(
|
|
657
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
658
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
659
|
+
).map((part) => part.refusal) : []
|
|
660
|
+
)
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function getResponseReplay(metadata) {
|
|
666
|
+
const response = metadata?.openai?.response;
|
|
667
|
+
if (response === void 0) return void 0;
|
|
668
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
669
|
+
throw new Error(
|
|
670
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
return response.output;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/core/provider-error.ts
|
|
677
|
+
function providerErrorEvent(error, code) {
|
|
678
|
+
const details = error ?? {};
|
|
679
|
+
const event = {
|
|
680
|
+
type: "error",
|
|
681
|
+
message: error instanceof Error ? error.message : String(error),
|
|
682
|
+
code,
|
|
683
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
684
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
685
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
686
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
687
|
+
};
|
|
688
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
689
|
+
return event;
|
|
690
|
+
}
|
|
691
|
+
function throwIfAborted(signal) {
|
|
692
|
+
if (!signal?.aborted) return;
|
|
693
|
+
const error = new Error("The operation was aborted");
|
|
694
|
+
error.name = "AbortError";
|
|
695
|
+
throw error;
|
|
696
|
+
}
|
|
697
|
+
|
|
472
698
|
// src/core/utils.ts
|
|
473
699
|
function generateId(prefix = "id") {
|
|
474
700
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -500,39 +726,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
500
726
|
const { default: OpenAI } = await import('openai');
|
|
501
727
|
this.client = new OpenAI({
|
|
502
728
|
apiKey: this.config.apiKey,
|
|
503
|
-
baseURL: this.config.baseUrl
|
|
729
|
+
baseURL: this.config.baseUrl,
|
|
730
|
+
organization: this.config.organization,
|
|
731
|
+
defaultHeaders: this.config.headers
|
|
504
732
|
});
|
|
505
733
|
}
|
|
506
734
|
return this.client;
|
|
507
735
|
}
|
|
736
|
+
parameterOptions(request) {
|
|
737
|
+
return {
|
|
738
|
+
...this.config,
|
|
739
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
toolDefinitions(request) {
|
|
743
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
744
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
745
|
+
name: tool.function.name,
|
|
746
|
+
description: tool.function.description ?? "",
|
|
747
|
+
location: "server",
|
|
748
|
+
inputSchema: tool.function.parameters
|
|
749
|
+
}));
|
|
750
|
+
}
|
|
508
751
|
shouldUseResponsesApi(request) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
request.
|
|
752
|
+
return useOpenAIResponses(
|
|
753
|
+
request.config?.model || this.model,
|
|
754
|
+
this.config.baseUrl,
|
|
755
|
+
this.parameterOptions(request),
|
|
756
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
757
|
+
this.toolDefinitions(request).length > 0,
|
|
758
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
759
|
+
(message) => message.providerMetadata?.openai?.response
|
|
760
|
+
),
|
|
761
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
516
762
|
);
|
|
517
763
|
}
|
|
518
|
-
/**
|
|
519
|
-
* Whether this model rejects function tools on /v1/chat/completions.
|
|
520
|
-
*
|
|
521
|
-
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
522
|
-
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
523
|
-
* restriction and may not implement /v1/responses at all.
|
|
524
|
-
*/
|
|
525
|
-
requiresResponsesApiForTools(modelId) {
|
|
526
|
-
if (!modelId) return false;
|
|
527
|
-
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
528
|
-
const baseUrl = this.config.baseUrl;
|
|
529
|
-
if (!baseUrl) return true;
|
|
530
|
-
try {
|
|
531
|
-
return new URL(baseUrl).hostname === "api.openai.com";
|
|
532
|
-
} catch {
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
764
|
/**
|
|
537
765
|
* Convert one message's content to Responses content parts.
|
|
538
766
|
*
|
|
@@ -563,11 +791,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
563
791
|
});
|
|
564
792
|
}
|
|
565
793
|
buildResponsesInput(request) {
|
|
566
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
794
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
567
795
|
const input = [];
|
|
568
796
|
for (const message of sourceMessages) {
|
|
569
797
|
if (message.role === "system") {
|
|
570
|
-
if (
|
|
798
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
571
799
|
input.push({
|
|
572
800
|
type: "message",
|
|
573
801
|
role: "developer",
|
|
@@ -577,6 +805,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
577
805
|
continue;
|
|
578
806
|
}
|
|
579
807
|
if (message.role === "assistant") {
|
|
808
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
809
|
+
if (replay) {
|
|
810
|
+
input.push(...replay);
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
580
813
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
581
814
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
582
815
|
input.push({
|
|
@@ -600,7 +833,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
600
833
|
input.push({
|
|
601
834
|
type: "function_call_output",
|
|
602
835
|
call_id: message.tool_call_id,
|
|
603
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
836
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
604
837
|
});
|
|
605
838
|
continue;
|
|
606
839
|
}
|
|
@@ -613,6 +846,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
613
846
|
}
|
|
614
847
|
return input;
|
|
615
848
|
}
|
|
849
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
850
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
851
|
+
return messages.map((message) => {
|
|
852
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
853
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
854
|
+
if (preserveProviderMetadata && modelOutput)
|
|
855
|
+
rest.modelOutput = modelOutput;
|
|
856
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
857
|
+
content = null;
|
|
858
|
+
}
|
|
859
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
860
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
861
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
862
|
+
).filter((image) => image !== null);
|
|
863
|
+
if (images.length > 0) {
|
|
864
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
865
|
+
content = [...parts, ...images];
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return {
|
|
869
|
+
...rest,
|
|
870
|
+
content,
|
|
871
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
872
|
+
};
|
|
873
|
+
});
|
|
874
|
+
}
|
|
616
875
|
/**
|
|
617
876
|
* Build the web_search tool for the Responses API, or undefined when web
|
|
618
877
|
* search is off.
|
|
@@ -691,20 +950,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
691
950
|
).map((part) => part.text) : []
|
|
692
951
|
).join("") : "";
|
|
693
952
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
name
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
953
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
954
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
955
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
956
|
+
throw new Error("Responses contains an incomplete function call");
|
|
957
|
+
}
|
|
958
|
+
if (seenCallIds.has(item.call_id))
|
|
959
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
960
|
+
seenCallIds.add(item.call_id);
|
|
961
|
+
const args = JSON.parse(item.arguments);
|
|
962
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
963
|
+
throw new Error(
|
|
964
|
+
"Responses function arguments must be a JSON object"
|
|
965
|
+
);
|
|
966
|
+
return { id: item.call_id, name: item.name, args };
|
|
967
|
+
}) : [];
|
|
705
968
|
return {
|
|
706
969
|
content,
|
|
707
970
|
toolCalls,
|
|
971
|
+
providerMetadata: responseMetadata(response),
|
|
708
972
|
usage: response?.usage ? {
|
|
709
973
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
710
974
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -715,6 +979,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
715
979
|
}
|
|
716
980
|
buildResponsesPayload(request, stream) {
|
|
717
981
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
982
|
+
const options = resolveOpenAITransport(
|
|
983
|
+
this.config,
|
|
984
|
+
request.config?.openai
|
|
985
|
+
).responses;
|
|
986
|
+
const parameters = buildOpenAIParameters(
|
|
987
|
+
request.config?.model || this.model,
|
|
988
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
989
|
+
request.config?.temperature ?? this.config.temperature,
|
|
990
|
+
this.parameterOptions(request),
|
|
991
|
+
"responses"
|
|
992
|
+
);
|
|
718
993
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
719
994
|
request.config?.responseFormat
|
|
720
995
|
);
|
|
@@ -724,18 +999,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
724
999
|
instructions: request.systemPrompt,
|
|
725
1000
|
input: this.buildResponsesInput(request),
|
|
726
1001
|
tools: this.buildResponsesTools(
|
|
727
|
-
|
|
1002
|
+
this.toolDefinitions(request),
|
|
728
1003
|
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
729
1004
|
this.buildResponsesWebSearchTool(request)
|
|
730
1005
|
),
|
|
731
|
-
tool_choice: openaiToolOptions?.toolChoice
|
|
1006
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
732
1007
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1008
|
+
...parameters,
|
|
1009
|
+
...options?.reasoningSummary ? {
|
|
1010
|
+
reasoning: {
|
|
1011
|
+
...parameters.reasoning,
|
|
1012
|
+
summary: options.reasoningSummary
|
|
1013
|
+
}
|
|
1014
|
+
} : {},
|
|
1015
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
1016
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
1017
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
1018
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
1019
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
1020
|
+
...options?.store === false || options?.include ? {
|
|
1021
|
+
include: [
|
|
1022
|
+
.../* @__PURE__ */ new Set([
|
|
1023
|
+
...options?.include ?? [],
|
|
1024
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1025
|
+
])
|
|
1026
|
+
]
|
|
1027
|
+
} : {},
|
|
739
1028
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
740
1029
|
stream
|
|
741
1030
|
};
|
|
@@ -743,10 +1032,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
743
1032
|
}
|
|
744
1033
|
async completeWithResponses(request) {
|
|
745
1034
|
const client = await this.getClient();
|
|
1035
|
+
throwIfAborted(request.signal);
|
|
746
1036
|
const payload = this.buildResponsesPayload(request, false);
|
|
747
1037
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
748
|
-
|
|
1038
|
+
if (!client.responses?.create)
|
|
1039
|
+
throw new Error(
|
|
1040
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1041
|
+
);
|
|
1042
|
+
const response = await client.responses.create(payload, {
|
|
1043
|
+
signal: request.signal
|
|
1044
|
+
});
|
|
1045
|
+
throwIfAborted(request.signal);
|
|
749
1046
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1047
|
+
if (response.status && response.status !== "completed") {
|
|
1048
|
+
throw Object.assign(
|
|
1049
|
+
new Error(
|
|
1050
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
1051
|
+
),
|
|
1052
|
+
response.error
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
750
1055
|
return this.parseResponsesResult(response);
|
|
751
1056
|
}
|
|
752
1057
|
/**
|
|
@@ -764,13 +1069,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
764
1069
|
*/
|
|
765
1070
|
async *streamWithResponses(request) {
|
|
766
1071
|
const client = await this.getClient();
|
|
1072
|
+
throwIfAborted(request.signal);
|
|
767
1073
|
const payload = this.buildResponsesPayload(request, true);
|
|
768
1074
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
769
|
-
|
|
1075
|
+
if (!client.responses?.create)
|
|
1076
|
+
throw new Error(
|
|
1077
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1078
|
+
);
|
|
1079
|
+
const stream = await client.responses.create(payload, {
|
|
1080
|
+
signal: request.signal
|
|
1081
|
+
});
|
|
770
1082
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
771
1083
|
let usage;
|
|
772
1084
|
const citations = [];
|
|
1085
|
+
let completed = false;
|
|
1086
|
+
let terminalResponse;
|
|
1087
|
+
let responseIdentity = {};
|
|
1088
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1089
|
+
let streamedText = "";
|
|
773
1090
|
function* flushToolCalls() {
|
|
1091
|
+
for (const call of toolCalls.values()) {
|
|
1092
|
+
if (!call.name)
|
|
1093
|
+
throw new Error("Responses tool call is missing its name");
|
|
1094
|
+
JSON.parse(call.arguments || "{}");
|
|
1095
|
+
}
|
|
774
1096
|
for (const call of toolCalls.values()) {
|
|
775
1097
|
yield { type: "action:start", id: call.id, name: call.name };
|
|
776
1098
|
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
@@ -779,19 +1101,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
779
1101
|
toolCalls.clear();
|
|
780
1102
|
}
|
|
781
1103
|
for await (const event of stream) {
|
|
782
|
-
|
|
1104
|
+
throwIfAborted(request.signal);
|
|
783
1105
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1106
|
+
if (completed)
|
|
1107
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
784
1108
|
const index = event?.output_index ?? 0;
|
|
785
1109
|
switch (event?.type) {
|
|
1110
|
+
case "response.created":
|
|
1111
|
+
responseIdentity = {
|
|
1112
|
+
id: event.response?.id,
|
|
1113
|
+
model: event.response?.model
|
|
1114
|
+
};
|
|
1115
|
+
break;
|
|
1116
|
+
case "response.output_item.done":
|
|
1117
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1118
|
+
if (event.item?.type === "function_call") {
|
|
1119
|
+
toolCalls.set(index, {
|
|
1120
|
+
id: event.item.call_id,
|
|
1121
|
+
name: event.item.name,
|
|
1122
|
+
arguments: event.item.arguments
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
break;
|
|
786
1126
|
case "response.output_text.delta":
|
|
787
1127
|
if (event.delta) {
|
|
1128
|
+
streamedText += event.delta;
|
|
788
1129
|
yield { type: "message:delta", content: event.delta };
|
|
789
1130
|
}
|
|
790
1131
|
break;
|
|
791
1132
|
case "response.output_item.added":
|
|
792
1133
|
if (event.item?.type === "function_call") {
|
|
793
1134
|
toolCalls.set(index, {
|
|
794
|
-
id: event.item.call_id ??
|
|
1135
|
+
id: event.item.call_id ?? "",
|
|
795
1136
|
name: event.item.name ?? "",
|
|
796
1137
|
arguments: event.item.arguments ?? ""
|
|
797
1138
|
});
|
|
@@ -809,8 +1150,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
809
1150
|
}
|
|
810
1151
|
break;
|
|
811
1152
|
}
|
|
812
|
-
case "response.completed":
|
|
813
|
-
|
|
1153
|
+
case "response.completed": {
|
|
1154
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1155
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
1156
|
+
completed = true;
|
|
1157
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
814
1158
|
const found = this.collectResponsesCitations(
|
|
815
1159
|
event.response?.output,
|
|
816
1160
|
citations.length
|
|
@@ -826,21 +1170,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
826
1170
|
}
|
|
827
1171
|
break;
|
|
828
1172
|
}
|
|
829
|
-
case "
|
|
830
|
-
case "response.failed":
|
|
1173
|
+
case "response.incomplete":
|
|
831
1174
|
throw new Error(
|
|
832
|
-
|
|
1175
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
1176
|
+
);
|
|
1177
|
+
case "error":
|
|
1178
|
+
case "response.failed": {
|
|
1179
|
+
const details = event.response?.error ?? event;
|
|
1180
|
+
throw Object.assign(
|
|
1181
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
1182
|
+
{
|
|
1183
|
+
code: details.code,
|
|
1184
|
+
param: details.param,
|
|
1185
|
+
status: details.status
|
|
1186
|
+
}
|
|
833
1187
|
);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
throwIfAborted(request.signal);
|
|
1192
|
+
if (!completed)
|
|
1193
|
+
throw new Error("Responses stream ended without a completed response");
|
|
1194
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1195
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1196
|
+
toolCalls.clear();
|
|
1197
|
+
for (const [index, item] of output.entries()) {
|
|
1198
|
+
if (item.type === "function_call") {
|
|
1199
|
+
toolCalls.set(index, {
|
|
1200
|
+
id: item.call_id,
|
|
1201
|
+
name: item.name,
|
|
1202
|
+
arguments: item.arguments
|
|
1203
|
+
});
|
|
834
1204
|
}
|
|
835
1205
|
}
|
|
1206
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1207
|
+
for (const call of toolCalls.values()) {
|
|
1208
|
+
if (!call.id || !call.name)
|
|
1209
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1210
|
+
uniqueCalls.set(call.id, call);
|
|
1211
|
+
}
|
|
1212
|
+
toolCalls.clear();
|
|
1213
|
+
[...uniqueCalls.values()].forEach(
|
|
1214
|
+
(call, index) => toolCalls.set(index, call)
|
|
1215
|
+
);
|
|
1216
|
+
if (!output.length && toolCalls.size) {
|
|
1217
|
+
for (const call of toolCalls.values())
|
|
1218
|
+
output.push({
|
|
1219
|
+
type: "function_call",
|
|
1220
|
+
call_id: call.id,
|
|
1221
|
+
name: call.name,
|
|
1222
|
+
arguments: call.arguments
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
if (!output.length && streamedText)
|
|
1226
|
+
output.push({
|
|
1227
|
+
type: "message",
|
|
1228
|
+
role: "assistant",
|
|
1229
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1230
|
+
});
|
|
1231
|
+
terminalResponse.output = output;
|
|
1232
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1233
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1234
|
+
yield {
|
|
1235
|
+
type: "message:delta",
|
|
1236
|
+
content: finalText.slice(streamedText.length)
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
836
1239
|
yield* flushToolCalls();
|
|
837
1240
|
if (citations.length > 0) {
|
|
838
1241
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
839
1242
|
}
|
|
840
|
-
yield {
|
|
841
|
-
|
|
1243
|
+
yield {
|
|
1244
|
+
type: "message:end",
|
|
1245
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1246
|
+
};
|
|
1247
|
+
yield {
|
|
1248
|
+
type: "done",
|
|
1249
|
+
usage,
|
|
1250
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1251
|
+
};
|
|
842
1252
|
}
|
|
843
1253
|
async *stream(request) {
|
|
1254
|
+
throwIfAborted(request.signal);
|
|
844
1255
|
if (this.shouldUseResponsesApi(request)) {
|
|
845
1256
|
const messageId2 = generateMessageId();
|
|
846
1257
|
yield { type: "message:start", id: messageId2 };
|
|
@@ -848,47 +1259,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
848
1259
|
yield* this.streamWithResponses(request);
|
|
849
1260
|
return;
|
|
850
1261
|
} catch (error) {
|
|
851
|
-
yield
|
|
852
|
-
type: "error",
|
|
853
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
854
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
855
|
-
};
|
|
1262
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
856
1263
|
return;
|
|
857
1264
|
}
|
|
858
1265
|
}
|
|
859
1266
|
const client = await this.getClient();
|
|
860
1267
|
let messages;
|
|
861
1268
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
862
|
-
const processedMessages = request.rawMessages
|
|
863
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
864
|
-
return { ...msg, content: null };
|
|
865
|
-
}
|
|
866
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
867
|
-
if (hasAttachments) {
|
|
868
|
-
const content = [];
|
|
869
|
-
if (msg.content) {
|
|
870
|
-
content.push({ type: "text", text: msg.content });
|
|
871
|
-
}
|
|
872
|
-
for (const attachment of msg.attachments) {
|
|
873
|
-
if (attachment.type === "image") {
|
|
874
|
-
let imageUrl;
|
|
875
|
-
if (attachment.url) {
|
|
876
|
-
imageUrl = attachment.url;
|
|
877
|
-
} else if (attachment.data) {
|
|
878
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
879
|
-
} else {
|
|
880
|
-
continue;
|
|
881
|
-
}
|
|
882
|
-
content.push({
|
|
883
|
-
type: "image_url",
|
|
884
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
return { ...msg, content, attachments: void 0 };
|
|
889
|
-
}
|
|
890
|
-
return msg;
|
|
891
|
-
});
|
|
1269
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
892
1270
|
if (request.systemPrompt) {
|
|
893
1271
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
894
1272
|
if (!hasSystem) {
|
|
@@ -908,7 +1286,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
908
1286
|
request.systemPrompt
|
|
909
1287
|
);
|
|
910
1288
|
}
|
|
911
|
-
const tools = request.
|
|
1289
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1290
|
+
type: "function",
|
|
1291
|
+
function: {
|
|
1292
|
+
name: tool.name,
|
|
1293
|
+
description: tool.description,
|
|
1294
|
+
parameters: normalizeObjectJsonSchema(
|
|
1295
|
+
tool.inputSchema
|
|
1296
|
+
)
|
|
1297
|
+
}
|
|
1298
|
+
})) : formatTools(request.actions ?? []);
|
|
912
1299
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
913
1300
|
if (webSearchConfig) {
|
|
914
1301
|
const webSearchTool = {
|
|
@@ -937,26 +1324,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
937
1324
|
tools: tools.length > 0 ? tools : void 0,
|
|
938
1325
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
939
1326
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
940
|
-
...
|
|
1327
|
+
...buildOpenAIParameters(
|
|
941
1328
|
modelIdForPayload,
|
|
942
1329
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
943
|
-
request.config?.temperature ?? this.config.temperature
|
|
1330
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1331
|
+
this.parameterOptions(request)
|
|
944
1332
|
),
|
|
945
1333
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
946
1334
|
stream: true,
|
|
947
1335
|
stream_options: { include_usage: true }
|
|
948
1336
|
};
|
|
949
1337
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
950
|
-
const stream = await client.chat.completions.create(payload
|
|
951
|
-
|
|
1338
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1339
|
+
signal: request.signal
|
|
1340
|
+
});
|
|
1341
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1342
|
+
let finishReason;
|
|
952
1343
|
const collectedCitations = [];
|
|
953
1344
|
let citationIndex = 0;
|
|
954
1345
|
let usage;
|
|
955
1346
|
for await (const chunk of stream) {
|
|
956
1347
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
957
|
-
|
|
958
|
-
break;
|
|
959
|
-
}
|
|
1348
|
+
throwIfAborted(request.signal);
|
|
960
1349
|
const delta = chunk.choices[0]?.delta;
|
|
961
1350
|
const choice = chunk.choices[0];
|
|
962
1351
|
if (delta?.content) {
|
|
@@ -979,38 +1368,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
979
1368
|
}
|
|
980
1369
|
}
|
|
981
1370
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
const tcExtraContent = toolCall.extra_content;
|
|
998
|
-
currentToolCall = {
|
|
999
|
-
id: toolCall.id,
|
|
1000
|
-
name: toolCall.function?.name || "",
|
|
1001
|
-
arguments: toolCall.function?.arguments || "",
|
|
1002
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
1003
|
-
};
|
|
1004
|
-
yield {
|
|
1005
|
-
type: "action:start",
|
|
1006
|
-
id: currentToolCall.id,
|
|
1007
|
-
name: currentToolCall.name,
|
|
1008
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
1009
|
-
};
|
|
1010
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
1011
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1371
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1372
|
+
const index = part.index ?? 0;
|
|
1373
|
+
const call = toolCalls.get(index) ?? {
|
|
1374
|
+
id: "",
|
|
1375
|
+
name: "",
|
|
1376
|
+
arguments: ""
|
|
1377
|
+
};
|
|
1378
|
+
if (part.id) call.id = part.id;
|
|
1379
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1380
|
+
if (part.function?.arguments)
|
|
1381
|
+
call.arguments += part.function.arguments;
|
|
1382
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1383
|
+
toolCalls.set(index, call);
|
|
1014
1384
|
}
|
|
1015
1385
|
if (chunk.usage) {
|
|
1016
1386
|
usage = {
|
|
@@ -1019,21 +1389,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1019
1389
|
total_tokens: chunk.usage.total_tokens
|
|
1020
1390
|
};
|
|
1021
1391
|
}
|
|
1022
|
-
if (choice?.finish_reason)
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1392
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1393
|
+
}
|
|
1394
|
+
throwIfAborted(request.signal);
|
|
1395
|
+
if (!finishReason)
|
|
1396
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1397
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1398
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1399
|
+
}
|
|
1400
|
+
for (const call of toolCalls.values()) {
|
|
1401
|
+
if (!call.id || !call.name)
|
|
1402
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1403
|
+
JSON.parse(call.arguments || "{}");
|
|
1404
|
+
}
|
|
1405
|
+
for (const call of toolCalls.values()) {
|
|
1406
|
+
yield {
|
|
1407
|
+
type: "action:start",
|
|
1408
|
+
id: call.id,
|
|
1409
|
+
name: call.name,
|
|
1410
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1411
|
+
};
|
|
1412
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1413
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
1037
1414
|
}
|
|
1038
1415
|
if (collectedCitations.length > 0) {
|
|
1039
1416
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -1042,26 +1419,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1042
1419
|
yield { type: "message:end" };
|
|
1043
1420
|
yield { type: "done", usage };
|
|
1044
1421
|
} catch (error) {
|
|
1045
|
-
yield {
|
|
1046
|
-
type: "error",
|
|
1047
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
1048
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
1049
|
-
};
|
|
1422
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
1050
1423
|
}
|
|
1051
1424
|
}
|
|
1052
1425
|
async complete(request) {
|
|
1426
|
+
throwIfAborted(request.signal);
|
|
1053
1427
|
if (this.shouldUseResponsesApi(request)) {
|
|
1054
1428
|
return this.completeWithResponses(request);
|
|
1055
1429
|
}
|
|
1056
1430
|
const client = await this.getClient();
|
|
1057
1431
|
let messages;
|
|
1058
1432
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1059
|
-
const sanitized = request.rawMessages
|
|
1060
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
1061
|
-
return { ...msg, content: null };
|
|
1062
|
-
}
|
|
1063
|
-
return msg;
|
|
1064
|
-
});
|
|
1433
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
1065
1434
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
1066
1435
|
messages = [
|
|
1067
1436
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -1076,7 +1445,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1076
1445
|
request.systemPrompt
|
|
1077
1446
|
);
|
|
1078
1447
|
}
|
|
1079
|
-
const tools = request.
|
|
1448
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1449
|
+
type: "function",
|
|
1450
|
+
function: {
|
|
1451
|
+
name: tool.name,
|
|
1452
|
+
description: tool.description,
|
|
1453
|
+
parameters: normalizeObjectJsonSchema(
|
|
1454
|
+
tool.inputSchema
|
|
1455
|
+
)
|
|
1456
|
+
}
|
|
1457
|
+
})) : formatTools(request.actions ?? []);
|
|
1080
1458
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1081
1459
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
1082
1460
|
type: "function",
|
|
@@ -1091,16 +1469,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1091
1469
|
tools: tools.length > 0 ? tools : void 0,
|
|
1092
1470
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
1093
1471
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
1094
|
-
...
|
|
1472
|
+
...buildOpenAIParameters(
|
|
1095
1473
|
modelIdForCompletePayload,
|
|
1096
1474
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1097
|
-
request.config?.temperature ?? this.config.temperature
|
|
1475
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1476
|
+
this.parameterOptions(request)
|
|
1098
1477
|
),
|
|
1099
1478
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
1100
1479
|
stream: false
|
|
1101
1480
|
};
|
|
1102
1481
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1103
|
-
const response = await client.chat.completions.create(payload
|
|
1482
|
+
const response = await client.chat.completions.create(payload, {
|
|
1483
|
+
signal: request.signal
|
|
1484
|
+
});
|
|
1104
1485
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1105
1486
|
const choice = response.choices?.[0];
|
|
1106
1487
|
const message = choice?.message;
|