@tanstack/openai-base 0.10.16 → 0.12.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/dist/esm/adapters/chat-completions-text.d.ts +4 -2
- package/dist/esm/adapters/chat-completions-text.js +29 -4
- package/dist/esm/adapters/chat-completions-text.js.map +1 -1
- package/dist/esm/adapters/responses-text.d.ts +18 -6
- package/dist/esm/adapters/responses-text.js +261 -16
- package/dist/esm/adapters/responses-text.js.map +1 -1
- package/dist/esm/adapters/responses-user-tools.d.ts +42 -0
- package/dist/esm/adapters/responses-user-tools.js +241 -0
- package/dist/esm/adapters/responses-user-tools.js.map +1 -0
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/usage.d.ts +2 -2
- package/dist/esm/usage.js +5 -3
- package/dist/esm/usage.js.map +1 -1
- package/dist/esm/utils/schema-converter.d.ts +22 -0
- package/dist/esm/utils/schema-converter.js +41 -8
- package/dist/esm/utils/schema-converter.js.map +1 -1
- package/package.json +4 -3
- package/src/adapters/chat-completions-text.ts +74 -5
- package/src/adapters/responses-text.ts +468 -37
- package/src/adapters/responses-user-tools.ts +355 -0
- package/src/index.ts +2 -0
- package/src/usage.ts +16 -5
- package/src/utils/schema-converter.ts +72 -12
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { makeStructuredOutputCompatibleWithMap } from "../utils/schema-converter.js";
|
|
1
|
+
import { makeStructuredOutputCompatibleWithMap, warnStrictFallback } from "../utils/schema-converter.js";
|
|
2
2
|
import { buildResponsesUsage } from "../usage.js";
|
|
3
3
|
import { extractRequestOptions } from "../utils/request-options.js";
|
|
4
4
|
import { createToolInputNormalizer } from "../utils/tool-input-normalizer.js";
|
|
5
5
|
import { convertToolsToResponsesFormat } from "./responses-tool-converter.js";
|
|
6
|
-
import {
|
|
6
|
+
import { hostedShellCallIds, readUserExecutedCall, userToolRequestItem, userToolResultItem } from "./responses-user-tools.js";
|
|
7
|
+
import { EventType, fileReferenceFor, isFileSource, normalizeSystemPrompts, unsupportedFileSourceError } from "@tanstack/ai";
|
|
7
8
|
import { toRunErrorPayload, toRunErrorRawEvent } from "@tanstack/ai/adapter-internals";
|
|
8
9
|
import { BaseTextAdapter } from "@tanstack/ai/adapters";
|
|
9
10
|
import { generateId } from "@tanstack/ai-utils";
|
|
@@ -12,6 +13,40 @@ var PDF_BASE64_MAGIC = "JVBERi";
|
|
|
12
13
|
function isRecord(value) {
|
|
13
14
|
return typeof value === "object" && value !== null;
|
|
14
15
|
}
|
|
16
|
+
function readURLCitation(value) {
|
|
17
|
+
if (!isRecord(value) || value.type !== "url_citation") return void 0;
|
|
18
|
+
if (typeof value.url !== "string" || typeof value.title !== "string" || typeof value.start_index !== "number" || typeof value.end_index !== "number") return;
|
|
19
|
+
return {
|
|
20
|
+
type: "url_citation",
|
|
21
|
+
url: value.url,
|
|
22
|
+
title: value.title,
|
|
23
|
+
start_index: value.start_index,
|
|
24
|
+
end_index: value.end_index
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function readWebSearchCall(value) {
|
|
28
|
+
if (!isRecord(value) || value.type !== "web_search_call") return void 0;
|
|
29
|
+
if (typeof value.id !== "string" || typeof value.status !== "string" || !isRecord(value.action) || typeof value.action.type !== "string") return;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function collectWebSearchSources(item, citations) {
|
|
33
|
+
const sources = /* @__PURE__ */ new Map();
|
|
34
|
+
const add = (url, title) => {
|
|
35
|
+
if (typeof url !== "string" || url.length === 0) return;
|
|
36
|
+
const existing = sources.get(url);
|
|
37
|
+
if (existing) {
|
|
38
|
+
if (!existing.title && typeof title === "string" && title.length > 0) existing.title = title;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
sources.set(url, {
|
|
42
|
+
url,
|
|
43
|
+
...typeof title === "string" && title.length > 0 ? { title } : {}
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
if (item.action.type === "search") for (const source of item.action.sources ?? []) add(source.url);
|
|
47
|
+
for (const citation of citations) add(citation.url, citation.title);
|
|
48
|
+
return [...sources.values()];
|
|
49
|
+
}
|
|
15
50
|
function packResponsesReasoningSignature(id, encryptedContent) {
|
|
16
51
|
if (!id && !encryptedContent) return void 0;
|
|
17
52
|
return JSON.stringify({
|
|
@@ -54,10 +89,13 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
54
89
|
kind = "text";
|
|
55
90
|
name;
|
|
56
91
|
client;
|
|
57
|
-
|
|
92
|
+
/** See {@link OpenAIBaseTextAdapterOptions.strictFallbackWarning}. */
|
|
93
|
+
strictFallbackWarning;
|
|
94
|
+
constructor(model, name, client, options = {}) {
|
|
58
95
|
super({}, model);
|
|
59
96
|
this.name = name;
|
|
60
97
|
this.client = client;
|
|
98
|
+
this.strictFallbackWarning = options.strictFallbackWarning ?? true;
|
|
61
99
|
}
|
|
62
100
|
async *chatStream(options) {
|
|
63
101
|
const toolCallMetadata = /* @__PURE__ */ new Map();
|
|
@@ -197,6 +235,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
197
235
|
let hasClosedReasoning = false;
|
|
198
236
|
let model = chatOptions.model;
|
|
199
237
|
let usage;
|
|
238
|
+
let responseCompleted = false;
|
|
200
239
|
const closeReasoning = function* () {
|
|
201
240
|
if (reasoningMessageId && !hasClosedReasoning) {
|
|
202
241
|
hasClosedReasoning = true;
|
|
@@ -347,10 +386,11 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
347
386
|
continue;
|
|
348
387
|
}
|
|
349
388
|
if (chunk.type === "response.completed") {
|
|
389
|
+
responseCompleted = true;
|
|
350
390
|
const response = chunk.response;
|
|
351
391
|
if (response.usage) usage = response.usage;
|
|
352
392
|
if (response.model) model = response.model;
|
|
353
|
-
|
|
393
|
+
break;
|
|
354
394
|
}
|
|
355
395
|
if (chunk.type === "response.failed") {
|
|
356
396
|
const response = chunk.response;
|
|
@@ -378,6 +418,22 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
378
418
|
model,
|
|
379
419
|
timestamp: Date.now()
|
|
380
420
|
};
|
|
421
|
+
if (!responseCompleted) {
|
|
422
|
+
const message = "Response stream ended before response.completed";
|
|
423
|
+
yield {
|
|
424
|
+
type: EventType.RUN_ERROR,
|
|
425
|
+
runId: aguiState.runId,
|
|
426
|
+
model,
|
|
427
|
+
timestamp: Date.now(),
|
|
428
|
+
message,
|
|
429
|
+
code: "incomplete-stream",
|
|
430
|
+
error: {
|
|
431
|
+
message,
|
|
432
|
+
code: "incomplete-stream"
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
381
437
|
if (accumulatedContent.length === 0) {
|
|
382
438
|
yield {
|
|
383
439
|
type: EventType.RUN_ERROR,
|
|
@@ -566,8 +622,61 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
566
622
|
let closedReasoningStepId;
|
|
567
623
|
let hasClosedReasoning = false;
|
|
568
624
|
let runFinishedEmitted = false;
|
|
625
|
+
const providerWebSearchCalls = /* @__PURE__ */ new Map();
|
|
626
|
+
const webSearchCitations = [];
|
|
569
627
|
const adapterName = this.name;
|
|
570
628
|
const emitModel = () => model || options.model;
|
|
629
|
+
const recordProviderWebSearchCall = (value, index) => {
|
|
630
|
+
const item = readWebSearchCall(value);
|
|
631
|
+
if (!item) return;
|
|
632
|
+
const existing = providerWebSearchCalls.get(item.id);
|
|
633
|
+
if (existing) {
|
|
634
|
+
existing.item = item;
|
|
635
|
+
existing.index = index;
|
|
636
|
+
} else providerWebSearchCalls.set(item.id, {
|
|
637
|
+
item,
|
|
638
|
+
index,
|
|
639
|
+
started: false
|
|
640
|
+
});
|
|
641
|
+
};
|
|
642
|
+
const emitProviderWebSearchCalls = function* (assistantMessage, completedOnly = false) {
|
|
643
|
+
for (const entry of providerWebSearchCalls.values()) {
|
|
644
|
+
if (entry.started || completedOnly && entry.item.status !== "completed") continue;
|
|
645
|
+
const callUrls = new Set(entry.item.action.type === "search" ? (entry.item.action.sources ?? []).map((source) => source.url) : []);
|
|
646
|
+
const citations = webSearchCitations.filter((citation) => callUrls.has(citation.url));
|
|
647
|
+
const metadata = {
|
|
648
|
+
itemId: entry.item.id,
|
|
649
|
+
providerExecuted: true,
|
|
650
|
+
sources: collectWebSearchSources(entry.item, citations),
|
|
651
|
+
openai: {
|
|
652
|
+
webSearchCall: entry.item,
|
|
653
|
+
urlCitations: citations,
|
|
654
|
+
...assistantMessage ? { assistantMessage } : {}
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
entry.started = true;
|
|
658
|
+
yield {
|
|
659
|
+
type: EventType.TOOL_CALL_START,
|
|
660
|
+
toolCallId: entry.item.id,
|
|
661
|
+
toolCallName: "web_search",
|
|
662
|
+
toolName: "web_search",
|
|
663
|
+
parentMessageId: aguiState.messageId,
|
|
664
|
+
model: emitModel(),
|
|
665
|
+
timestamp: Date.now(),
|
|
666
|
+
index: entry.index,
|
|
667
|
+
metadata
|
|
668
|
+
};
|
|
669
|
+
yield {
|
|
670
|
+
type: EventType.TOOL_CALL_END,
|
|
671
|
+
toolCallId: entry.item.id,
|
|
672
|
+
toolCallName: "web_search",
|
|
673
|
+
toolName: "web_search",
|
|
674
|
+
model: emitModel(),
|
|
675
|
+
timestamp: Date.now(),
|
|
676
|
+
input: entry.item.action
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
};
|
|
571
680
|
const openReasoning = function* () {
|
|
572
681
|
if (reasoningMessageId) return;
|
|
573
682
|
reasoningMessageId = generateId(adapterName);
|
|
@@ -639,6 +748,46 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
639
748
|
hasClosedReasoning = false;
|
|
640
749
|
accumulatedReasoning = "";
|
|
641
750
|
};
|
|
751
|
+
const userToolChunks = (item, outputIndex, bareShell) => {
|
|
752
|
+
const call = readUserExecutedCall(item, { bareShell });
|
|
753
|
+
if (!call || toolCallMetadata.get(call.callId)?.ended) return [];
|
|
754
|
+
const tracked = toolCallMetadata.get(call.callId) ?? {
|
|
755
|
+
callId: call.callId,
|
|
756
|
+
index: outputIndex,
|
|
757
|
+
name: call.name,
|
|
758
|
+
started: false
|
|
759
|
+
};
|
|
760
|
+
toolCallMetadata.set(call.callId, tracked);
|
|
761
|
+
tracked.started = true;
|
|
762
|
+
tracked.ended = true;
|
|
763
|
+
tracked.name = call.name;
|
|
764
|
+
const timestamp = Date.now();
|
|
765
|
+
const modelName = model || options.model;
|
|
766
|
+
const metadata = {
|
|
767
|
+
openaiUserTool: call.name,
|
|
768
|
+
...call.itemId ? { itemId: call.itemId } : {},
|
|
769
|
+
...call.maxOutputLength !== void 0 ? { maxOutputLength: call.maxOutputLength } : {}
|
|
770
|
+
};
|
|
771
|
+
return [{
|
|
772
|
+
type: EventType.TOOL_CALL_START,
|
|
773
|
+
toolCallId: call.callId,
|
|
774
|
+
toolCallName: call.name,
|
|
775
|
+
toolName: call.name,
|
|
776
|
+
parentMessageId: aguiState.messageId,
|
|
777
|
+
model: modelName,
|
|
778
|
+
timestamp,
|
|
779
|
+
index: outputIndex,
|
|
780
|
+
metadata
|
|
781
|
+
}, {
|
|
782
|
+
type: EventType.TOOL_CALL_END,
|
|
783
|
+
toolCallId: call.callId,
|
|
784
|
+
toolCallName: call.name,
|
|
785
|
+
toolName: call.name,
|
|
786
|
+
model: modelName,
|
|
787
|
+
timestamp,
|
|
788
|
+
input: call.input
|
|
789
|
+
}];
|
|
790
|
+
};
|
|
642
791
|
const emitReasoningDelta = function* (text) {
|
|
643
792
|
if (!text) return;
|
|
644
793
|
yield* openReasoning();
|
|
@@ -709,6 +858,10 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
709
858
|
accumulatedReasoning = "";
|
|
710
859
|
}
|
|
711
860
|
if (chunk.type === "response.failed" || chunk.type === "response.incomplete") {
|
|
861
|
+
if (chunk.type === "response.incomplete") {
|
|
862
|
+
for (const [index, item] of (chunk.response.output ?? []).entries()) recordProviderWebSearchCall(item, index);
|
|
863
|
+
yield* emitProviderWebSearchCalls(void 0, true);
|
|
864
|
+
}
|
|
712
865
|
yield* closeReasoning();
|
|
713
866
|
if (hasEmittedTextMessageStart) {
|
|
714
867
|
yield {
|
|
@@ -817,6 +970,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
817
970
|
}
|
|
818
971
|
if (chunk.type === "response.output_item.added") {
|
|
819
972
|
const item = chunk.item;
|
|
973
|
+
if (item.type === "web_search_call") recordProviderWebSearchCall(item, chunk.output_index);
|
|
820
974
|
if (item.type === "reasoning") {
|
|
821
975
|
captureReasoningItem(item);
|
|
822
976
|
yield* openReasoning();
|
|
@@ -850,6 +1004,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
850
1004
|
metadata.started = true;
|
|
851
1005
|
}
|
|
852
1006
|
}
|
|
1007
|
+
yield* userToolChunks(item, chunk.output_index, false);
|
|
853
1008
|
}
|
|
854
1009
|
if (chunk.type === "response.function_call_arguments.delta" && chunk.delta) {
|
|
855
1010
|
const metadata = toolCallMetadata.get(chunk.item_id);
|
|
@@ -912,6 +1067,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
912
1067
|
}
|
|
913
1068
|
if (chunk.type === "response.output_item.done") {
|
|
914
1069
|
const item = chunk.item;
|
|
1070
|
+
if (item.type === "web_search_call") recordProviderWebSearchCall(item, chunk.output_index);
|
|
915
1071
|
if (item.type === "reasoning") {
|
|
916
1072
|
captureReasoningItem(item);
|
|
917
1073
|
yield* openReasoning();
|
|
@@ -973,9 +1129,17 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
973
1129
|
metadata.pendingArguments = void 0;
|
|
974
1130
|
}
|
|
975
1131
|
}
|
|
1132
|
+
yield* userToolChunks(item, chunk.output_index, false);
|
|
1133
|
+
}
|
|
1134
|
+
if (chunk.type === "response.output_text.annotation.added") {
|
|
1135
|
+
const citation = readURLCitation(chunk.annotation);
|
|
1136
|
+
if (citation) webSearchCitations.push(citation);
|
|
976
1137
|
}
|
|
977
1138
|
if (chunk.type === "response.completed") {
|
|
978
|
-
const
|
|
1139
|
+
const responseOutput = Array.isArray(chunk.response.output) ? chunk.response.output : [];
|
|
1140
|
+
const assistantMessage = responseOutput.find((item) => item.type === "message");
|
|
1141
|
+
for (const [index, item] of responseOutput.entries()) if (item.type === "web_search_call") recordProviderWebSearchCall(item, index);
|
|
1142
|
+
const completedText = responseOutput.flatMap((item) => item.type === "message" ? item.content : []).filter((part) => part.type === "output_text").map((part) => part.text).join("");
|
|
979
1143
|
if (accumulatedContent.length === 0 && completedText.length > 0) {
|
|
980
1144
|
if (!hasEmittedTextMessageStart) {
|
|
981
1145
|
hasEmittedTextMessageStart = true;
|
|
@@ -998,7 +1162,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
998
1162
|
content: accumulatedContent
|
|
999
1163
|
};
|
|
1000
1164
|
}
|
|
1001
|
-
|
|
1165
|
+
for (const item of responseOutput) captureReasoningItem(item);
|
|
1002
1166
|
if (!reasoningMessageId && (reasoningItemId || reasoningEncryptedContent)) {
|
|
1003
1167
|
const signature = packResponsesReasoningSignature(reasoningItemId, reasoningEncryptedContent);
|
|
1004
1168
|
if (closedReasoningStepId && signature) yield {
|
|
@@ -1012,11 +1176,11 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1012
1176
|
};
|
|
1013
1177
|
else if (!closedReasoningStepId) yield* openReasoning();
|
|
1014
1178
|
}
|
|
1015
|
-
for (const item of
|
|
1179
|
+
for (const [outputIndex, item] of responseOutput.entries()) {
|
|
1016
1180
|
if (item.type !== "function_call" || !item.id) continue;
|
|
1017
1181
|
const metadata = toolCallMetadata.get(item.id) ?? {
|
|
1018
1182
|
callId: item.call_id || item.id,
|
|
1019
|
-
index:
|
|
1183
|
+
index: outputIndex,
|
|
1020
1184
|
name: item.name || "",
|
|
1021
1185
|
started: false
|
|
1022
1186
|
};
|
|
@@ -1070,6 +1234,12 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1070
1234
|
metadata.pendingArguments = void 0;
|
|
1071
1235
|
}
|
|
1072
1236
|
}
|
|
1237
|
+
yield* emitProviderWebSearchCalls(assistantMessage);
|
|
1238
|
+
const shellOutputs = hostedShellCallIds(responseOutput);
|
|
1239
|
+
for (const [outputIndex, item] of responseOutput.entries()) {
|
|
1240
|
+
if (isRecord(item) && item.type === "shell_call" && typeof item.call_id === "string" && shellOutputs.has(item.call_id)) continue;
|
|
1241
|
+
yield* userToolChunks(item, outputIndex, true);
|
|
1242
|
+
}
|
|
1073
1243
|
yield* closeReasoning();
|
|
1074
1244
|
if (hasEmittedTextMessageStart) {
|
|
1075
1245
|
yield {
|
|
@@ -1080,7 +1250,12 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1080
1250
|
};
|
|
1081
1251
|
hasEmittedTextMessageStart = false;
|
|
1082
1252
|
}
|
|
1083
|
-
const hasFunctionCalls =
|
|
1253
|
+
const hasFunctionCalls = responseOutput.some((item) => {
|
|
1254
|
+
if (!isRecord(item)) return false;
|
|
1255
|
+
if (item.type === "function_call") return true;
|
|
1256
|
+
if (item.type === "shell_call" && typeof item.call_id === "string" && shellOutputs.has(item.call_id)) return false;
|
|
1257
|
+
return readUserExecutedCall(item, { bareShell: true }) !== null;
|
|
1258
|
+
});
|
|
1084
1259
|
const incompleteReason = chunk.response.incomplete_details?.reason;
|
|
1085
1260
|
const finishReason = hasFunctionCalls ? "tool_calls" : incompleteReason === "max_output_tokens" ? "length" : incompleteReason === "content_filter" ? "content_filter" : "stop";
|
|
1086
1261
|
yield {
|
|
@@ -1093,6 +1268,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1093
1268
|
finishReason
|
|
1094
1269
|
};
|
|
1095
1270
|
runFinishedEmitted = true;
|
|
1271
|
+
return;
|
|
1096
1272
|
}
|
|
1097
1273
|
if (chunk.type === "error") {
|
|
1098
1274
|
const code = chunk.code ?? void 0;
|
|
@@ -1112,6 +1288,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1112
1288
|
}
|
|
1113
1289
|
}
|
|
1114
1290
|
if (!runFinishedEmitted && aguiState.hasEmittedRunStarted) {
|
|
1291
|
+
yield* emitProviderWebSearchCalls(void 0, true);
|
|
1115
1292
|
yield* closeReasoning();
|
|
1116
1293
|
if (hasEmittedTextMessageStart) yield {
|
|
1117
1294
|
type: EventType.TEXT_MESSAGE_END,
|
|
@@ -1119,13 +1296,17 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1119
1296
|
model: model || options.model,
|
|
1120
1297
|
timestamp: Date.now()
|
|
1121
1298
|
};
|
|
1299
|
+
const message = "Response stream ended before response.completed";
|
|
1122
1300
|
yield {
|
|
1123
|
-
type: EventType.
|
|
1124
|
-
runId: aguiState.runId,
|
|
1125
|
-
threadId: aguiState.threadId,
|
|
1301
|
+
type: EventType.RUN_ERROR,
|
|
1126
1302
|
model: model || options.model,
|
|
1127
1303
|
timestamp: Date.now(),
|
|
1128
|
-
|
|
1304
|
+
message,
|
|
1305
|
+
code: "incomplete-stream",
|
|
1306
|
+
error: {
|
|
1307
|
+
message,
|
|
1308
|
+
code: "incomplete-stream"
|
|
1309
|
+
}
|
|
1129
1310
|
};
|
|
1130
1311
|
}
|
|
1131
1312
|
} catch (error) {
|
|
@@ -1155,6 +1336,7 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1155
1336
|
*/
|
|
1156
1337
|
mapOptionsToRequest(options) {
|
|
1157
1338
|
const input = this.convertMessagesToInput(options.messages);
|
|
1339
|
+
if (this.strictFallbackWarning) warnStrictFallback(options.tools, options.logger);
|
|
1158
1340
|
const tools = options.tools ? convertToolsToResponsesFormat(options.tools, this.makeStructuredOutputCompatible.bind(this)) : void 0;
|
|
1159
1341
|
const modelOptions = options.modelOptions;
|
|
1160
1342
|
const combinedSchema = options.outputSchema;
|
|
@@ -1198,8 +1380,16 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1198
1380
|
*/
|
|
1199
1381
|
convertMessagesToInput(messages) {
|
|
1200
1382
|
const result = [];
|
|
1383
|
+
const seenReasoningIds = /* @__PURE__ */ new Set();
|
|
1384
|
+
const userToolCalls = /* @__PURE__ */ new Map();
|
|
1201
1385
|
for (const message of messages) {
|
|
1202
1386
|
if (message.role === "tool") {
|
|
1387
|
+
const owner = message.toolCallId ? userToolCalls.get(message.toolCallId) : void 0;
|
|
1388
|
+
const userOutput = owner ? userToolResultItem(owner, message.content) : null;
|
|
1389
|
+
if (userOutput) {
|
|
1390
|
+
result.push(userOutput);
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1203
1393
|
const toolContent = message.content;
|
|
1204
1394
|
const output = Array.isArray(toolContent) ? toolContent.map((part) => this.convertContentPartToInput(part)) : typeof toolContent === "string" ? toolContent : JSON.stringify(toolContent);
|
|
1205
1395
|
result.push({
|
|
@@ -1210,10 +1400,16 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1210
1400
|
continue;
|
|
1211
1401
|
}
|
|
1212
1402
|
if (message.role === "assistant") {
|
|
1403
|
+
let reasoningCandidates = 0;
|
|
1404
|
+
let emittedReasoning = 0;
|
|
1213
1405
|
if (message.thinking) for (const thinking of message.thinking) {
|
|
1214
1406
|
if (!thinking.signature) continue;
|
|
1215
1407
|
const packed = unpackResponsesReasoningSignature(thinking.signature);
|
|
1216
1408
|
if (!packed?.id) continue;
|
|
1409
|
+
reasoningCandidates++;
|
|
1410
|
+
if (seenReasoningIds.has(packed.id)) continue;
|
|
1411
|
+
seenReasoningIds.add(packed.id);
|
|
1412
|
+
emittedReasoning++;
|
|
1217
1413
|
result.push({
|
|
1218
1414
|
type: "reasoning",
|
|
1219
1415
|
id: packed.id,
|
|
@@ -1224,18 +1420,44 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1224
1420
|
}] : []
|
|
1225
1421
|
});
|
|
1226
1422
|
}
|
|
1423
|
+
const canPairReasoning = reasoningCandidates === 0 || reasoningCandidates === 1 && emittedReasoning === 1;
|
|
1424
|
+
let rawAssistantMessage;
|
|
1227
1425
|
if (message.toolCalls && message.toolCalls.length > 0) for (const toolCall of message.toolCalls) {
|
|
1426
|
+
const metadata = toolCall.metadata;
|
|
1427
|
+
if (metadata?.providerExecuted) {
|
|
1428
|
+
const webSearchCall = metadata.openai?.webSearchCall;
|
|
1429
|
+
if (webSearchCall && canPairReasoning) {
|
|
1430
|
+
result.push(webSearchCall);
|
|
1431
|
+
rawAssistantMessage ??= metadata.openai?.assistantMessage;
|
|
1432
|
+
}
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1228
1435
|
const argumentsString = typeof toolCall.function.arguments === "string" ? toolCall.function.arguments : JSON.stringify(toolCall.function.arguments);
|
|
1229
|
-
const
|
|
1436
|
+
const replayCall = {
|
|
1437
|
+
id: toolCall.id,
|
|
1438
|
+
function: {
|
|
1439
|
+
name: toolCall.function.name,
|
|
1440
|
+
arguments: argumentsString
|
|
1441
|
+
},
|
|
1442
|
+
...toolCall.metadata !== void 0 ? { metadata: toolCall.metadata } : {}
|
|
1443
|
+
};
|
|
1444
|
+
const userItem = userToolRequestItem(replayCall);
|
|
1445
|
+
if (userItem) {
|
|
1446
|
+
userToolCalls.set(toolCall.id, replayCall);
|
|
1447
|
+
result.push(userItem);
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
const itemId = metadata?.itemId;
|
|
1230
1451
|
result.push({
|
|
1231
1452
|
type: "function_call",
|
|
1232
1453
|
call_id: toolCall.id,
|
|
1233
|
-
...itemId && { id: itemId },
|
|
1454
|
+
...itemId && canPairReasoning && { id: itemId },
|
|
1234
1455
|
name: toolCall.function.name,
|
|
1235
1456
|
arguments: argumentsString
|
|
1236
1457
|
});
|
|
1237
1458
|
}
|
|
1238
|
-
if (
|
|
1459
|
+
if (rawAssistantMessage) result.push(rawAssistantMessage);
|
|
1460
|
+
else if (message.content) {
|
|
1239
1461
|
const contentStr = this.extractTextContent(message.content);
|
|
1240
1462
|
if (contentStr) result.push({
|
|
1241
1463
|
type: "message",
|
|
@@ -1270,6 +1492,14 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1270
1492
|
};
|
|
1271
1493
|
case "image": {
|
|
1272
1494
|
const imageMetadata = part.metadata;
|
|
1495
|
+
if (isFileSource(part.source)) {
|
|
1496
|
+
if (this.supportsFileSources !== true) throw unsupportedFileSourceError(this.name);
|
|
1497
|
+
return {
|
|
1498
|
+
type: "input_image",
|
|
1499
|
+
file_id: fileReferenceFor(part.source, this.name),
|
|
1500
|
+
detail: imageMetadata?.detail || "auto"
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1273
1503
|
if (part.source.type === "url") return {
|
|
1274
1504
|
type: "input_image",
|
|
1275
1505
|
image_url: part.source.value,
|
|
@@ -1284,6 +1514,13 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1284
1514
|
};
|
|
1285
1515
|
}
|
|
1286
1516
|
case "audio": {
|
|
1517
|
+
if (isFileSource(part.source)) {
|
|
1518
|
+
if (this.supportsFileSources !== true) throw unsupportedFileSourceError(this.name);
|
|
1519
|
+
return {
|
|
1520
|
+
type: "input_file",
|
|
1521
|
+
file_id: fileReferenceFor(part.source, this.name)
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1287
1524
|
if (part.source.type === "url") return {
|
|
1288
1525
|
type: "input_file",
|
|
1289
1526
|
file_url: part.source.value
|
|
@@ -1298,6 +1535,14 @@ var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
|
1298
1535
|
case "document": {
|
|
1299
1536
|
const documentMetadata = part.metadata;
|
|
1300
1537
|
const documentDetail = documentMetadata?.detail !== void 0 ? { detail: documentMetadata.detail } : {};
|
|
1538
|
+
if (isFileSource(part.source)) {
|
|
1539
|
+
if (this.supportsFileSources !== true) throw unsupportedFileSourceError(this.name);
|
|
1540
|
+
return {
|
|
1541
|
+
type: "input_file",
|
|
1542
|
+
file_id: fileReferenceFor(part.source, this.name),
|
|
1543
|
+
...documentDetail
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1301
1546
|
if (part.source.type === "url") return {
|
|
1302
1547
|
type: "input_file",
|
|
1303
1548
|
file_url: part.source.value,
|