@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
|
}
|
|
@@ -437,7 +583,7 @@ function formatMessages(messages) {
|
|
|
437
583
|
return {
|
|
438
584
|
role: "tool",
|
|
439
585
|
tool_call_id: msg.toolCallId,
|
|
440
|
-
content: msg.content
|
|
586
|
+
content: textToolOutput(msg.modelOutput, msg.content)
|
|
441
587
|
};
|
|
442
588
|
default:
|
|
443
589
|
return msg;
|
|
@@ -445,6 +591,86 @@ function formatMessages(messages) {
|
|
|
445
591
|
});
|
|
446
592
|
}
|
|
447
593
|
|
|
594
|
+
// src/core/openai-responses.ts
|
|
595
|
+
function resolveOpenAITransport(defaults, overrides) {
|
|
596
|
+
return {
|
|
597
|
+
api: overrides?.api ?? defaults.api ?? "auto",
|
|
598
|
+
responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
|
|
602
|
+
const api = transport.api ?? "auto";
|
|
603
|
+
if (!["auto", "chat", "responses"].includes(api)) {
|
|
604
|
+
throw new Error(`Unknown OpenAI API selection: ${api}`);
|
|
605
|
+
}
|
|
606
|
+
if (api === "chat") {
|
|
607
|
+
if (transport.responses !== void 0 || nativeToolSearch) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
"Responses options and native tool search require the Responses API"
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
let officialEndpoint = !baseURL;
|
|
615
|
+
try {
|
|
616
|
+
officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
|
|
617
|
+
} catch {
|
|
618
|
+
}
|
|
619
|
+
return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
|
|
620
|
+
}
|
|
621
|
+
function responseMetadata(response) {
|
|
622
|
+
const output = Array.isArray(response?.output) ? response.output : [];
|
|
623
|
+
return {
|
|
624
|
+
openai: {
|
|
625
|
+
response: {
|
|
626
|
+
id: response?.id,
|
|
627
|
+
model: response?.model,
|
|
628
|
+
status: response?.status,
|
|
629
|
+
output,
|
|
630
|
+
usage: response?.usage,
|
|
631
|
+
store: response?.store,
|
|
632
|
+
refusals: output.flatMap(
|
|
633
|
+
(item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
|
|
634
|
+
(part) => part?.type === "refusal" && typeof part.refusal === "string"
|
|
635
|
+
).map((part) => part.refusal) : []
|
|
636
|
+
)
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function getResponseReplay(metadata) {
|
|
642
|
+
const response = metadata?.openai?.response;
|
|
643
|
+
if (response === void 0) return void 0;
|
|
644
|
+
if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
|
|
645
|
+
throw new Error(
|
|
646
|
+
"Invalid OpenAI Responses replay metadata: output must contain typed items"
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
return response.output;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/core/provider-error.ts
|
|
653
|
+
function providerErrorEvent(error, code) {
|
|
654
|
+
const details = error ?? {};
|
|
655
|
+
const event = {
|
|
656
|
+
type: "error",
|
|
657
|
+
message: error instanceof Error ? error.message : String(error),
|
|
658
|
+
code,
|
|
659
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
660
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
661
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
662
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
663
|
+
};
|
|
664
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
665
|
+
return event;
|
|
666
|
+
}
|
|
667
|
+
function throwIfAborted(signal) {
|
|
668
|
+
if (!signal?.aborted) return;
|
|
669
|
+
const error = new Error("The operation was aborted");
|
|
670
|
+
error.name = "AbortError";
|
|
671
|
+
throw error;
|
|
672
|
+
}
|
|
673
|
+
|
|
448
674
|
// src/core/utils.ts
|
|
449
675
|
function generateId(prefix = "id") {
|
|
450
676
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -476,39 +702,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
476
702
|
const { default: OpenAI } = await import('openai');
|
|
477
703
|
this.client = new OpenAI({
|
|
478
704
|
apiKey: this.config.apiKey,
|
|
479
|
-
baseURL: this.config.baseUrl
|
|
705
|
+
baseURL: this.config.baseUrl,
|
|
706
|
+
organization: this.config.organization,
|
|
707
|
+
defaultHeaders: this.config.headers
|
|
480
708
|
});
|
|
481
709
|
}
|
|
482
710
|
return this.client;
|
|
483
711
|
}
|
|
712
|
+
parameterOptions(request) {
|
|
713
|
+
return {
|
|
714
|
+
...this.config,
|
|
715
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
toolDefinitions(request) {
|
|
719
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
720
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
721
|
+
name: tool.function.name,
|
|
722
|
+
description: tool.function.description ?? "",
|
|
723
|
+
location: "server",
|
|
724
|
+
inputSchema: tool.function.parameters
|
|
725
|
+
}));
|
|
726
|
+
}
|
|
484
727
|
shouldUseResponsesApi(request) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
request.
|
|
728
|
+
return useOpenAIResponses(
|
|
729
|
+
request.config?.model || this.model,
|
|
730
|
+
this.config.baseUrl,
|
|
731
|
+
this.parameterOptions(request),
|
|
732
|
+
resolveOpenAITransport(this.config, request.config?.openai),
|
|
733
|
+
this.toolDefinitions(request).length > 0,
|
|
734
|
+
!!(request.rawMessages ?? request.messages)?.some(
|
|
735
|
+
(message) => message.providerMetadata?.openai?.response
|
|
736
|
+
),
|
|
737
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
|
|
492
738
|
);
|
|
493
739
|
}
|
|
494
|
-
/**
|
|
495
|
-
* Whether this model rejects function tools on /v1/chat/completions.
|
|
496
|
-
*
|
|
497
|
-
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
498
|
-
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
499
|
-
* restriction and may not implement /v1/responses at all.
|
|
500
|
-
*/
|
|
501
|
-
requiresResponsesApiForTools(modelId) {
|
|
502
|
-
if (!modelId) return false;
|
|
503
|
-
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
504
|
-
const baseUrl = this.config.baseUrl;
|
|
505
|
-
if (!baseUrl) return true;
|
|
506
|
-
try {
|
|
507
|
-
return new URL(baseUrl).hostname === "api.openai.com";
|
|
508
|
-
} catch {
|
|
509
|
-
return false;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
740
|
/**
|
|
513
741
|
* Convert one message's content to Responses content parts.
|
|
514
742
|
*
|
|
@@ -539,11 +767,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
539
767
|
});
|
|
540
768
|
}
|
|
541
769
|
buildResponsesInput(request) {
|
|
542
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
770
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
|
|
543
771
|
const input = [];
|
|
544
772
|
for (const message of sourceMessages) {
|
|
545
773
|
if (message.role === "system") {
|
|
546
|
-
if (
|
|
774
|
+
if (message.content && message.content !== request.systemPrompt) {
|
|
547
775
|
input.push({
|
|
548
776
|
type: "message",
|
|
549
777
|
role: "developer",
|
|
@@ -553,6 +781,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
553
781
|
continue;
|
|
554
782
|
}
|
|
555
783
|
if (message.role === "assistant") {
|
|
784
|
+
const replay = getResponseReplay(message.providerMetadata);
|
|
785
|
+
if (replay) {
|
|
786
|
+
input.push(...replay);
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
556
789
|
const content = this.toResponsesContent(message.content, "assistant");
|
|
557
790
|
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
558
791
|
input.push({
|
|
@@ -576,7 +809,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
576
809
|
input.push({
|
|
577
810
|
type: "function_call_output",
|
|
578
811
|
call_id: message.tool_call_id,
|
|
579
|
-
output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
812
|
+
output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
|
|
580
813
|
});
|
|
581
814
|
continue;
|
|
582
815
|
}
|
|
@@ -589,6 +822,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
589
822
|
}
|
|
590
823
|
return input;
|
|
591
824
|
}
|
|
825
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
826
|
+
formatRawMessages(messages, preserveProviderMetadata = false) {
|
|
827
|
+
return messages.map((message) => {
|
|
828
|
+
const { attachments, providerMetadata, modelOutput, ...rest } = message;
|
|
829
|
+
let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
|
|
830
|
+
if (preserveProviderMetadata && modelOutput)
|
|
831
|
+
rest.modelOutput = modelOutput;
|
|
832
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
833
|
+
content = null;
|
|
834
|
+
}
|
|
835
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
836
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
837
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
838
|
+
).filter((image) => image !== null);
|
|
839
|
+
if (images.length > 0) {
|
|
840
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
841
|
+
content = [...parts, ...images];
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return {
|
|
845
|
+
...rest,
|
|
846
|
+
content,
|
|
847
|
+
...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
|
|
848
|
+
};
|
|
849
|
+
});
|
|
850
|
+
}
|
|
592
851
|
/**
|
|
593
852
|
* Build the web_search tool for the Responses API, or undefined when web
|
|
594
853
|
* search is off.
|
|
@@ -667,20 +926,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
667
926
|
).map((part) => part.text) : []
|
|
668
927
|
).join("") : "";
|
|
669
928
|
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
name
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
929
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
930
|
+
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
|
|
931
|
+
if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
|
|
932
|
+
throw new Error("Responses contains an incomplete function call");
|
|
933
|
+
}
|
|
934
|
+
if (seenCallIds.has(item.call_id))
|
|
935
|
+
throw new Error("Responses contains duplicate function call IDs");
|
|
936
|
+
seenCallIds.add(item.call_id);
|
|
937
|
+
const args = JSON.parse(item.arguments);
|
|
938
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
939
|
+
throw new Error(
|
|
940
|
+
"Responses function arguments must be a JSON object"
|
|
941
|
+
);
|
|
942
|
+
return { id: item.call_id, name: item.name, args };
|
|
943
|
+
}) : [];
|
|
681
944
|
return {
|
|
682
945
|
content,
|
|
683
946
|
toolCalls,
|
|
947
|
+
providerMetadata: responseMetadata(response),
|
|
684
948
|
usage: response?.usage ? {
|
|
685
949
|
promptTokens: response.usage.input_tokens ?? 0,
|
|
686
950
|
completionTokens: response.usage.output_tokens ?? 0,
|
|
@@ -691,6 +955,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
691
955
|
}
|
|
692
956
|
buildResponsesPayload(request, stream) {
|
|
693
957
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
958
|
+
const options = resolveOpenAITransport(
|
|
959
|
+
this.config,
|
|
960
|
+
request.config?.openai
|
|
961
|
+
).responses;
|
|
962
|
+
const parameters = buildOpenAIParameters(
|
|
963
|
+
request.config?.model || this.model,
|
|
964
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
965
|
+
request.config?.temperature ?? this.config.temperature,
|
|
966
|
+
this.parameterOptions(request),
|
|
967
|
+
"responses"
|
|
968
|
+
);
|
|
694
969
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
695
970
|
request.config?.responseFormat
|
|
696
971
|
);
|
|
@@ -700,18 +975,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
700
975
|
instructions: request.systemPrompt,
|
|
701
976
|
input: this.buildResponsesInput(request),
|
|
702
977
|
tools: this.buildResponsesTools(
|
|
703
|
-
|
|
978
|
+
this.toolDefinitions(request),
|
|
704
979
|
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
705
980
|
this.buildResponsesWebSearchTool(request)
|
|
706
981
|
),
|
|
707
|
-
tool_choice: openaiToolOptions?.toolChoice
|
|
982
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
708
983
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
984
|
+
...parameters,
|
|
985
|
+
...options?.reasoningSummary ? {
|
|
986
|
+
reasoning: {
|
|
987
|
+
...parameters.reasoning,
|
|
988
|
+
summary: options.reasoningSummary
|
|
989
|
+
}
|
|
990
|
+
} : {},
|
|
991
|
+
...options?.store !== void 0 ? { store: options.store } : {},
|
|
992
|
+
...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
|
|
993
|
+
...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
|
|
994
|
+
...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
|
|
995
|
+
...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
|
|
996
|
+
...options?.store === false || options?.include ? {
|
|
997
|
+
include: [
|
|
998
|
+
.../* @__PURE__ */ new Set([
|
|
999
|
+
...options?.include ?? [],
|
|
1000
|
+
...options?.store === false ? ["reasoning.encrypted_content"] : []
|
|
1001
|
+
])
|
|
1002
|
+
]
|
|
1003
|
+
} : {},
|
|
715
1004
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
716
1005
|
stream
|
|
717
1006
|
};
|
|
@@ -719,10 +1008,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
719
1008
|
}
|
|
720
1009
|
async completeWithResponses(request) {
|
|
721
1010
|
const client = await this.getClient();
|
|
1011
|
+
throwIfAborted(request.signal);
|
|
722
1012
|
const payload = this.buildResponsesPayload(request, false);
|
|
723
1013
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
724
|
-
|
|
1014
|
+
if (!client.responses?.create)
|
|
1015
|
+
throw new Error(
|
|
1016
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1017
|
+
);
|
|
1018
|
+
const response = await client.responses.create(payload, {
|
|
1019
|
+
signal: request.signal
|
|
1020
|
+
});
|
|
1021
|
+
throwIfAborted(request.signal);
|
|
725
1022
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1023
|
+
if (response.status && response.status !== "completed") {
|
|
1024
|
+
throw Object.assign(
|
|
1025
|
+
new Error(
|
|
1026
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
1027
|
+
),
|
|
1028
|
+
response.error
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
726
1031
|
return this.parseResponsesResult(response);
|
|
727
1032
|
}
|
|
728
1033
|
/**
|
|
@@ -740,13 +1045,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
740
1045
|
*/
|
|
741
1046
|
async *streamWithResponses(request) {
|
|
742
1047
|
const client = await this.getClient();
|
|
1048
|
+
throwIfAborted(request.signal);
|
|
743
1049
|
const payload = this.buildResponsesPayload(request, true);
|
|
744
1050
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
745
|
-
|
|
1051
|
+
if (!client.responses?.create)
|
|
1052
|
+
throw new Error(
|
|
1053
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
1054
|
+
);
|
|
1055
|
+
const stream = await client.responses.create(payload, {
|
|
1056
|
+
signal: request.signal
|
|
1057
|
+
});
|
|
746
1058
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
747
1059
|
let usage;
|
|
748
1060
|
const citations = [];
|
|
1061
|
+
let completed = false;
|
|
1062
|
+
let terminalResponse;
|
|
1063
|
+
let responseIdentity = {};
|
|
1064
|
+
const outputItems = /* @__PURE__ */ new Map();
|
|
1065
|
+
let streamedText = "";
|
|
749
1066
|
function* flushToolCalls() {
|
|
1067
|
+
for (const call of toolCalls.values()) {
|
|
1068
|
+
if (!call.name)
|
|
1069
|
+
throw new Error("Responses tool call is missing its name");
|
|
1070
|
+
JSON.parse(call.arguments || "{}");
|
|
1071
|
+
}
|
|
750
1072
|
for (const call of toolCalls.values()) {
|
|
751
1073
|
yield { type: "action:start", id: call.id, name: call.name };
|
|
752
1074
|
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
@@ -755,19 +1077,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
755
1077
|
toolCalls.clear();
|
|
756
1078
|
}
|
|
757
1079
|
for await (const event of stream) {
|
|
758
|
-
|
|
1080
|
+
throwIfAborted(request.signal);
|
|
759
1081
|
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
1082
|
+
if (completed)
|
|
1083
|
+
throw new Error("Responses stream emitted an event after completion");
|
|
760
1084
|
const index = event?.output_index ?? 0;
|
|
761
1085
|
switch (event?.type) {
|
|
1086
|
+
case "response.created":
|
|
1087
|
+
responseIdentity = {
|
|
1088
|
+
id: event.response?.id,
|
|
1089
|
+
model: event.response?.model
|
|
1090
|
+
};
|
|
1091
|
+
break;
|
|
1092
|
+
case "response.output_item.done":
|
|
1093
|
+
if (event.item) outputItems.set(index, event.item);
|
|
1094
|
+
if (event.item?.type === "function_call") {
|
|
1095
|
+
toolCalls.set(index, {
|
|
1096
|
+
id: event.item.call_id,
|
|
1097
|
+
name: event.item.name,
|
|
1098
|
+
arguments: event.item.arguments
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
break;
|
|
762
1102
|
case "response.output_text.delta":
|
|
763
1103
|
if (event.delta) {
|
|
1104
|
+
streamedText += event.delta;
|
|
764
1105
|
yield { type: "message:delta", content: event.delta };
|
|
765
1106
|
}
|
|
766
1107
|
break;
|
|
767
1108
|
case "response.output_item.added":
|
|
768
1109
|
if (event.item?.type === "function_call") {
|
|
769
1110
|
toolCalls.set(index, {
|
|
770
|
-
id: event.item.call_id ??
|
|
1111
|
+
id: event.item.call_id ?? "",
|
|
771
1112
|
name: event.item.name ?? "",
|
|
772
1113
|
arguments: event.item.arguments ?? ""
|
|
773
1114
|
});
|
|
@@ -785,8 +1126,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
785
1126
|
}
|
|
786
1127
|
break;
|
|
787
1128
|
}
|
|
788
|
-
case "response.completed":
|
|
789
|
-
|
|
1129
|
+
case "response.completed": {
|
|
1130
|
+
if (event.response?.status && event.response.status !== "completed")
|
|
1131
|
+
throw new Error(`Responses stream ${event.response.status}`);
|
|
1132
|
+
completed = true;
|
|
1133
|
+
terminalResponse = { ...responseIdentity, ...event.response };
|
|
790
1134
|
const found = this.collectResponsesCitations(
|
|
791
1135
|
event.response?.output,
|
|
792
1136
|
citations.length
|
|
@@ -802,21 +1146,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
802
1146
|
}
|
|
803
1147
|
break;
|
|
804
1148
|
}
|
|
805
|
-
case "
|
|
806
|
-
case "response.failed":
|
|
1149
|
+
case "response.incomplete":
|
|
807
1150
|
throw new Error(
|
|
808
|
-
|
|
1151
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
809
1152
|
);
|
|
1153
|
+
case "error":
|
|
1154
|
+
case "response.failed": {
|
|
1155
|
+
const details = event.response?.error ?? event;
|
|
1156
|
+
throw Object.assign(
|
|
1157
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
1158
|
+
{
|
|
1159
|
+
code: details.code,
|
|
1160
|
+
param: details.param,
|
|
1161
|
+
status: details.status
|
|
1162
|
+
}
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
throwIfAborted(request.signal);
|
|
1168
|
+
if (!completed)
|
|
1169
|
+
throw new Error("Responses stream ended without a completed response");
|
|
1170
|
+
const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
1171
|
+
if (output.some((item) => item.type === "function_call"))
|
|
1172
|
+
toolCalls.clear();
|
|
1173
|
+
for (const [index, item] of output.entries()) {
|
|
1174
|
+
if (item.type === "function_call") {
|
|
1175
|
+
toolCalls.set(index, {
|
|
1176
|
+
id: item.call_id,
|
|
1177
|
+
name: item.name,
|
|
1178
|
+
arguments: item.arguments
|
|
1179
|
+
});
|
|
810
1180
|
}
|
|
811
1181
|
}
|
|
1182
|
+
const uniqueCalls = /* @__PURE__ */ new Map();
|
|
1183
|
+
for (const call of toolCalls.values()) {
|
|
1184
|
+
if (!call.id || !call.name)
|
|
1185
|
+
throw new Error("Responses stream contains an incomplete tool call");
|
|
1186
|
+
uniqueCalls.set(call.id, call);
|
|
1187
|
+
}
|
|
1188
|
+
toolCalls.clear();
|
|
1189
|
+
[...uniqueCalls.values()].forEach(
|
|
1190
|
+
(call, index) => toolCalls.set(index, call)
|
|
1191
|
+
);
|
|
1192
|
+
if (!output.length && toolCalls.size) {
|
|
1193
|
+
for (const call of toolCalls.values())
|
|
1194
|
+
output.push({
|
|
1195
|
+
type: "function_call",
|
|
1196
|
+
call_id: call.id,
|
|
1197
|
+
name: call.name,
|
|
1198
|
+
arguments: call.arguments
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
if (!output.length && streamedText)
|
|
1202
|
+
output.push({
|
|
1203
|
+
type: "message",
|
|
1204
|
+
role: "assistant",
|
|
1205
|
+
content: [{ type: "output_text", text: streamedText, annotations: [] }]
|
|
1206
|
+
});
|
|
1207
|
+
terminalResponse.output = output;
|
|
1208
|
+
const finalText = this.parseResponsesResult(terminalResponse).content;
|
|
1209
|
+
if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
|
|
1210
|
+
yield {
|
|
1211
|
+
type: "message:delta",
|
|
1212
|
+
content: finalText.slice(streamedText.length)
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
812
1215
|
yield* flushToolCalls();
|
|
813
1216
|
if (citations.length > 0) {
|
|
814
1217
|
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
815
1218
|
}
|
|
816
|
-
yield {
|
|
817
|
-
|
|
1219
|
+
yield {
|
|
1220
|
+
type: "message:end",
|
|
1221
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1222
|
+
};
|
|
1223
|
+
yield {
|
|
1224
|
+
type: "done",
|
|
1225
|
+
usage,
|
|
1226
|
+
providerMetadata: responseMetadata(terminalResponse)
|
|
1227
|
+
};
|
|
818
1228
|
}
|
|
819
1229
|
async *stream(request) {
|
|
1230
|
+
throwIfAborted(request.signal);
|
|
820
1231
|
if (this.shouldUseResponsesApi(request)) {
|
|
821
1232
|
const messageId2 = generateMessageId();
|
|
822
1233
|
yield { type: "message:start", id: messageId2 };
|
|
@@ -824,47 +1235,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
824
1235
|
yield* this.streamWithResponses(request);
|
|
825
1236
|
return;
|
|
826
1237
|
} catch (error) {
|
|
827
|
-
yield
|
|
828
|
-
type: "error",
|
|
829
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
830
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
831
|
-
};
|
|
1238
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
832
1239
|
return;
|
|
833
1240
|
}
|
|
834
1241
|
}
|
|
835
1242
|
const client = await this.getClient();
|
|
836
1243
|
let messages;
|
|
837
1244
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
838
|
-
const processedMessages = request.rawMessages
|
|
839
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
840
|
-
return { ...msg, content: null };
|
|
841
|
-
}
|
|
842
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
843
|
-
if (hasAttachments) {
|
|
844
|
-
const content = [];
|
|
845
|
-
if (msg.content) {
|
|
846
|
-
content.push({ type: "text", text: msg.content });
|
|
847
|
-
}
|
|
848
|
-
for (const attachment of msg.attachments) {
|
|
849
|
-
if (attachment.type === "image") {
|
|
850
|
-
let imageUrl;
|
|
851
|
-
if (attachment.url) {
|
|
852
|
-
imageUrl = attachment.url;
|
|
853
|
-
} else if (attachment.data) {
|
|
854
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
855
|
-
} else {
|
|
856
|
-
continue;
|
|
857
|
-
}
|
|
858
|
-
content.push({
|
|
859
|
-
type: "image_url",
|
|
860
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
return { ...msg, content, attachments: void 0 };
|
|
865
|
-
}
|
|
866
|
-
return msg;
|
|
867
|
-
});
|
|
1245
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
868
1246
|
if (request.systemPrompt) {
|
|
869
1247
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
870
1248
|
if (!hasSystem) {
|
|
@@ -884,7 +1262,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
884
1262
|
request.systemPrompt
|
|
885
1263
|
);
|
|
886
1264
|
}
|
|
887
|
-
const tools = request.
|
|
1265
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1266
|
+
type: "function",
|
|
1267
|
+
function: {
|
|
1268
|
+
name: tool.name,
|
|
1269
|
+
description: tool.description,
|
|
1270
|
+
parameters: normalizeObjectJsonSchema(
|
|
1271
|
+
tool.inputSchema
|
|
1272
|
+
)
|
|
1273
|
+
}
|
|
1274
|
+
})) : formatTools(request.actions ?? []);
|
|
888
1275
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
889
1276
|
if (webSearchConfig) {
|
|
890
1277
|
const webSearchTool = {
|
|
@@ -913,26 +1300,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
913
1300
|
tools: tools.length > 0 ? tools : void 0,
|
|
914
1301
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
915
1302
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
916
|
-
...
|
|
1303
|
+
...buildOpenAIParameters(
|
|
917
1304
|
modelIdForPayload,
|
|
918
1305
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
919
|
-
request.config?.temperature ?? this.config.temperature
|
|
1306
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1307
|
+
this.parameterOptions(request)
|
|
920
1308
|
),
|
|
921
1309
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
922
1310
|
stream: true,
|
|
923
1311
|
stream_options: { include_usage: true }
|
|
924
1312
|
};
|
|
925
1313
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
926
|
-
const stream = await client.chat.completions.create(payload
|
|
927
|
-
|
|
1314
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1315
|
+
signal: request.signal
|
|
1316
|
+
});
|
|
1317
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1318
|
+
let finishReason;
|
|
928
1319
|
const collectedCitations = [];
|
|
929
1320
|
let citationIndex = 0;
|
|
930
1321
|
let usage;
|
|
931
1322
|
for await (const chunk of stream) {
|
|
932
1323
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
933
|
-
|
|
934
|
-
break;
|
|
935
|
-
}
|
|
1324
|
+
throwIfAborted(request.signal);
|
|
936
1325
|
const delta = chunk.choices[0]?.delta;
|
|
937
1326
|
const choice = chunk.choices[0];
|
|
938
1327
|
if (delta?.content) {
|
|
@@ -955,38 +1344,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
955
1344
|
}
|
|
956
1345
|
}
|
|
957
1346
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
const tcExtraContent = toolCall.extra_content;
|
|
974
|
-
currentToolCall = {
|
|
975
|
-
id: toolCall.id,
|
|
976
|
-
name: toolCall.function?.name || "",
|
|
977
|
-
arguments: toolCall.function?.arguments || "",
|
|
978
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
979
|
-
};
|
|
980
|
-
yield {
|
|
981
|
-
type: "action:start",
|
|
982
|
-
id: currentToolCall.id,
|
|
983
|
-
name: currentToolCall.name,
|
|
984
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
985
|
-
};
|
|
986
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
987
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
1347
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1348
|
+
const index = part.index ?? 0;
|
|
1349
|
+
const call = toolCalls.get(index) ?? {
|
|
1350
|
+
id: "",
|
|
1351
|
+
name: "",
|
|
1352
|
+
arguments: ""
|
|
1353
|
+
};
|
|
1354
|
+
if (part.id) call.id = part.id;
|
|
1355
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1356
|
+
if (part.function?.arguments)
|
|
1357
|
+
call.arguments += part.function.arguments;
|
|
1358
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1359
|
+
toolCalls.set(index, call);
|
|
990
1360
|
}
|
|
991
1361
|
if (chunk.usage) {
|
|
992
1362
|
usage = {
|
|
@@ -995,21 +1365,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
995
1365
|
total_tokens: chunk.usage.total_tokens
|
|
996
1366
|
};
|
|
997
1367
|
}
|
|
998
|
-
if (choice?.finish_reason)
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1368
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1369
|
+
}
|
|
1370
|
+
throwIfAborted(request.signal);
|
|
1371
|
+
if (!finishReason)
|
|
1372
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1373
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1374
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1375
|
+
}
|
|
1376
|
+
for (const call of toolCalls.values()) {
|
|
1377
|
+
if (!call.id || !call.name)
|
|
1378
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1379
|
+
JSON.parse(call.arguments || "{}");
|
|
1380
|
+
}
|
|
1381
|
+
for (const call of toolCalls.values()) {
|
|
1382
|
+
yield {
|
|
1383
|
+
type: "action:start",
|
|
1384
|
+
id: call.id,
|
|
1385
|
+
name: call.name,
|
|
1386
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1387
|
+
};
|
|
1388
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1389
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
1013
1390
|
}
|
|
1014
1391
|
if (collectedCitations.length > 0) {
|
|
1015
1392
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -1018,26 +1395,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1018
1395
|
yield { type: "message:end" };
|
|
1019
1396
|
yield { type: "done", usage };
|
|
1020
1397
|
} catch (error) {
|
|
1021
|
-
yield {
|
|
1022
|
-
type: "error",
|
|
1023
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
1024
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
1025
|
-
};
|
|
1398
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
1026
1399
|
}
|
|
1027
1400
|
}
|
|
1028
1401
|
async complete(request) {
|
|
1402
|
+
throwIfAborted(request.signal);
|
|
1029
1403
|
if (this.shouldUseResponsesApi(request)) {
|
|
1030
1404
|
return this.completeWithResponses(request);
|
|
1031
1405
|
}
|
|
1032
1406
|
const client = await this.getClient();
|
|
1033
1407
|
let messages;
|
|
1034
1408
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
1035
|
-
const sanitized = request.rawMessages
|
|
1036
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
1037
|
-
return { ...msg, content: null };
|
|
1038
|
-
}
|
|
1039
|
-
return msg;
|
|
1040
|
-
});
|
|
1409
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
1041
1410
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
1042
1411
|
messages = [
|
|
1043
1412
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -1052,7 +1421,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1052
1421
|
request.systemPrompt
|
|
1053
1422
|
);
|
|
1054
1423
|
}
|
|
1055
|
-
const tools = request.
|
|
1424
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1425
|
+
type: "function",
|
|
1426
|
+
function: {
|
|
1427
|
+
name: tool.name,
|
|
1428
|
+
description: tool.description,
|
|
1429
|
+
parameters: normalizeObjectJsonSchema(
|
|
1430
|
+
tool.inputSchema
|
|
1431
|
+
)
|
|
1432
|
+
}
|
|
1433
|
+
})) : formatTools(request.actions ?? []);
|
|
1056
1434
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
1057
1435
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
1058
1436
|
type: "function",
|
|
@@ -1067,16 +1445,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
1067
1445
|
tools: tools.length > 0 ? tools : void 0,
|
|
1068
1446
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
1069
1447
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
1070
|
-
...
|
|
1448
|
+
...buildOpenAIParameters(
|
|
1071
1449
|
modelIdForCompletePayload,
|
|
1072
1450
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
1073
|
-
request.config?.temperature ?? this.config.temperature
|
|
1451
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1452
|
+
this.parameterOptions(request)
|
|
1074
1453
|
),
|
|
1075
1454
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
1076
1455
|
stream: false
|
|
1077
1456
|
};
|
|
1078
1457
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
1079
|
-
const response = await client.chat.completions.create(payload
|
|
1458
|
+
const response = await client.chat.completions.create(payload, {
|
|
1459
|
+
signal: request.signal
|
|
1460
|
+
});
|
|
1080
1461
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
1081
1462
|
const choice = response.choices?.[0];
|
|
1082
1463
|
const message = choice?.message;
|