@yourgpt/llm-sdk 2.5.1-beta.6 → 2.6.0-beta.1
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 +77 -0
- package/dist/adapters/index.d.mts +54 -5
- package/dist/adapters/index.d.ts +54 -5
- package/dist/adapters/index.js +466 -169
- package/dist/adapters/index.mjs +466 -169
- package/dist/{base-C58Dsr9p.d.ts → base-BdTpUAWI.d.ts} +2 -1
- package/dist/{base-tNgbBaSo.d.mts → base-CkeT7u-O.d.mts} +2 -1
- 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 +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +89 -30
- package/dist/index.mjs +89 -30
- package/dist/providers/anthropic/index.d.mts +3 -3
- package/dist/providers/anthropic/index.d.ts +3 -3
- package/dist/providers/azure/index.d.mts +3 -3
- package/dist/providers/azure/index.d.ts +3 -3
- package/dist/providers/azure/index.js +8 -3
- package/dist/providers/azure/index.mjs +8 -3
- package/dist/providers/fireworks/index.d.mts +3 -3
- package/dist/providers/fireworks/index.d.ts +3 -3
- package/dist/providers/fireworks/index.js +466 -169
- package/dist/providers/fireworks/index.mjs +466 -169
- package/dist/providers/google/index.d.mts +3 -3
- package/dist/providers/google/index.d.ts +3 -3
- package/dist/providers/google/index.js +466 -169
- package/dist/providers/google/index.mjs +466 -169
- package/dist/providers/ollama/index.d.mts +4 -4
- package/dist/providers/ollama/index.d.ts +4 -4
- package/dist/providers/openai/index.d.mts +6 -23
- package/dist/providers/openai/index.d.ts +6 -23
- package/dist/providers/openai/index.js +912 -500
- package/dist/providers/openai/index.mjs +912 -500
- package/dist/providers/openrouter/index.d.mts +3 -3
- package/dist/providers/openrouter/index.d.ts +3 -3
- package/dist/providers/openrouter/index.js +466 -169
- package/dist/providers/openrouter/index.mjs +466 -169
- package/dist/providers/togetherai/index.d.mts +3 -3
- package/dist/providers/togetherai/index.d.ts +3 -3
- package/dist/providers/togetherai/index.js +466 -169
- package/dist/providers/togetherai/index.mjs +466 -169
- package/dist/providers/xai/index.d.mts +3 -3
- package/dist/providers/xai/index.d.ts +3 -3
- package/dist/providers/xai/index.js +466 -169
- package/dist/providers/xai/index.mjs +466 -169
- package/dist/{types-BkQCSiIt.d.mts → types-BW3csYo5.d.mts} +22 -1
- package/dist/{types-BkQCSiIt.d.ts → types-BW3csYo5.d.ts} +22 -1
- package/dist/{types-B6dhnguR.d.mts → types-BWRYIvl_.d.mts} +11 -4
- package/dist/{types-CCxPmkmK.d.ts → types-CpXcT-tY.d.ts} +11 -4
- package/dist/{types-BQ31QIsA.d.ts → types-DA54bSmX.d.ts} +3 -11
- package/dist/{types-BSSiJW2o.d.mts → types-NOswlhm9.d.mts} +3 -11
- package/dist/yourgpt/index.d.mts +1 -1
- package/dist/yourgpt/index.d.ts +1 -1
- package/package.json +115 -48
- package/LICENSE +0 -21
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/core/openai-parameters.ts
|
|
4
|
+
function capabilities(model) {
|
|
5
|
+
const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
|
|
6
|
+
const major = Number(gpt?.[1] ?? 0);
|
|
7
|
+
const minor = Number(gpt?.[2] ?? 0);
|
|
8
|
+
const chat = gpt?.[3]?.startsWith("chat") ?? false;
|
|
9
|
+
const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
|
|
10
|
+
const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
|
|
11
|
+
return { major, minor, reasoning, supportsNone };
|
|
12
|
+
}
|
|
13
|
+
function requiresOpenAIResponses(model, baseUrl, options = {}) {
|
|
14
|
+
if (options.parameterPolicy === "passthrough") return false;
|
|
15
|
+
const c = capabilities(model);
|
|
16
|
+
if (!c.reasoning || options.reasoningEffort === "none") return false;
|
|
17
|
+
if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
|
|
18
|
+
if (!baseUrl) return true;
|
|
19
|
+
try {
|
|
20
|
+
return new URL(baseUrl).hostname === "api.openai.com";
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
|
|
26
|
+
const c = capabilities(model ?? "");
|
|
27
|
+
const normalize = options.parameterPolicy !== "passthrough";
|
|
28
|
+
const effort = options.reasoningEffort;
|
|
29
|
+
if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
|
|
30
|
+
throw new Error(`${model} does not support reasoningEffort: none`);
|
|
31
|
+
}
|
|
32
|
+
if (normalize && c.major >= 6 && effort === "minimal") {
|
|
33
|
+
throw new Error(`${model} does not support reasoningEffort: minimal`);
|
|
34
|
+
}
|
|
35
|
+
const result = {
|
|
36
|
+
[api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
|
|
37
|
+
};
|
|
38
|
+
if (effort !== void 0) {
|
|
39
|
+
if (api === "responses") result.reasoning = { effort };
|
|
40
|
+
else result.reasoning_effort = effort;
|
|
41
|
+
}
|
|
42
|
+
if (temperature !== void 0) {
|
|
43
|
+
if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
|
|
44
|
+
result.temperature = temperature;
|
|
45
|
+
} else {
|
|
46
|
+
(options.onWarning ?? console.warn)(
|
|
47
|
+
`[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
3
54
|
// src/adapters/base.ts
|
|
4
55
|
function stringifyForDebug(value) {
|
|
5
56
|
return JSON.stringify(
|
|
@@ -91,25 +142,20 @@ function normalizeObjectJsonSchema(schema) {
|
|
|
91
142
|
(value) => typeof value === "string"
|
|
92
143
|
) : [];
|
|
93
144
|
normalized.required = Array.from(/* @__PURE__ */ new Set([...required, ...propertyKeys]));
|
|
94
|
-
|
|
95
|
-
normalized.additionalProperties = false;
|
|
96
|
-
}
|
|
145
|
+
normalized.additionalProperties = false;
|
|
97
146
|
} else if (type === "array" && normalized.items && typeof normalized.items === "object") {
|
|
98
147
|
normalized.items = normalizeObjectJsonSchema(
|
|
99
148
|
normalized.items
|
|
100
149
|
);
|
|
101
150
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
function buildOpenAITokenParams(modelId, maxTokens, temperature) {
|
|
109
|
-
if (isOpenAIReasoningModel(modelId)) {
|
|
110
|
-
return { max_completion_tokens: maxTokens };
|
|
151
|
+
for (const keyword of ["anyOf", "oneOf", "allOf"]) {
|
|
152
|
+
const branches = normalized[keyword];
|
|
153
|
+
if (!Array.isArray(branches)) continue;
|
|
154
|
+
normalized[keyword] = branches.map(
|
|
155
|
+
(branch) => branch && typeof branch === "object" ? normalizeObjectJsonSchema(branch) : branch
|
|
156
|
+
);
|
|
111
157
|
}
|
|
112
|
-
return
|
|
158
|
+
return normalized;
|
|
113
159
|
}
|
|
114
160
|
function toOpenAIResponseFormat(rf) {
|
|
115
161
|
if (!rf) return void 0;
|
|
@@ -490,6 +536,28 @@ async function searchOpenRouterModels(query, apiKey) {
|
|
|
490
536
|
);
|
|
491
537
|
}
|
|
492
538
|
|
|
539
|
+
// src/core/provider-error.ts
|
|
540
|
+
function providerErrorEvent(error, code) {
|
|
541
|
+
const details = error ?? {};
|
|
542
|
+
const event = {
|
|
543
|
+
type: "error",
|
|
544
|
+
message: error instanceof Error ? error.message : String(error),
|
|
545
|
+
code,
|
|
546
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
547
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
548
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
549
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
550
|
+
};
|
|
551
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
552
|
+
return event;
|
|
553
|
+
}
|
|
554
|
+
function throwIfAborted(signal) {
|
|
555
|
+
if (!signal?.aborted) return;
|
|
556
|
+
const error = new Error("The operation was aborted");
|
|
557
|
+
error.name = "AbortError";
|
|
558
|
+
throw error;
|
|
559
|
+
}
|
|
560
|
+
|
|
493
561
|
// src/core/utils.ts
|
|
494
562
|
function generateId(prefix = "id") {
|
|
495
563
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -521,24 +589,84 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
521
589
|
const { default: OpenAI } = await import('openai');
|
|
522
590
|
this.client = new OpenAI({
|
|
523
591
|
apiKey: this.config.apiKey,
|
|
524
|
-
baseURL: this.config.baseUrl
|
|
592
|
+
baseURL: this.config.baseUrl,
|
|
593
|
+
organization: this.config.organization,
|
|
594
|
+
defaultHeaders: this.config.headers
|
|
525
595
|
});
|
|
526
596
|
}
|
|
527
597
|
return this.client;
|
|
528
598
|
}
|
|
599
|
+
parameterOptions(request) {
|
|
600
|
+
return {
|
|
601
|
+
...this.config,
|
|
602
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
toolDefinitions(request) {
|
|
606
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
607
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
608
|
+
name: tool.function.name,
|
|
609
|
+
description: tool.function.description ?? "",
|
|
610
|
+
location: "server",
|
|
611
|
+
inputSchema: tool.function.parameters
|
|
612
|
+
}));
|
|
613
|
+
}
|
|
529
614
|
shouldUseResponsesApi(request) {
|
|
530
|
-
|
|
615
|
+
if (this.toolDefinitions(request).length === 0) return false;
|
|
616
|
+
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
|
|
617
|
+
return true;
|
|
618
|
+
return requiresOpenAIResponses(
|
|
619
|
+
request.config?.model || this.model,
|
|
620
|
+
this.config.baseUrl,
|
|
621
|
+
this.parameterOptions(request)
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Convert one message's content to Responses content parts.
|
|
626
|
+
*
|
|
627
|
+
* The Responses API does not accept Chat Completions content blocks: input
|
|
628
|
+
* parts are `input_text` / `input_image` (with a bare `image_url` string)
|
|
629
|
+
* rather than `text` / `image_url: { url }`. Passing the Chat Completions
|
|
630
|
+
* shape through unchanged makes the API reject the request, so anything
|
|
631
|
+
* array-shaped has to be translated rather than forwarded.
|
|
632
|
+
*/
|
|
633
|
+
toResponsesContent(content, role) {
|
|
634
|
+
const textType = role === "assistant" ? "output_text" : "input_text";
|
|
635
|
+
if (typeof content === "string") return content;
|
|
636
|
+
if (!Array.isArray(content)) {
|
|
637
|
+
return content == null ? "" : JSON.stringify(content);
|
|
638
|
+
}
|
|
639
|
+
return content.map((part) => {
|
|
640
|
+
const p = part;
|
|
641
|
+
if (p?.type === "text") return { type: textType, text: p.text ?? "" };
|
|
642
|
+
if (p?.type === "image_url") {
|
|
643
|
+
const url = typeof p.image_url === "string" ? p.image_url : p.image_url?.url;
|
|
644
|
+
return {
|
|
645
|
+
type: "input_image",
|
|
646
|
+
image_url: url,
|
|
647
|
+
...p.image_url?.detail ? { detail: p.image_url.detail } : {}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
return p;
|
|
651
|
+
});
|
|
531
652
|
}
|
|
532
653
|
buildResponsesInput(request) {
|
|
533
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
654
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
|
|
534
655
|
const input = [];
|
|
535
656
|
for (const message of sourceMessages) {
|
|
536
657
|
if (message.role === "system") {
|
|
658
|
+
if (!request.systemPrompt && message.content) {
|
|
659
|
+
input.push({
|
|
660
|
+
type: "message",
|
|
661
|
+
role: "developer",
|
|
662
|
+
content: this.toResponsesContent(message.content, "developer")
|
|
663
|
+
});
|
|
664
|
+
}
|
|
537
665
|
continue;
|
|
538
666
|
}
|
|
539
667
|
if (message.role === "assistant") {
|
|
540
|
-
const content =
|
|
541
|
-
if (content) {
|
|
668
|
+
const content = this.toResponsesContent(message.content, "assistant");
|
|
669
|
+
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
542
670
|
input.push({
|
|
543
671
|
type: "message",
|
|
544
672
|
role: "assistant",
|
|
@@ -564,15 +692,87 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
564
692
|
});
|
|
565
693
|
continue;
|
|
566
694
|
}
|
|
695
|
+
const role = message.role === "developer" ? "developer" : "user";
|
|
567
696
|
input.push({
|
|
568
697
|
type: "message",
|
|
569
|
-
role
|
|
570
|
-
content:
|
|
698
|
+
role,
|
|
699
|
+
content: this.toResponsesContent(message.content, role)
|
|
571
700
|
});
|
|
572
701
|
}
|
|
573
702
|
return input;
|
|
574
703
|
}
|
|
575
|
-
|
|
704
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
705
|
+
formatRawMessages(messages) {
|
|
706
|
+
return messages.map((message) => {
|
|
707
|
+
const { attachments, ...rest } = message;
|
|
708
|
+
let content = message.content;
|
|
709
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
710
|
+
content = null;
|
|
711
|
+
}
|
|
712
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
713
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
714
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
715
|
+
).filter((image) => image !== null);
|
|
716
|
+
if (images.length > 0) {
|
|
717
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
718
|
+
content = [...parts, ...images];
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return { ...rest, content };
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Build the web_search tool for the Responses API, or undefined when web
|
|
726
|
+
* search is off.
|
|
727
|
+
*
|
|
728
|
+
* The Chat Completions path uses `web_search_preview`; the Responses API
|
|
729
|
+
* names the same built-in `web_search`. Without this, enabling web search
|
|
730
|
+
* alongside a gpt-5.4+ function tool silently did nothing, because routing
|
|
731
|
+
* to /v1/responses skipped the Chat Completions tool assembly entirely.
|
|
732
|
+
*/
|
|
733
|
+
buildResponsesWebSearchTool(request) {
|
|
734
|
+
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
735
|
+
if (!webSearchConfig) return void 0;
|
|
736
|
+
const tool = { type: "web_search" };
|
|
737
|
+
const wsConfig = typeof webSearchConfig === "object" ? webSearchConfig : {};
|
|
738
|
+
if (wsConfig.userLocation) {
|
|
739
|
+
tool.search_context_size = "medium";
|
|
740
|
+
}
|
|
741
|
+
return tool;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Extract url_citation annotations from Responses output items.
|
|
745
|
+
*
|
|
746
|
+
* Annotations hang off the output_text content parts rather than arriving as
|
|
747
|
+
* their own stream events, so both the streaming and non-streaming paths read
|
|
748
|
+
* them from the same place.
|
|
749
|
+
*/
|
|
750
|
+
collectResponsesCitations(output, startIndex = 0) {
|
|
751
|
+
if (!Array.isArray(output)) return [];
|
|
752
|
+
const citations = [];
|
|
753
|
+
let index = startIndex;
|
|
754
|
+
for (const item of output) {
|
|
755
|
+
if (item?.type !== "message" || !Array.isArray(item.content)) continue;
|
|
756
|
+
for (const part of item.content) {
|
|
757
|
+
if (!Array.isArray(part?.annotations)) continue;
|
|
758
|
+
for (const annotation of part.annotations) {
|
|
759
|
+
const url = annotation?.url;
|
|
760
|
+
if (annotation?.type !== "url_citation" || !url) continue;
|
|
761
|
+
index++;
|
|
762
|
+
const domain = extractDomain(url);
|
|
763
|
+
citations.push({
|
|
764
|
+
index,
|
|
765
|
+
url,
|
|
766
|
+
title: annotation.title || domain,
|
|
767
|
+
domain,
|
|
768
|
+
favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return citations;
|
|
774
|
+
}
|
|
775
|
+
buildResponsesTools(tools, includeToolSearch, webSearchTool) {
|
|
576
776
|
const nativeTools = tools.filter((tool) => tool.available !== false).map((tool) => ({
|
|
577
777
|
type: "function",
|
|
578
778
|
name: tool.name,
|
|
@@ -587,20 +787,22 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
587
787
|
strict: true,
|
|
588
788
|
defer_loading: tool.deferLoading === true
|
|
589
789
|
}));
|
|
590
|
-
|
|
790
|
+
const builtins = [];
|
|
791
|
+
if (includeToolSearch) builtins.push({ type: "tool_search" });
|
|
792
|
+
if (webSearchTool) builtins.push(webSearchTool);
|
|
793
|
+
return [...builtins, ...nativeTools];
|
|
591
794
|
}
|
|
592
795
|
parseResponsesResult(response) {
|
|
593
|
-
const
|
|
796
|
+
const textFromOutput = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "message").flatMap(
|
|
797
|
+
(item) => Array.isArray(item.content) ? item.content.filter(
|
|
798
|
+
(part) => part?.type === "output_text" && typeof part.text === "string"
|
|
799
|
+
).map((part) => part.text) : []
|
|
800
|
+
).join("") : "";
|
|
801
|
+
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
594
802
|
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
|
|
595
803
|
id: item.call_id ?? item.id ?? generateToolCallId(),
|
|
596
804
|
name: item.name,
|
|
597
|
-
args: (
|
|
598
|
-
try {
|
|
599
|
-
return JSON.parse(item.arguments ?? "{}");
|
|
600
|
-
} catch {
|
|
601
|
-
return {};
|
|
602
|
-
}
|
|
603
|
-
})()
|
|
805
|
+
args: JSON.parse(item.arguments ?? "{}")
|
|
604
806
|
})) : [];
|
|
605
807
|
return {
|
|
606
808
|
content,
|
|
@@ -613,102 +815,194 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
613
815
|
rawResponse: response
|
|
614
816
|
};
|
|
615
817
|
}
|
|
616
|
-
|
|
617
|
-
const client = await this.getClient();
|
|
818
|
+
buildResponsesPayload(request, stream) {
|
|
618
819
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
619
820
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
620
821
|
request.config?.responseFormat
|
|
621
822
|
);
|
|
823
|
+
const modelIdForPayload = request.config?.model || this.model;
|
|
622
824
|
const payload = {
|
|
623
|
-
model:
|
|
825
|
+
model: modelIdForPayload,
|
|
624
826
|
instructions: request.systemPrompt,
|
|
625
827
|
input: this.buildResponsesInput(request),
|
|
626
|
-
tools: this.buildResponsesTools(
|
|
627
|
-
|
|
828
|
+
tools: this.buildResponsesTools(
|
|
829
|
+
this.toolDefinitions(request),
|
|
830
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
831
|
+
this.buildResponsesWebSearchTool(request)
|
|
832
|
+
),
|
|
833
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
628
834
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
629
|
-
|
|
630
|
-
|
|
835
|
+
...buildOpenAIParameters(
|
|
836
|
+
modelIdForPayload,
|
|
837
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
838
|
+
request.config?.temperature ?? this.config.temperature,
|
|
839
|
+
this.parameterOptions(request),
|
|
840
|
+
"responses"
|
|
841
|
+
),
|
|
631
842
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
632
|
-
stream
|
|
843
|
+
stream
|
|
633
844
|
};
|
|
845
|
+
return payload;
|
|
846
|
+
}
|
|
847
|
+
async completeWithResponses(request) {
|
|
848
|
+
const client = await this.getClient();
|
|
849
|
+
throwIfAborted(request.signal);
|
|
850
|
+
const payload = this.buildResponsesPayload(request, false);
|
|
634
851
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
635
|
-
|
|
852
|
+
if (!client.responses?.create)
|
|
853
|
+
throw new Error(
|
|
854
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
855
|
+
);
|
|
856
|
+
const response = await client.responses.create(payload, {
|
|
857
|
+
signal: request.signal
|
|
858
|
+
});
|
|
859
|
+
throwIfAborted(request.signal);
|
|
636
860
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
861
|
+
if (response.status && response.status !== "completed") {
|
|
862
|
+
throw Object.assign(
|
|
863
|
+
new Error(
|
|
864
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
865
|
+
),
|
|
866
|
+
response.error
|
|
867
|
+
);
|
|
868
|
+
}
|
|
637
869
|
return this.parseResponsesResult(response);
|
|
638
870
|
}
|
|
871
|
+
/**
|
|
872
|
+
* Stream from /v1/responses as real SSE.
|
|
873
|
+
*
|
|
874
|
+
* The Responses API emits semantic events rather than Chat Completions
|
|
875
|
+
* deltas, so the interesting ones are:
|
|
876
|
+
* response.output_text.delta — incremental assistant text
|
|
877
|
+
* response.output_item.added — a function_call item begins (name/id)
|
|
878
|
+
* response.function_call_arguments.delta / .done — tool arguments
|
|
879
|
+
* response.completed — terminal, carries usage
|
|
880
|
+
*
|
|
881
|
+
* Tool calls are buffered and flushed as contiguous start→args→end triples
|
|
882
|
+
* because the runtime pairs action:args with the action:end that follows it.
|
|
883
|
+
*/
|
|
884
|
+
async *streamWithResponses(request) {
|
|
885
|
+
const client = await this.getClient();
|
|
886
|
+
throwIfAborted(request.signal);
|
|
887
|
+
const payload = this.buildResponsesPayload(request, true);
|
|
888
|
+
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
889
|
+
if (!client.responses?.create)
|
|
890
|
+
throw new Error(
|
|
891
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
892
|
+
);
|
|
893
|
+
const stream = await client.responses.create(payload, {
|
|
894
|
+
signal: request.signal
|
|
895
|
+
});
|
|
896
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
897
|
+
let usage;
|
|
898
|
+
const citations = [];
|
|
899
|
+
let completed = false;
|
|
900
|
+
function* flushToolCalls() {
|
|
901
|
+
for (const call of toolCalls.values()) {
|
|
902
|
+
if (!call.name)
|
|
903
|
+
throw new Error("Responses tool call is missing its name");
|
|
904
|
+
JSON.parse(call.arguments || "{}");
|
|
905
|
+
}
|
|
906
|
+
for (const call of toolCalls.values()) {
|
|
907
|
+
yield { type: "action:start", id: call.id, name: call.name };
|
|
908
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
909
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
910
|
+
}
|
|
911
|
+
toolCalls.clear();
|
|
912
|
+
}
|
|
913
|
+
for await (const event of stream) {
|
|
914
|
+
throwIfAborted(request.signal);
|
|
915
|
+
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
916
|
+
const index = event?.output_index ?? 0;
|
|
917
|
+
switch (event?.type) {
|
|
918
|
+
case "response.output_text.delta":
|
|
919
|
+
if (event.delta) {
|
|
920
|
+
yield { type: "message:delta", content: event.delta };
|
|
921
|
+
}
|
|
922
|
+
break;
|
|
923
|
+
case "response.output_item.added":
|
|
924
|
+
if (event.item?.type === "function_call") {
|
|
925
|
+
toolCalls.set(index, {
|
|
926
|
+
id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
|
|
927
|
+
name: event.item.name ?? "",
|
|
928
|
+
arguments: event.item.arguments ?? ""
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
break;
|
|
932
|
+
case "response.function_call_arguments.delta": {
|
|
933
|
+
const existing = toolCalls.get(index);
|
|
934
|
+
if (existing && event.delta) existing.arguments += event.delta;
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
case "response.function_call_arguments.done": {
|
|
938
|
+
const existing = toolCalls.get(index);
|
|
939
|
+
if (existing && typeof event.arguments === "string") {
|
|
940
|
+
existing.arguments = event.arguments;
|
|
941
|
+
}
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
case "response.completed": {
|
|
945
|
+
completed = true;
|
|
946
|
+
const found = this.collectResponsesCitations(
|
|
947
|
+
event.response?.output,
|
|
948
|
+
citations.length
|
|
949
|
+
);
|
|
950
|
+
if (found.length > 0) citations.push(...found);
|
|
951
|
+
const u = event.response?.usage;
|
|
952
|
+
if (u) {
|
|
953
|
+
usage = {
|
|
954
|
+
prompt_tokens: u.input_tokens ?? 0,
|
|
955
|
+
completion_tokens: u.output_tokens ?? 0,
|
|
956
|
+
total_tokens: u.total_tokens ?? (u.input_tokens ?? 0) + (u.output_tokens ?? 0)
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
case "response.incomplete":
|
|
962
|
+
throw new Error(
|
|
963
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
964
|
+
);
|
|
965
|
+
case "error":
|
|
966
|
+
case "response.failed": {
|
|
967
|
+
const details = event.response?.error ?? event;
|
|
968
|
+
throw Object.assign(
|
|
969
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
970
|
+
{
|
|
971
|
+
code: details.code,
|
|
972
|
+
param: details.param,
|
|
973
|
+
status: details.status
|
|
974
|
+
}
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
throwIfAborted(request.signal);
|
|
980
|
+
if (!completed)
|
|
981
|
+
throw new Error("Responses stream ended without a completed response");
|
|
982
|
+
yield* flushToolCalls();
|
|
983
|
+
if (citations.length > 0) {
|
|
984
|
+
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
985
|
+
}
|
|
986
|
+
yield { type: "message:end" };
|
|
987
|
+
yield { type: "done", usage };
|
|
988
|
+
}
|
|
639
989
|
async *stream(request) {
|
|
990
|
+
throwIfAborted(request.signal);
|
|
640
991
|
if (this.shouldUseResponsesApi(request)) {
|
|
641
992
|
const messageId2 = generateMessageId();
|
|
642
993
|
yield { type: "message:start", id: messageId2 };
|
|
643
994
|
try {
|
|
644
|
-
|
|
645
|
-
if (result.content) {
|
|
646
|
-
yield { type: "message:delta", content: result.content };
|
|
647
|
-
}
|
|
648
|
-
for (const toolCall of result.toolCalls) {
|
|
649
|
-
yield {
|
|
650
|
-
type: "action:start",
|
|
651
|
-
id: toolCall.id,
|
|
652
|
-
name: toolCall.name
|
|
653
|
-
};
|
|
654
|
-
yield {
|
|
655
|
-
type: "action:args",
|
|
656
|
-
id: toolCall.id,
|
|
657
|
-
args: JSON.stringify(toolCall.args)
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
yield { type: "message:end" };
|
|
661
|
-
yield {
|
|
662
|
-
type: "done",
|
|
663
|
-
usage: result.usage ? {
|
|
664
|
-
prompt_tokens: result.usage.promptTokens,
|
|
665
|
-
completion_tokens: result.usage.completionTokens,
|
|
666
|
-
total_tokens: result.usage.totalTokens
|
|
667
|
-
} : void 0
|
|
668
|
-
};
|
|
995
|
+
yield* this.streamWithResponses(request);
|
|
669
996
|
return;
|
|
670
997
|
} catch (error) {
|
|
671
|
-
yield
|
|
672
|
-
type: "error",
|
|
673
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
674
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
675
|
-
};
|
|
998
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
676
999
|
return;
|
|
677
1000
|
}
|
|
678
1001
|
}
|
|
679
1002
|
const client = await this.getClient();
|
|
680
1003
|
let messages;
|
|
681
1004
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
682
|
-
const processedMessages = request.rawMessages
|
|
683
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
684
|
-
return { ...msg, content: null };
|
|
685
|
-
}
|
|
686
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
687
|
-
if (hasAttachments) {
|
|
688
|
-
const content = [];
|
|
689
|
-
if (msg.content) {
|
|
690
|
-
content.push({ type: "text", text: msg.content });
|
|
691
|
-
}
|
|
692
|
-
for (const attachment of msg.attachments) {
|
|
693
|
-
if (attachment.type === "image") {
|
|
694
|
-
let imageUrl;
|
|
695
|
-
if (attachment.url) {
|
|
696
|
-
imageUrl = attachment.url;
|
|
697
|
-
} else if (attachment.data) {
|
|
698
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
699
|
-
} else {
|
|
700
|
-
continue;
|
|
701
|
-
}
|
|
702
|
-
content.push({
|
|
703
|
-
type: "image_url",
|
|
704
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
return { ...msg, content, attachments: void 0 };
|
|
709
|
-
}
|
|
710
|
-
return msg;
|
|
711
|
-
});
|
|
1005
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
712
1006
|
if (request.systemPrompt) {
|
|
713
1007
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
714
1008
|
if (!hasSystem) {
|
|
@@ -728,7 +1022,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
728
1022
|
request.systemPrompt
|
|
729
1023
|
);
|
|
730
1024
|
}
|
|
731
|
-
const tools = request.
|
|
1025
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1026
|
+
type: "function",
|
|
1027
|
+
function: {
|
|
1028
|
+
name: tool.name,
|
|
1029
|
+
description: tool.description,
|
|
1030
|
+
parameters: normalizeObjectJsonSchema(
|
|
1031
|
+
tool.inputSchema
|
|
1032
|
+
)
|
|
1033
|
+
}
|
|
1034
|
+
})) : formatTools(request.actions ?? []);
|
|
732
1035
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
733
1036
|
if (webSearchConfig) {
|
|
734
1037
|
const webSearchTool = {
|
|
@@ -757,26 +1060,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
757
1060
|
tools: tools.length > 0 ? tools : void 0,
|
|
758
1061
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
759
1062
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
760
|
-
...
|
|
1063
|
+
...buildOpenAIParameters(
|
|
761
1064
|
modelIdForPayload,
|
|
762
1065
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
763
|
-
request.config?.temperature ?? this.config.temperature
|
|
1066
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1067
|
+
this.parameterOptions(request)
|
|
764
1068
|
),
|
|
765
1069
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
766
1070
|
stream: true,
|
|
767
1071
|
stream_options: { include_usage: true }
|
|
768
1072
|
};
|
|
769
1073
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
770
|
-
const stream = await client.chat.completions.create(payload
|
|
771
|
-
|
|
1074
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1075
|
+
signal: request.signal
|
|
1076
|
+
});
|
|
1077
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1078
|
+
let finishReason;
|
|
772
1079
|
const collectedCitations = [];
|
|
773
1080
|
let citationIndex = 0;
|
|
774
1081
|
let usage;
|
|
775
1082
|
for await (const chunk of stream) {
|
|
776
1083
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
777
|
-
|
|
778
|
-
break;
|
|
779
|
-
}
|
|
1084
|
+
throwIfAborted(request.signal);
|
|
780
1085
|
const delta = chunk.choices[0]?.delta;
|
|
781
1086
|
const choice = chunk.choices[0];
|
|
782
1087
|
if (delta?.content) {
|
|
@@ -799,38 +1104,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
799
1104
|
}
|
|
800
1105
|
}
|
|
801
1106
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
const tcExtraContent = toolCall.extra_content;
|
|
818
|
-
currentToolCall = {
|
|
819
|
-
id: toolCall.id,
|
|
820
|
-
name: toolCall.function?.name || "",
|
|
821
|
-
arguments: toolCall.function?.arguments || "",
|
|
822
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
823
|
-
};
|
|
824
|
-
yield {
|
|
825
|
-
type: "action:start",
|
|
826
|
-
id: currentToolCall.id,
|
|
827
|
-
name: currentToolCall.name,
|
|
828
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
829
|
-
};
|
|
830
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
831
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
1107
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1108
|
+
const index = part.index ?? 0;
|
|
1109
|
+
const call = toolCalls.get(index) ?? {
|
|
1110
|
+
id: "",
|
|
1111
|
+
name: "",
|
|
1112
|
+
arguments: ""
|
|
1113
|
+
};
|
|
1114
|
+
if (part.id) call.id = part.id;
|
|
1115
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1116
|
+
if (part.function?.arguments)
|
|
1117
|
+
call.arguments += part.function.arguments;
|
|
1118
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1119
|
+
toolCalls.set(index, call);
|
|
834
1120
|
}
|
|
835
1121
|
if (chunk.usage) {
|
|
836
1122
|
usage = {
|
|
@@ -839,21 +1125,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
839
1125
|
total_tokens: chunk.usage.total_tokens
|
|
840
1126
|
};
|
|
841
1127
|
}
|
|
842
|
-
if (choice?.finish_reason)
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1128
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1129
|
+
}
|
|
1130
|
+
throwIfAborted(request.signal);
|
|
1131
|
+
if (!finishReason)
|
|
1132
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1133
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1134
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1135
|
+
}
|
|
1136
|
+
for (const call of toolCalls.values()) {
|
|
1137
|
+
if (!call.id || !call.name)
|
|
1138
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1139
|
+
JSON.parse(call.arguments || "{}");
|
|
1140
|
+
}
|
|
1141
|
+
for (const call of toolCalls.values()) {
|
|
1142
|
+
yield {
|
|
1143
|
+
type: "action:start",
|
|
1144
|
+
id: call.id,
|
|
1145
|
+
name: call.name,
|
|
1146
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1147
|
+
};
|
|
1148
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1149
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
857
1150
|
}
|
|
858
1151
|
if (collectedCitations.length > 0) {
|
|
859
1152
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -862,26 +1155,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
862
1155
|
yield { type: "message:end" };
|
|
863
1156
|
yield { type: "done", usage };
|
|
864
1157
|
} catch (error) {
|
|
865
|
-
yield {
|
|
866
|
-
type: "error",
|
|
867
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
868
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
869
|
-
};
|
|
1158
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
870
1159
|
}
|
|
871
1160
|
}
|
|
872
1161
|
async complete(request) {
|
|
1162
|
+
throwIfAborted(request.signal);
|
|
873
1163
|
if (this.shouldUseResponsesApi(request)) {
|
|
874
1164
|
return this.completeWithResponses(request);
|
|
875
1165
|
}
|
|
876
1166
|
const client = await this.getClient();
|
|
877
1167
|
let messages;
|
|
878
1168
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
879
|
-
const sanitized = request.rawMessages
|
|
880
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
881
|
-
return { ...msg, content: null };
|
|
882
|
-
}
|
|
883
|
-
return msg;
|
|
884
|
-
});
|
|
1169
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
885
1170
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
886
1171
|
messages = [
|
|
887
1172
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -896,7 +1181,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
896
1181
|
request.systemPrompt
|
|
897
1182
|
);
|
|
898
1183
|
}
|
|
899
|
-
const tools = request.
|
|
1184
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1185
|
+
type: "function",
|
|
1186
|
+
function: {
|
|
1187
|
+
name: tool.name,
|
|
1188
|
+
description: tool.description,
|
|
1189
|
+
parameters: normalizeObjectJsonSchema(
|
|
1190
|
+
tool.inputSchema
|
|
1191
|
+
)
|
|
1192
|
+
}
|
|
1193
|
+
})) : formatTools(request.actions ?? []);
|
|
900
1194
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
901
1195
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
902
1196
|
type: "function",
|
|
@@ -911,16 +1205,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
911
1205
|
tools: tools.length > 0 ? tools : void 0,
|
|
912
1206
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
913
1207
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
914
|
-
...
|
|
1208
|
+
...buildOpenAIParameters(
|
|
915
1209
|
modelIdForCompletePayload,
|
|
916
1210
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
917
|
-
request.config?.temperature ?? this.config.temperature
|
|
1211
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1212
|
+
this.parameterOptions(request)
|
|
918
1213
|
),
|
|
919
1214
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
920
1215
|
stream: false
|
|
921
1216
|
};
|
|
922
1217
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
923
|
-
const response = await client.chat.completions.create(payload
|
|
1218
|
+
const response = await client.chat.completions.create(payload, {
|
|
1219
|
+
signal: request.signal
|
|
1220
|
+
});
|
|
924
1221
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
925
1222
|
const choice = response.choices?.[0];
|
|
926
1223
|
const message = choice?.message;
|