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