@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;
|
|
@@ -437,6 +483,28 @@ function formatMessages(messages) {
|
|
|
437
483
|
});
|
|
438
484
|
}
|
|
439
485
|
|
|
486
|
+
// src/core/provider-error.ts
|
|
487
|
+
function providerErrorEvent(error, code) {
|
|
488
|
+
const details = error ?? {};
|
|
489
|
+
const event = {
|
|
490
|
+
type: "error",
|
|
491
|
+
message: error instanceof Error ? error.message : String(error),
|
|
492
|
+
code,
|
|
493
|
+
...typeof details.status === "number" ? { status: details.status } : {},
|
|
494
|
+
...typeof details.code === "string" ? { providerCode: details.code } : {},
|
|
495
|
+
...typeof details.param === "string" ? { param: details.param } : {},
|
|
496
|
+
...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
|
|
497
|
+
};
|
|
498
|
+
Object.defineProperty(event, "cause", { value: error, enumerable: false });
|
|
499
|
+
return event;
|
|
500
|
+
}
|
|
501
|
+
function throwIfAborted(signal) {
|
|
502
|
+
if (!signal?.aborted) return;
|
|
503
|
+
const error = new Error("The operation was aborted");
|
|
504
|
+
error.name = "AbortError";
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
507
|
+
|
|
440
508
|
// src/core/utils.ts
|
|
441
509
|
function generateId(prefix = "id") {
|
|
442
510
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
@@ -468,24 +536,84 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
468
536
|
const { default: OpenAI } = await import('openai');
|
|
469
537
|
this.client = new OpenAI({
|
|
470
538
|
apiKey: this.config.apiKey,
|
|
471
|
-
baseURL: this.config.baseUrl
|
|
539
|
+
baseURL: this.config.baseUrl,
|
|
540
|
+
organization: this.config.organization,
|
|
541
|
+
defaultHeaders: this.config.headers
|
|
472
542
|
});
|
|
473
543
|
}
|
|
474
544
|
return this.client;
|
|
475
545
|
}
|
|
546
|
+
parameterOptions(request) {
|
|
547
|
+
return {
|
|
548
|
+
...this.config,
|
|
549
|
+
reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
toolDefinitions(request) {
|
|
553
|
+
if (request.toolDefinitions?.length) return request.toolDefinitions;
|
|
554
|
+
return formatTools(request.actions ?? []).map((tool) => ({
|
|
555
|
+
name: tool.function.name,
|
|
556
|
+
description: tool.function.description ?? "",
|
|
557
|
+
location: "server",
|
|
558
|
+
inputSchema: tool.function.parameters
|
|
559
|
+
}));
|
|
560
|
+
}
|
|
476
561
|
shouldUseResponsesApi(request) {
|
|
477
|
-
|
|
562
|
+
if (this.toolDefinitions(request).length === 0) return false;
|
|
563
|
+
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
|
|
564
|
+
return true;
|
|
565
|
+
return requiresOpenAIResponses(
|
|
566
|
+
request.config?.model || this.model,
|
|
567
|
+
this.config.baseUrl,
|
|
568
|
+
this.parameterOptions(request)
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Convert one message's content to Responses content parts.
|
|
573
|
+
*
|
|
574
|
+
* The Responses API does not accept Chat Completions content blocks: input
|
|
575
|
+
* parts are `input_text` / `input_image` (with a bare `image_url` string)
|
|
576
|
+
* rather than `text` / `image_url: { url }`. Passing the Chat Completions
|
|
577
|
+
* shape through unchanged makes the API reject the request, so anything
|
|
578
|
+
* array-shaped has to be translated rather than forwarded.
|
|
579
|
+
*/
|
|
580
|
+
toResponsesContent(content, role) {
|
|
581
|
+
const textType = role === "assistant" ? "output_text" : "input_text";
|
|
582
|
+
if (typeof content === "string") return content;
|
|
583
|
+
if (!Array.isArray(content)) {
|
|
584
|
+
return content == null ? "" : JSON.stringify(content);
|
|
585
|
+
}
|
|
586
|
+
return content.map((part) => {
|
|
587
|
+
const p = part;
|
|
588
|
+
if (p?.type === "text") return { type: textType, text: p.text ?? "" };
|
|
589
|
+
if (p?.type === "image_url") {
|
|
590
|
+
const url = typeof p.image_url === "string" ? p.image_url : p.image_url?.url;
|
|
591
|
+
return {
|
|
592
|
+
type: "input_image",
|
|
593
|
+
image_url: url,
|
|
594
|
+
...p.image_url?.detail ? { detail: p.image_url.detail } : {}
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
return p;
|
|
598
|
+
});
|
|
478
599
|
}
|
|
479
600
|
buildResponsesInput(request) {
|
|
480
|
-
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
601
|
+
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
|
|
481
602
|
const input = [];
|
|
482
603
|
for (const message of sourceMessages) {
|
|
483
604
|
if (message.role === "system") {
|
|
605
|
+
if (!request.systemPrompt && message.content) {
|
|
606
|
+
input.push({
|
|
607
|
+
type: "message",
|
|
608
|
+
role: "developer",
|
|
609
|
+
content: this.toResponsesContent(message.content, "developer")
|
|
610
|
+
});
|
|
611
|
+
}
|
|
484
612
|
continue;
|
|
485
613
|
}
|
|
486
614
|
if (message.role === "assistant") {
|
|
487
|
-
const content =
|
|
488
|
-
if (content) {
|
|
615
|
+
const content = this.toResponsesContent(message.content, "assistant");
|
|
616
|
+
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
489
617
|
input.push({
|
|
490
618
|
type: "message",
|
|
491
619
|
role: "assistant",
|
|
@@ -511,15 +639,87 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
511
639
|
});
|
|
512
640
|
continue;
|
|
513
641
|
}
|
|
642
|
+
const role = message.role === "developer" ? "developer" : "user";
|
|
514
643
|
input.push({
|
|
515
644
|
type: "message",
|
|
516
|
-
role
|
|
517
|
-
content:
|
|
645
|
+
role,
|
|
646
|
+
content: this.toResponsesContent(message.content, role)
|
|
518
647
|
});
|
|
519
648
|
}
|
|
520
649
|
return input;
|
|
521
650
|
}
|
|
522
|
-
|
|
651
|
+
/** Preserve Copilot screenshot attachments on every OpenAI transport. */
|
|
652
|
+
formatRawMessages(messages) {
|
|
653
|
+
return messages.map((message) => {
|
|
654
|
+
const { attachments, ...rest } = message;
|
|
655
|
+
let content = message.content;
|
|
656
|
+
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
|
|
657
|
+
content = null;
|
|
658
|
+
}
|
|
659
|
+
if (Array.isArray(attachments) && attachments.length > 0) {
|
|
660
|
+
const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
|
|
661
|
+
(attachment) => attachmentToOpenAIImage(attachment)
|
|
662
|
+
).filter((image) => image !== null);
|
|
663
|
+
if (images.length > 0) {
|
|
664
|
+
const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
|
|
665
|
+
content = [...parts, ...images];
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return { ...rest, content };
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Build the web_search tool for the Responses API, or undefined when web
|
|
673
|
+
* search is off.
|
|
674
|
+
*
|
|
675
|
+
* The Chat Completions path uses `web_search_preview`; the Responses API
|
|
676
|
+
* names the same built-in `web_search`. Without this, enabling web search
|
|
677
|
+
* alongside a gpt-5.4+ function tool silently did nothing, because routing
|
|
678
|
+
* to /v1/responses skipped the Chat Completions tool assembly entirely.
|
|
679
|
+
*/
|
|
680
|
+
buildResponsesWebSearchTool(request) {
|
|
681
|
+
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
682
|
+
if (!webSearchConfig) return void 0;
|
|
683
|
+
const tool = { type: "web_search" };
|
|
684
|
+
const wsConfig = typeof webSearchConfig === "object" ? webSearchConfig : {};
|
|
685
|
+
if (wsConfig.userLocation) {
|
|
686
|
+
tool.search_context_size = "medium";
|
|
687
|
+
}
|
|
688
|
+
return tool;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Extract url_citation annotations from Responses output items.
|
|
692
|
+
*
|
|
693
|
+
* Annotations hang off the output_text content parts rather than arriving as
|
|
694
|
+
* their own stream events, so both the streaming and non-streaming paths read
|
|
695
|
+
* them from the same place.
|
|
696
|
+
*/
|
|
697
|
+
collectResponsesCitations(output, startIndex = 0) {
|
|
698
|
+
if (!Array.isArray(output)) return [];
|
|
699
|
+
const citations = [];
|
|
700
|
+
let index = startIndex;
|
|
701
|
+
for (const item of output) {
|
|
702
|
+
if (item?.type !== "message" || !Array.isArray(item.content)) continue;
|
|
703
|
+
for (const part of item.content) {
|
|
704
|
+
if (!Array.isArray(part?.annotations)) continue;
|
|
705
|
+
for (const annotation of part.annotations) {
|
|
706
|
+
const url = annotation?.url;
|
|
707
|
+
if (annotation?.type !== "url_citation" || !url) continue;
|
|
708
|
+
index++;
|
|
709
|
+
const domain = extractDomain(url);
|
|
710
|
+
citations.push({
|
|
711
|
+
index,
|
|
712
|
+
url,
|
|
713
|
+
title: annotation.title || domain,
|
|
714
|
+
domain,
|
|
715
|
+
favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return citations;
|
|
721
|
+
}
|
|
722
|
+
buildResponsesTools(tools, includeToolSearch, webSearchTool) {
|
|
523
723
|
const nativeTools = tools.filter((tool) => tool.available !== false).map((tool) => ({
|
|
524
724
|
type: "function",
|
|
525
725
|
name: tool.name,
|
|
@@ -534,20 +734,22 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
534
734
|
strict: true,
|
|
535
735
|
defer_loading: tool.deferLoading === true
|
|
536
736
|
}));
|
|
537
|
-
|
|
737
|
+
const builtins = [];
|
|
738
|
+
if (includeToolSearch) builtins.push({ type: "tool_search" });
|
|
739
|
+
if (webSearchTool) builtins.push(webSearchTool);
|
|
740
|
+
return [...builtins, ...nativeTools];
|
|
538
741
|
}
|
|
539
742
|
parseResponsesResult(response) {
|
|
540
|
-
const
|
|
743
|
+
const textFromOutput = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "message").flatMap(
|
|
744
|
+
(item) => Array.isArray(item.content) ? item.content.filter(
|
|
745
|
+
(part) => part?.type === "output_text" && typeof part.text === "string"
|
|
746
|
+
).map((part) => part.text) : []
|
|
747
|
+
).join("") : "";
|
|
748
|
+
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
541
749
|
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
|
|
542
750
|
id: item.call_id ?? item.id ?? generateToolCallId(),
|
|
543
751
|
name: item.name,
|
|
544
|
-
args: (
|
|
545
|
-
try {
|
|
546
|
-
return JSON.parse(item.arguments ?? "{}");
|
|
547
|
-
} catch {
|
|
548
|
-
return {};
|
|
549
|
-
}
|
|
550
|
-
})()
|
|
752
|
+
args: JSON.parse(item.arguments ?? "{}")
|
|
551
753
|
})) : [];
|
|
552
754
|
return {
|
|
553
755
|
content,
|
|
@@ -560,102 +762,194 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
560
762
|
rawResponse: response
|
|
561
763
|
};
|
|
562
764
|
}
|
|
563
|
-
|
|
564
|
-
const client = await this.getClient();
|
|
765
|
+
buildResponsesPayload(request, stream) {
|
|
565
766
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
566
767
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
567
768
|
request.config?.responseFormat
|
|
568
769
|
);
|
|
770
|
+
const modelIdForPayload = request.config?.model || this.model;
|
|
569
771
|
const payload = {
|
|
570
|
-
model:
|
|
772
|
+
model: modelIdForPayload,
|
|
571
773
|
instructions: request.systemPrompt,
|
|
572
774
|
input: this.buildResponsesInput(request),
|
|
573
|
-
tools: this.buildResponsesTools(
|
|
574
|
-
|
|
775
|
+
tools: this.buildResponsesTools(
|
|
776
|
+
this.toolDefinitions(request),
|
|
777
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
778
|
+
this.buildResponsesWebSearchTool(request)
|
|
779
|
+
),
|
|
780
|
+
tool_choice: openaiToolOptions?.toolChoice,
|
|
575
781
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
576
|
-
|
|
577
|
-
|
|
782
|
+
...buildOpenAIParameters(
|
|
783
|
+
modelIdForPayload,
|
|
784
|
+
request.config?.maxTokens ?? this.config.maxTokens,
|
|
785
|
+
request.config?.temperature ?? this.config.temperature,
|
|
786
|
+
this.parameterOptions(request),
|
|
787
|
+
"responses"
|
|
788
|
+
),
|
|
578
789
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
579
|
-
stream
|
|
790
|
+
stream
|
|
580
791
|
};
|
|
792
|
+
return payload;
|
|
793
|
+
}
|
|
794
|
+
async completeWithResponses(request) {
|
|
795
|
+
const client = await this.getClient();
|
|
796
|
+
throwIfAborted(request.signal);
|
|
797
|
+
const payload = this.buildResponsesPayload(request, false);
|
|
581
798
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
582
|
-
|
|
799
|
+
if (!client.responses?.create)
|
|
800
|
+
throw new Error(
|
|
801
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
802
|
+
);
|
|
803
|
+
const response = await client.responses.create(payload, {
|
|
804
|
+
signal: request.signal
|
|
805
|
+
});
|
|
806
|
+
throwIfAborted(request.signal);
|
|
583
807
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
808
|
+
if (response.status && response.status !== "completed") {
|
|
809
|
+
throw Object.assign(
|
|
810
|
+
new Error(
|
|
811
|
+
response.error?.message ?? `Responses request ${response.status}`
|
|
812
|
+
),
|
|
813
|
+
response.error
|
|
814
|
+
);
|
|
815
|
+
}
|
|
584
816
|
return this.parseResponsesResult(response);
|
|
585
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
* Stream from /v1/responses as real SSE.
|
|
820
|
+
*
|
|
821
|
+
* The Responses API emits semantic events rather than Chat Completions
|
|
822
|
+
* deltas, so the interesting ones are:
|
|
823
|
+
* response.output_text.delta — incremental assistant text
|
|
824
|
+
* response.output_item.added — a function_call item begins (name/id)
|
|
825
|
+
* response.function_call_arguments.delta / .done — tool arguments
|
|
826
|
+
* response.completed — terminal, carries usage
|
|
827
|
+
*
|
|
828
|
+
* Tool calls are buffered and flushed as contiguous start→args→end triples
|
|
829
|
+
* because the runtime pairs action:args with the action:end that follows it.
|
|
830
|
+
*/
|
|
831
|
+
async *streamWithResponses(request) {
|
|
832
|
+
const client = await this.getClient();
|
|
833
|
+
throwIfAborted(request.signal);
|
|
834
|
+
const payload = this.buildResponsesPayload(request, true);
|
|
835
|
+
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
836
|
+
if (!client.responses?.create)
|
|
837
|
+
throw new Error(
|
|
838
|
+
"This OpenAI client does not support Responses; install an openai version with Responses support."
|
|
839
|
+
);
|
|
840
|
+
const stream = await client.responses.create(payload, {
|
|
841
|
+
signal: request.signal
|
|
842
|
+
});
|
|
843
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
844
|
+
let usage;
|
|
845
|
+
const citations = [];
|
|
846
|
+
let completed = false;
|
|
847
|
+
function* flushToolCalls() {
|
|
848
|
+
for (const call of toolCalls.values()) {
|
|
849
|
+
if (!call.name)
|
|
850
|
+
throw new Error("Responses tool call is missing its name");
|
|
851
|
+
JSON.parse(call.arguments || "{}");
|
|
852
|
+
}
|
|
853
|
+
for (const call of toolCalls.values()) {
|
|
854
|
+
yield { type: "action:start", id: call.id, name: call.name };
|
|
855
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
856
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
857
|
+
}
|
|
858
|
+
toolCalls.clear();
|
|
859
|
+
}
|
|
860
|
+
for await (const event of stream) {
|
|
861
|
+
throwIfAborted(request.signal);
|
|
862
|
+
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
863
|
+
const index = event?.output_index ?? 0;
|
|
864
|
+
switch (event?.type) {
|
|
865
|
+
case "response.output_text.delta":
|
|
866
|
+
if (event.delta) {
|
|
867
|
+
yield { type: "message:delta", content: event.delta };
|
|
868
|
+
}
|
|
869
|
+
break;
|
|
870
|
+
case "response.output_item.added":
|
|
871
|
+
if (event.item?.type === "function_call") {
|
|
872
|
+
toolCalls.set(index, {
|
|
873
|
+
id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
|
|
874
|
+
name: event.item.name ?? "",
|
|
875
|
+
arguments: event.item.arguments ?? ""
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
break;
|
|
879
|
+
case "response.function_call_arguments.delta": {
|
|
880
|
+
const existing = toolCalls.get(index);
|
|
881
|
+
if (existing && event.delta) existing.arguments += event.delta;
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
case "response.function_call_arguments.done": {
|
|
885
|
+
const existing = toolCalls.get(index);
|
|
886
|
+
if (existing && typeof event.arguments === "string") {
|
|
887
|
+
existing.arguments = event.arguments;
|
|
888
|
+
}
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
case "response.completed": {
|
|
892
|
+
completed = true;
|
|
893
|
+
const found = this.collectResponsesCitations(
|
|
894
|
+
event.response?.output,
|
|
895
|
+
citations.length
|
|
896
|
+
);
|
|
897
|
+
if (found.length > 0) citations.push(...found);
|
|
898
|
+
const u = event.response?.usage;
|
|
899
|
+
if (u) {
|
|
900
|
+
usage = {
|
|
901
|
+
prompt_tokens: u.input_tokens ?? 0,
|
|
902
|
+
completion_tokens: u.output_tokens ?? 0,
|
|
903
|
+
total_tokens: u.total_tokens ?? (u.input_tokens ?? 0) + (u.output_tokens ?? 0)
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
908
|
+
case "response.incomplete":
|
|
909
|
+
throw new Error(
|
|
910
|
+
`Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
|
|
911
|
+
);
|
|
912
|
+
case "error":
|
|
913
|
+
case "response.failed": {
|
|
914
|
+
const details = event.response?.error ?? event;
|
|
915
|
+
throw Object.assign(
|
|
916
|
+
new Error(details.message ?? "Responses stream failed"),
|
|
917
|
+
{
|
|
918
|
+
code: details.code,
|
|
919
|
+
param: details.param,
|
|
920
|
+
status: details.status
|
|
921
|
+
}
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
throwIfAborted(request.signal);
|
|
927
|
+
if (!completed)
|
|
928
|
+
throw new Error("Responses stream ended without a completed response");
|
|
929
|
+
yield* flushToolCalls();
|
|
930
|
+
if (citations.length > 0) {
|
|
931
|
+
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
932
|
+
}
|
|
933
|
+
yield { type: "message:end" };
|
|
934
|
+
yield { type: "done", usage };
|
|
935
|
+
}
|
|
586
936
|
async *stream(request) {
|
|
937
|
+
throwIfAborted(request.signal);
|
|
587
938
|
if (this.shouldUseResponsesApi(request)) {
|
|
588
939
|
const messageId2 = generateMessageId();
|
|
589
940
|
yield { type: "message:start", id: messageId2 };
|
|
590
941
|
try {
|
|
591
|
-
|
|
592
|
-
if (result.content) {
|
|
593
|
-
yield { type: "message:delta", content: result.content };
|
|
594
|
-
}
|
|
595
|
-
for (const toolCall of result.toolCalls) {
|
|
596
|
-
yield {
|
|
597
|
-
type: "action:start",
|
|
598
|
-
id: toolCall.id,
|
|
599
|
-
name: toolCall.name
|
|
600
|
-
};
|
|
601
|
-
yield {
|
|
602
|
-
type: "action:args",
|
|
603
|
-
id: toolCall.id,
|
|
604
|
-
args: JSON.stringify(toolCall.args)
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
yield { type: "message:end" };
|
|
608
|
-
yield {
|
|
609
|
-
type: "done",
|
|
610
|
-
usage: result.usage ? {
|
|
611
|
-
prompt_tokens: result.usage.promptTokens,
|
|
612
|
-
completion_tokens: result.usage.completionTokens,
|
|
613
|
-
total_tokens: result.usage.totalTokens
|
|
614
|
-
} : void 0
|
|
615
|
-
};
|
|
942
|
+
yield* this.streamWithResponses(request);
|
|
616
943
|
return;
|
|
617
944
|
} catch (error) {
|
|
618
|
-
yield
|
|
619
|
-
type: "error",
|
|
620
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
621
|
-
code: "OPENAI_RESPONSES_ERROR"
|
|
622
|
-
};
|
|
945
|
+
yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
|
|
623
946
|
return;
|
|
624
947
|
}
|
|
625
948
|
}
|
|
626
949
|
const client = await this.getClient();
|
|
627
950
|
let messages;
|
|
628
951
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
629
|
-
const processedMessages = request.rawMessages
|
|
630
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
631
|
-
return { ...msg, content: null };
|
|
632
|
-
}
|
|
633
|
-
const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
|
|
634
|
-
if (hasAttachments) {
|
|
635
|
-
const content = [];
|
|
636
|
-
if (msg.content) {
|
|
637
|
-
content.push({ type: "text", text: msg.content });
|
|
638
|
-
}
|
|
639
|
-
for (const attachment of msg.attachments) {
|
|
640
|
-
if (attachment.type === "image") {
|
|
641
|
-
let imageUrl;
|
|
642
|
-
if (attachment.url) {
|
|
643
|
-
imageUrl = attachment.url;
|
|
644
|
-
} else if (attachment.data) {
|
|
645
|
-
imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
|
|
646
|
-
} else {
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
content.push({
|
|
650
|
-
type: "image_url",
|
|
651
|
-
image_url: { url: imageUrl, detail: "auto" }
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
return { ...msg, content, attachments: void 0 };
|
|
656
|
-
}
|
|
657
|
-
return msg;
|
|
658
|
-
});
|
|
952
|
+
const processedMessages = this.formatRawMessages(request.rawMessages);
|
|
659
953
|
if (request.systemPrompt) {
|
|
660
954
|
const hasSystem = processedMessages.some((m) => m.role === "system");
|
|
661
955
|
if (!hasSystem) {
|
|
@@ -675,7 +969,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
675
969
|
request.systemPrompt
|
|
676
970
|
);
|
|
677
971
|
}
|
|
678
|
-
const tools = request.
|
|
972
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
973
|
+
type: "function",
|
|
974
|
+
function: {
|
|
975
|
+
name: tool.name,
|
|
976
|
+
description: tool.description,
|
|
977
|
+
parameters: normalizeObjectJsonSchema(
|
|
978
|
+
tool.inputSchema
|
|
979
|
+
)
|
|
980
|
+
}
|
|
981
|
+
})) : formatTools(request.actions ?? []);
|
|
679
982
|
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
680
983
|
if (webSearchConfig) {
|
|
681
984
|
const webSearchTool = {
|
|
@@ -704,26 +1007,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
704
1007
|
tools: tools.length > 0 ? tools : void 0,
|
|
705
1008
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
706
1009
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
707
|
-
...
|
|
1010
|
+
...buildOpenAIParameters(
|
|
708
1011
|
modelIdForPayload,
|
|
709
1012
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
710
|
-
request.config?.temperature ?? this.config.temperature
|
|
1013
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1014
|
+
this.parameterOptions(request)
|
|
711
1015
|
),
|
|
712
1016
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
713
1017
|
stream: true,
|
|
714
1018
|
stream_options: { include_usage: true }
|
|
715
1019
|
};
|
|
716
1020
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
717
|
-
const stream = await client.chat.completions.create(payload
|
|
718
|
-
|
|
1021
|
+
const stream = await client.chat.completions.create(payload, {
|
|
1022
|
+
signal: request.signal
|
|
1023
|
+
});
|
|
1024
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1025
|
+
let finishReason;
|
|
719
1026
|
const collectedCitations = [];
|
|
720
1027
|
let citationIndex = 0;
|
|
721
1028
|
let usage;
|
|
722
1029
|
for await (const chunk of stream) {
|
|
723
1030
|
logProviderPayload("openai", "stream chunk", chunk, request.debug);
|
|
724
|
-
|
|
725
|
-
break;
|
|
726
|
-
}
|
|
1031
|
+
throwIfAborted(request.signal);
|
|
727
1032
|
const delta = chunk.choices[0]?.delta;
|
|
728
1033
|
const choice = chunk.choices[0];
|
|
729
1034
|
if (delta?.content) {
|
|
@@ -746,38 +1051,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
746
1051
|
}
|
|
747
1052
|
}
|
|
748
1053
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
const tcExtraContent = toolCall.extra_content;
|
|
765
|
-
currentToolCall = {
|
|
766
|
-
id: toolCall.id,
|
|
767
|
-
name: toolCall.function?.name || "",
|
|
768
|
-
arguments: toolCall.function?.arguments || "",
|
|
769
|
-
...tcExtraContent ? { extra_content: tcExtraContent } : {}
|
|
770
|
-
};
|
|
771
|
-
yield {
|
|
772
|
-
type: "action:start",
|
|
773
|
-
id: currentToolCall.id,
|
|
774
|
-
name: currentToolCall.name,
|
|
775
|
-
...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
|
|
776
|
-
};
|
|
777
|
-
} else if (currentToolCall && toolCall.function?.arguments) {
|
|
778
|
-
currentToolCall.arguments += toolCall.function.arguments;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
1054
|
+
for (const part of delta?.tool_calls ?? []) {
|
|
1055
|
+
const index = part.index ?? 0;
|
|
1056
|
+
const call = toolCalls.get(index) ?? {
|
|
1057
|
+
id: "",
|
|
1058
|
+
name: "",
|
|
1059
|
+
arguments: ""
|
|
1060
|
+
};
|
|
1061
|
+
if (part.id) call.id = part.id;
|
|
1062
|
+
if (part.function?.name) call.name += part.function.name;
|
|
1063
|
+
if (part.function?.arguments)
|
|
1064
|
+
call.arguments += part.function.arguments;
|
|
1065
|
+
if (part.extra_content) call.extra_content = part.extra_content;
|
|
1066
|
+
toolCalls.set(index, call);
|
|
781
1067
|
}
|
|
782
1068
|
if (chunk.usage) {
|
|
783
1069
|
usage = {
|
|
@@ -786,21 +1072,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
786
1072
|
total_tokens: chunk.usage.total_tokens
|
|
787
1073
|
};
|
|
788
1074
|
}
|
|
789
|
-
if (choice?.finish_reason)
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
1075
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1076
|
+
}
|
|
1077
|
+
throwIfAborted(request.signal);
|
|
1078
|
+
if (!finishReason)
|
|
1079
|
+
throw new Error("OpenAI stream ended without a finish reason");
|
|
1080
|
+
if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
|
|
1081
|
+
throw new Error("OpenAI stream ended with incomplete tool calls");
|
|
1082
|
+
}
|
|
1083
|
+
for (const call of toolCalls.values()) {
|
|
1084
|
+
if (!call.id || !call.name)
|
|
1085
|
+
throw new Error("OpenAI stream contains an incomplete tool call");
|
|
1086
|
+
JSON.parse(call.arguments || "{}");
|
|
1087
|
+
}
|
|
1088
|
+
for (const call of toolCalls.values()) {
|
|
1089
|
+
yield {
|
|
1090
|
+
type: "action:start",
|
|
1091
|
+
id: call.id,
|
|
1092
|
+
name: call.name,
|
|
1093
|
+
...call.extra_content ? { extra_content: call.extra_content } : {}
|
|
1094
|
+
};
|
|
1095
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
1096
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
804
1097
|
}
|
|
805
1098
|
if (collectedCitations.length > 0) {
|
|
806
1099
|
const uniqueCitations = deduplicateCitations(collectedCitations);
|
|
@@ -809,26 +1102,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
809
1102
|
yield { type: "message:end" };
|
|
810
1103
|
yield { type: "done", usage };
|
|
811
1104
|
} catch (error) {
|
|
812
|
-
yield {
|
|
813
|
-
type: "error",
|
|
814
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
815
|
-
code: `${this.provider.toUpperCase()}_ERROR`
|
|
816
|
-
};
|
|
1105
|
+
yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
|
|
817
1106
|
}
|
|
818
1107
|
}
|
|
819
1108
|
async complete(request) {
|
|
1109
|
+
throwIfAborted(request.signal);
|
|
820
1110
|
if (this.shouldUseResponsesApi(request)) {
|
|
821
1111
|
return this.completeWithResponses(request);
|
|
822
1112
|
}
|
|
823
1113
|
const client = await this.getClient();
|
|
824
1114
|
let messages;
|
|
825
1115
|
if (request.rawMessages && request.rawMessages.length > 0) {
|
|
826
|
-
const sanitized = request.rawMessages
|
|
827
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
|
|
828
|
-
return { ...msg, content: null };
|
|
829
|
-
}
|
|
830
|
-
return msg;
|
|
831
|
-
});
|
|
1116
|
+
const sanitized = this.formatRawMessages(request.rawMessages);
|
|
832
1117
|
if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
|
|
833
1118
|
messages = [
|
|
834
1119
|
{ role: "system", content: request.systemPrompt },
|
|
@@ -843,7 +1128,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
843
1128
|
request.systemPrompt
|
|
844
1129
|
);
|
|
845
1130
|
}
|
|
846
|
-
const tools = request.
|
|
1131
|
+
const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
|
|
1132
|
+
type: "function",
|
|
1133
|
+
function: {
|
|
1134
|
+
name: tool.name,
|
|
1135
|
+
description: tool.description,
|
|
1136
|
+
parameters: normalizeObjectJsonSchema(
|
|
1137
|
+
tool.inputSchema
|
|
1138
|
+
)
|
|
1139
|
+
}
|
|
1140
|
+
})) : formatTools(request.actions ?? []);
|
|
847
1141
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
848
1142
|
const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
|
|
849
1143
|
type: "function",
|
|
@@ -858,16 +1152,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
858
1152
|
tools: tools.length > 0 ? tools : void 0,
|
|
859
1153
|
tool_choice: tools.length > 0 ? toolChoice : void 0,
|
|
860
1154
|
parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
|
|
861
|
-
...
|
|
1155
|
+
...buildOpenAIParameters(
|
|
862
1156
|
modelIdForCompletePayload,
|
|
863
1157
|
request.config?.maxTokens ?? this.config.maxTokens,
|
|
864
|
-
request.config?.temperature ?? this.config.temperature
|
|
1158
|
+
request.config?.temperature ?? this.config.temperature,
|
|
1159
|
+
this.parameterOptions(request)
|
|
865
1160
|
),
|
|
866
1161
|
response_format: toOpenAIResponseFormat(request.config?.responseFormat),
|
|
867
1162
|
stream: false
|
|
868
1163
|
};
|
|
869
1164
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
870
|
-
const response = await client.chat.completions.create(payload
|
|
1165
|
+
const response = await client.chat.completions.create(payload, {
|
|
1166
|
+
signal: request.signal
|
|
1167
|
+
});
|
|
871
1168
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
872
1169
|
const choice = response.choices?.[0];
|
|
873
1170
|
const message = choice?.message;
|