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