@cognipeer/agent-sdk 0.10.5 → 0.12.0
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 +28 -3
- package/dist/index.cjs +1785 -501
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1073 -661
- package/dist/index.d.ts +1073 -661
- package/dist/index.mjs +1778 -501
- package/dist/index.mjs.map +1 -1
- package/package.json +39 -1
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,238 @@ var __export = (target, all) => {
|
|
|
28
28
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
// src/utils/content.ts
|
|
32
|
+
function isRecord(value) {
|
|
33
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
function nonEmpty(value) {
|
|
36
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
37
|
+
}
|
|
38
|
+
function kindFromMediaType(mediaType) {
|
|
39
|
+
const top = mediaType?.split("/")[0];
|
|
40
|
+
return top === "image" || top === "audio" || top === "video" ? top : "file";
|
|
41
|
+
}
|
|
42
|
+
function inlineDataOf(part) {
|
|
43
|
+
const inline = part.inlineData ?? part.inline_data ?? part.fileData ?? part.file_data;
|
|
44
|
+
return isRecord(inline) ? inline : void 0;
|
|
45
|
+
}
|
|
46
|
+
function mediaKindOf(part) {
|
|
47
|
+
if (!isRecord(part)) return void 0;
|
|
48
|
+
const type = nonEmpty(part.type);
|
|
49
|
+
if (type) return MEDIA_PART_KINDS[type];
|
|
50
|
+
const inline = inlineDataOf(part);
|
|
51
|
+
if (inline) return kindFromMediaType(nonEmpty(inline.mimeType) ?? nonEmpty(inline.mime_type));
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
function hasMediaPart(content) {
|
|
55
|
+
return Array.isArray(content) && content.some((part) => mediaKindOf(part) !== void 0);
|
|
56
|
+
}
|
|
57
|
+
function urlOfPart(part) {
|
|
58
|
+
const image = part.image_url;
|
|
59
|
+
const video = part.video_url;
|
|
60
|
+
return nonEmpty(typeof image === "string" ? image : image?.url) ?? nonEmpty(typeof video === "string" ? video : video?.url) ?? nonEmpty(part.url) ?? nonEmpty(part.source?.url) ?? nonEmpty(inlineDataOf(part)?.fileUri) ?? nonEmpty(inlineDataOf(part)?.file_uri);
|
|
61
|
+
}
|
|
62
|
+
function mediaTypeOfPart(part) {
|
|
63
|
+
const image = isRecord(part.image_url) ? part.image_url : void 0;
|
|
64
|
+
const source = isRecord(part.source) ? part.source : void 0;
|
|
65
|
+
const inline = inlineDataOf(part);
|
|
66
|
+
const audioFormat = nonEmpty(part.input_audio?.format);
|
|
67
|
+
const fromDataUrl = /^data:([^;,]+)/i.exec(urlOfPart(part) ?? "")?.[1];
|
|
68
|
+
return nonEmpty(part.mediaType) ?? nonEmpty(part.media_type) ?? nonEmpty(part.mimeType) ?? nonEmpty(part.mime_type) ?? nonEmpty(source?.mediaType) ?? nonEmpty(source?.media_type) ?? nonEmpty(image?.mediaType) ?? nonEmpty(image?.media_type) ?? nonEmpty(inline?.mimeType) ?? nonEmpty(inline?.mime_type) ?? nonEmpty(fromDataUrl) ?? (audioFormat ? `audio/${audioFormat}` : void 0);
|
|
69
|
+
}
|
|
70
|
+
function nameOfPart(part) {
|
|
71
|
+
const file = isRecord(part.file) ? part.file : void 0;
|
|
72
|
+
const image = isRecord(part.image_url) ? part.image_url : void 0;
|
|
73
|
+
const source = isRecord(part.source) ? part.source : void 0;
|
|
74
|
+
return nonEmpty(part.name) ?? nonEmpty(part.fileName) ?? nonEmpty(part.filename) ?? nonEmpty(part.file_name) ?? nonEmpty(file?.filename) ?? nonEmpty(file?.fileName) ?? nonEmpty(image?.name) ?? nonEmpty(source?.fileName) ?? nonEmpty(source?.filename) ?? nonEmpty(part.fileId) ?? nonEmpty(part.file_id) ?? nonEmpty(file?.file_id) ?? nonEmpty(file?.fileId) ?? nonEmpty(image?.fileId) ?? nonEmpty(image?.file_id) ?? nonEmpty(part.image_file?.file_id);
|
|
75
|
+
}
|
|
76
|
+
function labelFromUrl(url) {
|
|
77
|
+
if (!url || url.startsWith("data:")) return void 0;
|
|
78
|
+
try {
|
|
79
|
+
const parsed = new URL(url);
|
|
80
|
+
if (parsed.protocol === "session-file:") return nonEmpty(parsed.hostname) ?? nonEmpty(parsed.pathname.replace(/^\/+/, ""));
|
|
81
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
82
|
+
return nonEmpty(decodeURIComponent(parsed.pathname.split("/").filter(Boolean).pop() ?? ""));
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
function describeContentPart(part) {
|
|
89
|
+
const kind = mediaKindOf(part);
|
|
90
|
+
if (!kind || !isRecord(part)) return void 0;
|
|
91
|
+
const label = nameOfPart(part) ?? mediaTypeOfPart(part) ?? labelFromUrl(urlOfPart(part));
|
|
92
|
+
if (!label) return `[${kind}]`;
|
|
93
|
+
const clean = label.replace(/[\r\n\]]+/g, " ").slice(0, PLACEHOLDER_LABEL_MAX);
|
|
94
|
+
return `[${kind}: ${clean}]`;
|
|
95
|
+
}
|
|
96
|
+
function contentPartToText(part, fallback) {
|
|
97
|
+
if (typeof part === "string") return part;
|
|
98
|
+
if (!isRecord(part)) return part == null ? "" : String(part);
|
|
99
|
+
const media = describeContentPart(part);
|
|
100
|
+
if (media) return media;
|
|
101
|
+
if (typeof part.text === "string") return part.text;
|
|
102
|
+
if (typeof part.content === "string") return part.content;
|
|
103
|
+
if (fallback) return fallback(part);
|
|
104
|
+
try {
|
|
105
|
+
return JSON.stringify(part);
|
|
106
|
+
} catch {
|
|
107
|
+
return String(part);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function contentToString(content) {
|
|
111
|
+
try {
|
|
112
|
+
if (content == null) return "";
|
|
113
|
+
if (typeof content === "string") return content;
|
|
114
|
+
if (Array.isArray(content)) {
|
|
115
|
+
return content.map((p) => {
|
|
116
|
+
if (!p || typeof p !== "object") return String(p);
|
|
117
|
+
if (p.type === "text") return String(p.text ?? "");
|
|
118
|
+
if (p.type === "image_url") {
|
|
119
|
+
const img = p.image_url;
|
|
120
|
+
const url = typeof img === "string" ? img : img?.url;
|
|
121
|
+
if (typeof url === "string" && /^https?:\/\//i.test(url)) {
|
|
122
|
+
const detail = typeof img === "string" ? void 0 : img?.detail;
|
|
123
|
+
return `[image:${url}${detail ? ` detail=${detail}` : ""}]`;
|
|
124
|
+
}
|
|
125
|
+
return describeContentPart(p) ?? "[image]";
|
|
126
|
+
}
|
|
127
|
+
const media = describeContentPart(p);
|
|
128
|
+
if (media) return media;
|
|
129
|
+
return JSON.stringify(p);
|
|
130
|
+
}).join("\n");
|
|
131
|
+
}
|
|
132
|
+
return describeContentPart(content) ?? JSON.stringify(content);
|
|
133
|
+
} catch {
|
|
134
|
+
return String(content);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function mergeContentsToString(contents) {
|
|
138
|
+
return contents.map((c) => contentToString(c)).join("\n");
|
|
139
|
+
}
|
|
140
|
+
function safeStringify(value, pretty = false) {
|
|
141
|
+
if (value == null) return "";
|
|
142
|
+
if (typeof value === "string") return value;
|
|
143
|
+
try {
|
|
144
|
+
return JSON.stringify(value, null, pretty ? 2 : void 0) ?? String(value);
|
|
145
|
+
} catch {
|
|
146
|
+
return String(value);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function extractMessageText(message) {
|
|
150
|
+
if (!message) return "";
|
|
151
|
+
const content = message.content;
|
|
152
|
+
if (typeof content === "string") return content;
|
|
153
|
+
if (Array.isArray(content)) {
|
|
154
|
+
return content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join("");
|
|
155
|
+
}
|
|
156
|
+
return "";
|
|
157
|
+
}
|
|
158
|
+
function textFromContent(content) {
|
|
159
|
+
if (typeof content === "string") return content;
|
|
160
|
+
if (!Array.isArray(content)) return "";
|
|
161
|
+
return content.filter((part) => isTextPart(part)).map((part) => part.text ?? "").join("\n");
|
|
162
|
+
}
|
|
163
|
+
function isTextPart(part) {
|
|
164
|
+
return !!part && typeof part === "object" && part.type === "text";
|
|
165
|
+
}
|
|
166
|
+
function mapTextParts(content, fn) {
|
|
167
|
+
if (typeof content === "string") return fn(content);
|
|
168
|
+
if (!Array.isArray(content)) return content;
|
|
169
|
+
return content.map((part) => isTextPart(part) ? { ...part, text: fn(part.text) } : part);
|
|
170
|
+
}
|
|
171
|
+
function replaceTextContent(content, text) {
|
|
172
|
+
if (typeof content === "string" || content == null) return text;
|
|
173
|
+
if (!Array.isArray(content)) return text;
|
|
174
|
+
const textIndexes = content.reduce((acc, part, index) => {
|
|
175
|
+
if (isTextPart(part)) acc.push(index);
|
|
176
|
+
return acc;
|
|
177
|
+
}, []);
|
|
178
|
+
if (textIndexes.length === 0) {
|
|
179
|
+
return text.length > 0 ? [{ type: "text", text }, ...content] : content;
|
|
180
|
+
}
|
|
181
|
+
const [first, ...rest] = textIndexes;
|
|
182
|
+
const dropped = new Set(rest);
|
|
183
|
+
return content.map((part, index) => index === first ? { ...part, text } : part).filter((_part, index) => !dropped.has(index));
|
|
184
|
+
}
|
|
185
|
+
function normalizeSource(source) {
|
|
186
|
+
if (!source || typeof source !== "object") return {};
|
|
187
|
+
const src = source;
|
|
188
|
+
if (src.type === "base64" && typeof src.data === "string") {
|
|
189
|
+
return {
|
|
190
|
+
sourceType: "base64",
|
|
191
|
+
mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0,
|
|
192
|
+
// base64 encodes 3 bytes per 4 characters; padding makes this an upper bound.
|
|
193
|
+
approxBytes: Math.floor(src.data.length * 3 / 4)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (src.type === "url" && typeof src.url === "string") {
|
|
197
|
+
return {
|
|
198
|
+
sourceType: "url",
|
|
199
|
+
url: src.url,
|
|
200
|
+
mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {};
|
|
204
|
+
}
|
|
205
|
+
function collectAttachments(content) {
|
|
206
|
+
if (!Array.isArray(content)) return [];
|
|
207
|
+
const attachments = [];
|
|
208
|
+
content.forEach((part, index) => {
|
|
209
|
+
if (!part || typeof part !== "object") return;
|
|
210
|
+
const type = part.type;
|
|
211
|
+
if (!type || type === "text") return;
|
|
212
|
+
if (type === "image" || type === "audio" || type === "video" || type === "file") {
|
|
213
|
+
attachments.push({
|
|
214
|
+
index,
|
|
215
|
+
kind: type,
|
|
216
|
+
fileName: typeof part.fileName === "string" ? part.fileName : void 0,
|
|
217
|
+
...normalizeSource(part.source)
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (type === "image_url") {
|
|
222
|
+
const image = part.image_url;
|
|
223
|
+
const url = typeof image === "string" ? image : image?.url;
|
|
224
|
+
if (typeof url === "string" && url.startsWith("data:")) {
|
|
225
|
+
const comma = url.indexOf(",");
|
|
226
|
+
const header = url.slice(5, comma);
|
|
227
|
+
attachments.push({
|
|
228
|
+
index,
|
|
229
|
+
kind: "image",
|
|
230
|
+
sourceType: "base64",
|
|
231
|
+
mediaType: header.split(";")[0] || void 0,
|
|
232
|
+
approxBytes: Math.floor((url.length - comma - 1) * 3 / 4)
|
|
233
|
+
});
|
|
234
|
+
} else {
|
|
235
|
+
attachments.push({ index, kind: "image", sourceType: "url", url });
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
attachments.push({ index, kind: "unknown" });
|
|
240
|
+
});
|
|
241
|
+
return attachments;
|
|
242
|
+
}
|
|
243
|
+
var MEDIA_PART_KINDS, PLACEHOLDER_LABEL_MAX;
|
|
244
|
+
var init_content = __esm({
|
|
245
|
+
"src/utils/content.ts"() {
|
|
246
|
+
MEDIA_PART_KINDS = {
|
|
247
|
+
image: "image",
|
|
248
|
+
image_url: "image",
|
|
249
|
+
input_image: "image",
|
|
250
|
+
image_file: "image",
|
|
251
|
+
audio: "audio",
|
|
252
|
+
input_audio: "audio",
|
|
253
|
+
video: "video",
|
|
254
|
+
video_url: "video",
|
|
255
|
+
file: "file",
|
|
256
|
+
input_file: "file",
|
|
257
|
+
document: "file"
|
|
258
|
+
};
|
|
259
|
+
PLACEHOLDER_LABEL_MAX = 80;
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
31
263
|
// src/utils/utilTokens.ts
|
|
32
264
|
var utilTokens_exports = {};
|
|
33
265
|
__export(utilTokens_exports, {
|
|
@@ -52,7 +284,7 @@ function getTokenCounter() {
|
|
|
52
284
|
function countApproxTokens(text) {
|
|
53
285
|
return activeTokenCounter(text || "");
|
|
54
286
|
}
|
|
55
|
-
function
|
|
287
|
+
function extractMessageText2(m) {
|
|
56
288
|
if (!m) return "";
|
|
57
289
|
const parts = [];
|
|
58
290
|
parts.push(m.role || "");
|
|
@@ -62,6 +294,7 @@ function extractMessageText(m) {
|
|
|
62
294
|
} else if (Array.isArray(m.content)) {
|
|
63
295
|
for (const c of m.content) {
|
|
64
296
|
if (typeof c === "string") parts.push(c);
|
|
297
|
+
else if (describeContentPart(c)) parts.push(describeContentPart(c));
|
|
65
298
|
else if (c?.text) parts.push(c.text);
|
|
66
299
|
else if (c?.content) parts.push(c.content);
|
|
67
300
|
else if (c?.type === "tool_use" && c?.input) {
|
|
@@ -72,7 +305,7 @@ function extractMessageText(m) {
|
|
|
72
305
|
}
|
|
73
306
|
}
|
|
74
307
|
} else if (m.content != null && typeof m.content === "object") {
|
|
75
|
-
parts.push(JSON.stringify(m.content));
|
|
308
|
+
parts.push(describeContentPart(m.content) ?? JSON.stringify(m.content));
|
|
76
309
|
}
|
|
77
310
|
if (Array.isArray(m.tool_calls)) {
|
|
78
311
|
for (const tc of m.tool_calls) {
|
|
@@ -102,12 +335,13 @@ function extractMessageText(m) {
|
|
|
102
335
|
}
|
|
103
336
|
function countMessagesTokens(messages) {
|
|
104
337
|
if (!Array.isArray(messages) || messages.length === 0) return 0;
|
|
105
|
-
const allText = messages.map(
|
|
338
|
+
const allText = messages.map(extractMessageText2).join("\n");
|
|
106
339
|
return countApproxTokens(allText) + messages.length * 4;
|
|
107
340
|
}
|
|
108
341
|
var activeTokenCounter;
|
|
109
342
|
var init_utilTokens = __esm({
|
|
110
343
|
"src/utils/utilTokens.ts"() {
|
|
344
|
+
init_content();
|
|
111
345
|
activeTokenCounter = defaultTokenCounter;
|
|
112
346
|
}
|
|
113
347
|
});
|
|
@@ -568,6 +802,125 @@ function isOpenAIHostedEndpoint(lcModel) {
|
|
|
568
802
|
}
|
|
569
803
|
}
|
|
570
804
|
|
|
805
|
+
// src/utils/abort.ts
|
|
806
|
+
var POLL_INTERVAL_MS = 100;
|
|
807
|
+
var NOOP = () => void 0;
|
|
808
|
+
function isObject(value) {
|
|
809
|
+
return value !== null && typeof value === "object";
|
|
810
|
+
}
|
|
811
|
+
function isRealSignal(value) {
|
|
812
|
+
return typeof AbortSignal !== "undefined" && value instanceof AbortSignal;
|
|
813
|
+
}
|
|
814
|
+
function isCancellationRequested(source) {
|
|
815
|
+
if (!source) return false;
|
|
816
|
+
for (const candidate of [source.signal, source.cancellationToken]) {
|
|
817
|
+
if (!isObject(candidate)) continue;
|
|
818
|
+
if (candidate.aborted === true || candidate.isCancellationRequested === true) return true;
|
|
819
|
+
}
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
function linkAbortSignal(source) {
|
|
823
|
+
if (!source) return { signal: void 0, dispose: NOOP };
|
|
824
|
+
const candidates = [];
|
|
825
|
+
for (const candidate of [source.signal, source.cancellationToken]) {
|
|
826
|
+
if (isObject(candidate) && !candidates.includes(candidate)) candidates.push(candidate);
|
|
827
|
+
}
|
|
828
|
+
if (candidates.length === 0) return { signal: void 0, dispose: NOOP };
|
|
829
|
+
const real = candidates.filter(isRealSignal);
|
|
830
|
+
if (candidates.length === 1 && real.length === 1) {
|
|
831
|
+
return { signal: real[0], dispose: NOOP };
|
|
832
|
+
}
|
|
833
|
+
const controller = new AbortController();
|
|
834
|
+
const cleanups = [];
|
|
835
|
+
let polled = [];
|
|
836
|
+
let timer;
|
|
837
|
+
const fire = (reason) => {
|
|
838
|
+
if (controller.signal.aborted) return;
|
|
839
|
+
try {
|
|
840
|
+
controller.abort(reason);
|
|
841
|
+
} catch {
|
|
842
|
+
controller.abort();
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
const dispose = () => {
|
|
846
|
+
if (timer !== void 0) {
|
|
847
|
+
clearInterval(timer);
|
|
848
|
+
timer = void 0;
|
|
849
|
+
}
|
|
850
|
+
polled = [];
|
|
851
|
+
for (const cleanup of cleanups.splice(0)) {
|
|
852
|
+
try {
|
|
853
|
+
cleanup();
|
|
854
|
+
} catch {
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
for (const candidate of candidates) {
|
|
859
|
+
if (candidate.aborted === true || candidate.isCancellationRequested === true) {
|
|
860
|
+
fire(isRealSignal(candidate) ? candidate.reason : void 0);
|
|
861
|
+
return { signal: controller.signal, dispose };
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
for (const candidate of candidates) {
|
|
865
|
+
if (typeof candidate.addEventListener === "function") {
|
|
866
|
+
const onAbort = () => fire(isRealSignal(candidate) ? candidate.reason : void 0);
|
|
867
|
+
candidate.addEventListener("abort", onAbort, { once: true });
|
|
868
|
+
cleanups.push(() => candidate.removeEventListener?.("abort", onAbort));
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
if (typeof candidate.onCancellationRequested === "function") {
|
|
872
|
+
let handle;
|
|
873
|
+
try {
|
|
874
|
+
handle = candidate.onCancellationRequested(() => fire());
|
|
875
|
+
} catch {
|
|
876
|
+
handle = void 0;
|
|
877
|
+
}
|
|
878
|
+
if (isObject(handle) && typeof handle.dispose === "function") {
|
|
879
|
+
cleanups.push(() => handle.dispose());
|
|
880
|
+
}
|
|
881
|
+
polled.push(candidate);
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
if ("isCancellationRequested" in candidate || "aborted" in candidate) {
|
|
885
|
+
polled.push(candidate);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (polled.length > 0) {
|
|
889
|
+
timer = setInterval(() => {
|
|
890
|
+
for (const candidate of polled) {
|
|
891
|
+
if (candidate.aborted === true || candidate.isCancellationRequested === true) {
|
|
892
|
+
fire();
|
|
893
|
+
dispose();
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}, POLL_INTERVAL_MS);
|
|
898
|
+
timer.unref?.();
|
|
899
|
+
}
|
|
900
|
+
return { signal: controller.signal, dispose };
|
|
901
|
+
}
|
|
902
|
+
function abortableSleep(ms, signal) {
|
|
903
|
+
if (!signal) return new Promise((resolve) => setTimeout(resolve, ms));
|
|
904
|
+
if (signal.aborted) return Promise.reject(abortReason(signal));
|
|
905
|
+
return new Promise((resolve, reject) => {
|
|
906
|
+
const onAbort = () => {
|
|
907
|
+
clearTimeout(timer);
|
|
908
|
+
reject(abortReason(signal));
|
|
909
|
+
};
|
|
910
|
+
const timer = setTimeout(() => {
|
|
911
|
+
signal.removeEventListener("abort", onAbort);
|
|
912
|
+
resolve();
|
|
913
|
+
}, ms);
|
|
914
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
function abortReason(signal) {
|
|
918
|
+
if (signal.reason !== void 0) return signal.reason;
|
|
919
|
+
const error = new Error("This operation was aborted");
|
|
920
|
+
error.name = "AbortError";
|
|
921
|
+
return error;
|
|
922
|
+
}
|
|
923
|
+
|
|
571
924
|
// src/model.ts
|
|
572
925
|
function isSmartModel(m) {
|
|
573
926
|
return !!m && typeof m === "object" && typeof m.invoke === "function";
|
|
@@ -576,6 +929,10 @@ function withTools(model, tools, options) {
|
|
|
576
929
|
if (model?.bindTools) return model.bindTools(tools, options);
|
|
577
930
|
return model;
|
|
578
931
|
}
|
|
932
|
+
function withLinkedSignal(options, signal) {
|
|
933
|
+
if (!signal || options?.signal === signal) return options;
|
|
934
|
+
return { ...options || {}, signal };
|
|
935
|
+
}
|
|
579
936
|
function fromLangchainModel(lcModel) {
|
|
580
937
|
if (!lcModel) throw new Error("fromLangchainModel: model is undefined/null");
|
|
581
938
|
const normalizeContent = (content) => {
|
|
@@ -611,7 +968,13 @@ function fromLangchainModel(lcModel) {
|
|
|
611
968
|
const adapted = {
|
|
612
969
|
invoke: async (messages, options) => {
|
|
613
970
|
const lcMessages = messages.map(toLC);
|
|
614
|
-
const
|
|
971
|
+
const link = linkAbortSignal(options);
|
|
972
|
+
let response;
|
|
973
|
+
try {
|
|
974
|
+
response = await lcModel.invoke(lcMessages, withLinkedSignal(options, link.signal));
|
|
975
|
+
} finally {
|
|
976
|
+
link.dispose();
|
|
977
|
+
}
|
|
615
978
|
if (response && typeof response === "object") {
|
|
616
979
|
const content = response.content ?? response.text ?? "";
|
|
617
980
|
return {
|
|
@@ -629,10 +992,15 @@ function fromLangchainModel(lcModel) {
|
|
|
629
992
|
stream: async function* (messages, options) {
|
|
630
993
|
if (typeof lcModel.stream !== "function") return;
|
|
631
994
|
const lcMessages = messages.map(toLC);
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
995
|
+
const link = linkAbortSignal(options);
|
|
996
|
+
try {
|
|
997
|
+
const streamResult = lcModel.stream(lcMessages, withLinkedSignal(options, link.signal));
|
|
998
|
+
const stream = typeof streamResult?.then === "function" ? await streamResult : streamResult;
|
|
999
|
+
for await (const chunk of stream) {
|
|
1000
|
+
yield chunk;
|
|
1001
|
+
}
|
|
1002
|
+
} finally {
|
|
1003
|
+
link.dispose();
|
|
636
1004
|
}
|
|
637
1005
|
},
|
|
638
1006
|
bindTools: (tools, options) => {
|
|
@@ -811,6 +1179,9 @@ function safe(v) {
|
|
|
811
1179
|
const n = Number(v);
|
|
812
1180
|
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
813
1181
|
}
|
|
1182
|
+
|
|
1183
|
+
// src/utils/tracing.ts
|
|
1184
|
+
init_content();
|
|
814
1185
|
var DEFAULT_COGNIPEER_URL = "https://console.cognipeer.com/api/client/v1/tracing/sessions";
|
|
815
1186
|
function fileSink(path2) {
|
|
816
1187
|
return { type: "file", path: path2 };
|
|
@@ -1555,6 +1926,12 @@ function buildMessageLabel(role, index) {
|
|
|
1555
1926
|
const base = `${normalized.charAt(0).toUpperCase()}${normalized.slice(1)} Message`;
|
|
1556
1927
|
return index > 1 ? `${base} #${index}` : base;
|
|
1557
1928
|
}
|
|
1929
|
+
function structuredMessageParts(content) {
|
|
1930
|
+
if (!hasMediaPart(content)) return void 0;
|
|
1931
|
+
const cloned = sanitizeTracePayload(content);
|
|
1932
|
+
if (!Array.isArray(cloned)) return void 0;
|
|
1933
|
+
return cloned.map((part) => typeof part === "string" ? { type: "text", text: part } : part);
|
|
1934
|
+
}
|
|
1558
1935
|
function toDisplayString(value) {
|
|
1559
1936
|
const sanitized = sanitizeTracePayload(value);
|
|
1560
1937
|
if (sanitized == null) return "";
|
|
@@ -1563,7 +1940,9 @@ function toDisplayString(value) {
|
|
|
1563
1940
|
return sanitized.map((item) => toDisplayString(item)).filter((part) => part && part.trim().length > 0).join("\n");
|
|
1564
1941
|
}
|
|
1565
1942
|
if (typeof sanitized === "object") {
|
|
1566
|
-
|
|
1943
|
+
const media = describeContentPart(sanitized);
|
|
1944
|
+
if (media) return media;
|
|
1945
|
+
if (typeof sanitized.text === "string" && (Object.keys(sanitized).length === 1 || sanitized.type === "text")) {
|
|
1567
1946
|
return sanitized.text;
|
|
1568
1947
|
}
|
|
1569
1948
|
if (typeof sanitized.content === "string") {
|
|
@@ -1635,11 +2014,13 @@ function convertMessageListToSections(messageList) {
|
|
|
1635
2014
|
if (!content || !content.trim().length) continue;
|
|
1636
2015
|
}
|
|
1637
2016
|
}
|
|
2017
|
+
const parts = structuredMessageParts(message?.content);
|
|
1638
2018
|
sections.push({
|
|
1639
2019
|
kind: "message",
|
|
1640
2020
|
label: buildMessageLabel(role, next),
|
|
1641
2021
|
role,
|
|
1642
2022
|
content,
|
|
2023
|
+
...parts ? { parts } : {},
|
|
1643
2024
|
metadata: typeof message?.name === "string" ? { name: message.name } : void 0
|
|
1644
2025
|
});
|
|
1645
2026
|
}
|
|
@@ -2060,6 +2441,15 @@ function estimatePayloadBytes(value) {
|
|
|
2060
2441
|
}
|
|
2061
2442
|
}
|
|
2062
2443
|
}
|
|
2444
|
+
function toMessageUsage(u) {
|
|
2445
|
+
return {
|
|
2446
|
+
prompt_tokens: u.inputTokens,
|
|
2447
|
+
completion_tokens: u.outputTokens,
|
|
2448
|
+
total_tokens: u.totalTokens,
|
|
2449
|
+
prompt_tokens_details: { cached_tokens: u.cachedInputTokens, cache_write_tokens: u.cachedWriteTokens },
|
|
2450
|
+
completion_tokens_details: { reasoning_tokens: u.reasoningTokens }
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2063
2453
|
function fromNativeProvider(provider, options) {
|
|
2064
2454
|
let boundTools = options?.__tools;
|
|
2065
2455
|
const modelName = options?.model ?? provider.defaultModel ?? provider.providerName;
|
|
@@ -2067,50 +2457,87 @@ function fromNativeProvider(provider, options) {
|
|
|
2067
2457
|
modelName,
|
|
2068
2458
|
capabilities: resolveCapabilities(provider.providerName),
|
|
2069
2459
|
invoke: async (messages, _invokeOptions) => {
|
|
2070
|
-
const
|
|
2071
|
-
|
|
2072
|
-
|
|
2460
|
+
const link = linkAbortSignal(_invokeOptions);
|
|
2461
|
+
try {
|
|
2462
|
+
let request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
2463
|
+
if (link.signal) request.signal = link.signal;
|
|
2464
|
+
request = await applyTransform(request);
|
|
2465
|
+
const response = await provider.complete(request);
|
|
2466
|
+
return toBaseChatMessage(response);
|
|
2467
|
+
} finally {
|
|
2468
|
+
link.dispose();
|
|
2469
|
+
}
|
|
2073
2470
|
},
|
|
2074
2471
|
stream: async function* (messages, _invokeOptions) {
|
|
2075
|
-
const
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2472
|
+
const link = linkAbortSignal(_invokeOptions);
|
|
2473
|
+
try {
|
|
2474
|
+
let request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
2475
|
+
request.stream = true;
|
|
2476
|
+
if (link.signal) request.signal = link.signal;
|
|
2477
|
+
request = await applyTransform(request);
|
|
2478
|
+
let fullContent = "";
|
|
2479
|
+
let allToolCalls = [];
|
|
2480
|
+
let lastUsage;
|
|
2481
|
+
let reasoningBlocks;
|
|
2482
|
+
let reasoningText = "";
|
|
2483
|
+
let finishReason;
|
|
2484
|
+
let responseModel;
|
|
2485
|
+
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
2486
|
+
for await (const chunk of provider.completeStream(request)) {
|
|
2487
|
+
if (chunk.delta.content) {
|
|
2488
|
+
fullContent += chunk.delta.content;
|
|
2489
|
+
yield chunk.delta.content;
|
|
2490
|
+
}
|
|
2491
|
+
if (chunk.model) responseModel = chunk.model;
|
|
2492
|
+
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
2493
|
+
if (chunk.delta.reasoning?.blocks?.length) {
|
|
2494
|
+
reasoningBlocks = chunk.delta.reasoning.blocks;
|
|
2495
|
+
}
|
|
2496
|
+
const reasoningDelta = chunk.delta.reasoning?.summary;
|
|
2497
|
+
if (reasoningDelta) {
|
|
2498
|
+
reasoningText += reasoningDelta;
|
|
2499
|
+
yield { type: "reasoning", reasoning_content: reasoningDelta };
|
|
2500
|
+
}
|
|
2501
|
+
if (chunk.delta.toolCalls) {
|
|
2502
|
+
for (const tc of chunk.delta.toolCalls) {
|
|
2503
|
+
if (tc.id) {
|
|
2504
|
+
const existing = toolCallBuffers.get(tc.id);
|
|
2505
|
+
if (existing) {
|
|
2506
|
+
if (tc.arguments) existing.arguments += tc.arguments;
|
|
2507
|
+
if (tc.thoughtSignature) existing.thoughtSignature = tc.thoughtSignature;
|
|
2508
|
+
} else {
|
|
2509
|
+
toolCallBuffers.set(tc.id, {
|
|
2510
|
+
id: tc.id,
|
|
2511
|
+
name: tc.name ?? "",
|
|
2512
|
+
arguments: tc.arguments ?? "",
|
|
2513
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2102
2516
|
}
|
|
2103
2517
|
}
|
|
2104
2518
|
}
|
|
2519
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
2105
2520
|
}
|
|
2106
|
-
if (
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2521
|
+
if (link.signal?.aborted) return;
|
|
2522
|
+
allToolCalls = [...toolCallBuffers.values()];
|
|
2523
|
+
const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
|
|
2524
|
+
if (reasoningBlocks?.length || reasoningText) {
|
|
2525
|
+
finalMessage.reasoning = {
|
|
2526
|
+
...reasoningBlocks?.length ? { blocks: reasoningBlocks } : {},
|
|
2527
|
+
...reasoningText ? { summary: reasoningText } : {}
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
if (finishReason || responseModel) {
|
|
2531
|
+
finalMessage.response_metadata = {
|
|
2532
|
+
...finalMessage.response_metadata ?? {},
|
|
2533
|
+
...finishReason ? { finish_reason: finishReason } : {},
|
|
2534
|
+
...responseModel ? { model_name: responseModel } : {}
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
yield finalMessage;
|
|
2538
|
+
} finally {
|
|
2539
|
+
link.dispose();
|
|
2112
2540
|
}
|
|
2113
|
-
yield finalMessage;
|
|
2114
2541
|
},
|
|
2115
2542
|
bindTools: (tools, _bindOptions) => {
|
|
2116
2543
|
const strict = _bindOptions?.strict ?? false;
|
|
@@ -2119,6 +2546,11 @@ function fromNativeProvider(provider, options) {
|
|
|
2119
2546
|
}
|
|
2120
2547
|
};
|
|
2121
2548
|
return adapted;
|
|
2549
|
+
async function applyTransform(req) {
|
|
2550
|
+
if (!options?.transformRequest) return req;
|
|
2551
|
+
const out = await options.transformRequest(req);
|
|
2552
|
+
return out ?? req;
|
|
2553
|
+
}
|
|
2122
2554
|
function buildRequest(messages, tools, opts, invokeOptions) {
|
|
2123
2555
|
const req = {
|
|
2124
2556
|
model: opts?.model ?? modelName,
|
|
@@ -2126,6 +2558,10 @@ function fromNativeProvider(provider, options) {
|
|
|
2126
2558
|
};
|
|
2127
2559
|
if (opts?.temperature != null) req.temperature = opts.temperature;
|
|
2128
2560
|
if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
|
|
2561
|
+
if (opts?.topP != null) req.topP = opts.topP;
|
|
2562
|
+
if (opts?.stop?.length) req.stop = opts.stop;
|
|
2563
|
+
if (opts?.presencePenalty != null) req.presencePenalty = opts.presencePenalty;
|
|
2564
|
+
if (opts?.frequencyPenalty != null) req.frequencyPenalty = opts.frequencyPenalty;
|
|
2129
2565
|
if (tools?.length) req.tools = tools;
|
|
2130
2566
|
if (opts?.extra) req.extra = opts.extra;
|
|
2131
2567
|
const reasoningOverride = invokeOptions?.reasoning;
|
|
@@ -2178,7 +2614,8 @@ function toUnifiedMessage(msg) {
|
|
|
2178
2614
|
unified.toolCalls = msg.tool_calls.map((tc) => ({
|
|
2179
2615
|
id: tc.id,
|
|
2180
2616
|
name: tc.function?.name ?? tc.name ?? "",
|
|
2181
|
-
arguments: tc.function?.arguments ?? tc.arguments ?? "{}"
|
|
2617
|
+
arguments: tc.function?.arguments ?? tc.arguments ?? "{}",
|
|
2618
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2182
2619
|
}));
|
|
2183
2620
|
}
|
|
2184
2621
|
const reasoning = msg.reasoning;
|
|
@@ -2452,29 +2889,16 @@ function toToolDefinition(tool, strict) {
|
|
|
2452
2889
|
}
|
|
2453
2890
|
return { name, description, parameters, ...useStrict ? { strict: true } : {} };
|
|
2454
2891
|
}
|
|
2892
|
+
function legacyTokenUsage(u) {
|
|
2893
|
+
return { ...toMessageUsage(u), cached_tokens: u.cachedInputTokens, cache_write_tokens: u.cachedWriteTokens };
|
|
2894
|
+
}
|
|
2455
2895
|
function toBaseChatMessage(response) {
|
|
2456
2896
|
const msg = {
|
|
2457
2897
|
role: "assistant",
|
|
2458
2898
|
content: response.content ?? "",
|
|
2459
|
-
usage:
|
|
2460
|
-
prompt_tokens: response.usage.inputTokens,
|
|
2461
|
-
completion_tokens: response.usage.outputTokens,
|
|
2462
|
-
total_tokens: response.usage.totalTokens,
|
|
2463
|
-
prompt_tokens_details: {
|
|
2464
|
-
cached_tokens: response.usage.cachedInputTokens
|
|
2465
|
-
},
|
|
2466
|
-
completion_tokens_details: {
|
|
2467
|
-
reasoning_tokens: response.usage.reasoningTokens
|
|
2468
|
-
}
|
|
2469
|
-
},
|
|
2899
|
+
usage: toMessageUsage(response.usage),
|
|
2470
2900
|
response_metadata: {
|
|
2471
|
-
token_usage:
|
|
2472
|
-
prompt_tokens: response.usage.inputTokens,
|
|
2473
|
-
completion_tokens: response.usage.outputTokens,
|
|
2474
|
-
total_tokens: response.usage.totalTokens,
|
|
2475
|
-
cached_tokens: response.usage.cachedInputTokens,
|
|
2476
|
-
cache_write_tokens: response.usage.cachedWriteTokens
|
|
2477
|
-
},
|
|
2901
|
+
token_usage: legacyTokenUsage(response.usage),
|
|
2478
2902
|
finish_reason: response.finishReason,
|
|
2479
2903
|
model_name: response.model
|
|
2480
2904
|
}
|
|
@@ -2483,7 +2907,8 @@ function toBaseChatMessage(response) {
|
|
|
2483
2907
|
msg.tool_calls = response.toolCalls.map((tc) => ({
|
|
2484
2908
|
id: tc.id,
|
|
2485
2909
|
type: "function",
|
|
2486
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
2910
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
2911
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2487
2912
|
}));
|
|
2488
2913
|
}
|
|
2489
2914
|
if (response.reasoning && (response.reasoning.blocks?.length || response.reasoning.summary)) {
|
|
@@ -2497,32 +2922,15 @@ function assembleMessage(content, toolCalls, usage) {
|
|
|
2497
2922
|
content
|
|
2498
2923
|
};
|
|
2499
2924
|
if (usage) {
|
|
2500
|
-
msg.usage =
|
|
2501
|
-
|
|
2502
|
-
completion_tokens: usage.outputTokens,
|
|
2503
|
-
total_tokens: usage.totalTokens,
|
|
2504
|
-
prompt_tokens_details: {
|
|
2505
|
-
cached_tokens: usage.cachedInputTokens
|
|
2506
|
-
},
|
|
2507
|
-
completion_tokens_details: {
|
|
2508
|
-
reasoning_tokens: usage.reasoningTokens
|
|
2509
|
-
}
|
|
2510
|
-
};
|
|
2511
|
-
msg.response_metadata = {
|
|
2512
|
-
token_usage: {
|
|
2513
|
-
prompt_tokens: usage.inputTokens,
|
|
2514
|
-
completion_tokens: usage.outputTokens,
|
|
2515
|
-
total_tokens: usage.totalTokens,
|
|
2516
|
-
cached_tokens: usage.cachedInputTokens,
|
|
2517
|
-
cache_write_tokens: usage.cachedWriteTokens
|
|
2518
|
-
}
|
|
2519
|
-
};
|
|
2925
|
+
msg.usage = toMessageUsage(usage);
|
|
2926
|
+
msg.response_metadata = { token_usage: legacyTokenUsage(usage) };
|
|
2520
2927
|
}
|
|
2521
2928
|
if (toolCalls.length > 0) {
|
|
2522
2929
|
msg.tool_calls = toolCalls.map((tc) => ({
|
|
2523
2930
|
id: tc.id,
|
|
2524
2931
|
type: "function",
|
|
2525
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
2932
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
2933
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
2526
2934
|
}));
|
|
2527
2935
|
}
|
|
2528
2936
|
return msg;
|
|
@@ -2555,8 +2963,8 @@ function resolveCapabilities(provider) {
|
|
|
2555
2963
|
return {
|
|
2556
2964
|
structuredOutput: "tool_based",
|
|
2557
2965
|
strictToolCalling: false,
|
|
2558
|
-
streaming:
|
|
2559
|
-
//
|
|
2966
|
+
streaming: true,
|
|
2967
|
+
// ConverseStream (AWS event-stream frames)
|
|
2560
2968
|
provider: "bedrock"
|
|
2561
2969
|
};
|
|
2562
2970
|
case "vertex":
|
|
@@ -2573,150 +2981,21 @@ function resolveCapabilities(provider) {
|
|
|
2573
2981
|
|
|
2574
2982
|
// src/smart/toolResponses.ts
|
|
2575
2983
|
init_utilTokens();
|
|
2984
|
+
init_content();
|
|
2576
2985
|
|
|
2577
|
-
// src/
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
return `[image:${url || "unknown"}${detail ? ` detail=${detail}` : ""}]`;
|
|
2591
|
-
}
|
|
2592
|
-
return JSON.stringify(p);
|
|
2593
|
-
}).join("\n");
|
|
2594
|
-
}
|
|
2595
|
-
return JSON.stringify(content);
|
|
2596
|
-
} catch {
|
|
2597
|
-
return String(content);
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
function mergeContentsToString(contents) {
|
|
2601
|
-
return contents.map((c) => contentToString(c)).join("\n");
|
|
2602
|
-
}
|
|
2603
|
-
function safeStringify(value, pretty = false) {
|
|
2604
|
-
if (value == null) return "";
|
|
2605
|
-
if (typeof value === "string") return value;
|
|
2606
|
-
try {
|
|
2607
|
-
return JSON.stringify(value, null, pretty ? 2 : void 0) ?? String(value);
|
|
2608
|
-
} catch {
|
|
2609
|
-
return String(value);
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
function extractMessageText2(message) {
|
|
2613
|
-
if (!message) return "";
|
|
2614
|
-
const content = message.content;
|
|
2615
|
-
if (typeof content === "string") return content;
|
|
2616
|
-
if (Array.isArray(content)) {
|
|
2617
|
-
return content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join("");
|
|
2618
|
-
}
|
|
2619
|
-
return "";
|
|
2620
|
-
}
|
|
2621
|
-
function textFromContent(content) {
|
|
2622
|
-
if (typeof content === "string") return content;
|
|
2623
|
-
if (!Array.isArray(content)) return "";
|
|
2624
|
-
return content.filter((part) => isTextPart(part)).map((part) => part.text ?? "").join("\n");
|
|
2625
|
-
}
|
|
2626
|
-
function isTextPart(part) {
|
|
2627
|
-
return !!part && typeof part === "object" && part.type === "text";
|
|
2628
|
-
}
|
|
2629
|
-
function mapTextParts(content, fn) {
|
|
2630
|
-
if (typeof content === "string") return fn(content);
|
|
2631
|
-
if (!Array.isArray(content)) return content;
|
|
2632
|
-
return content.map((part) => isTextPart(part) ? { ...part, text: fn(part.text) } : part);
|
|
2633
|
-
}
|
|
2634
|
-
function replaceTextContent(content, text) {
|
|
2635
|
-
if (typeof content === "string" || content == null) return text;
|
|
2636
|
-
if (!Array.isArray(content)) return text;
|
|
2637
|
-
const textIndexes = content.reduce((acc, part, index) => {
|
|
2638
|
-
if (isTextPart(part)) acc.push(index);
|
|
2639
|
-
return acc;
|
|
2640
|
-
}, []);
|
|
2641
|
-
if (textIndexes.length === 0) {
|
|
2642
|
-
return text.length > 0 ? [{ type: "text", text }, ...content] : content;
|
|
2643
|
-
}
|
|
2644
|
-
const [first, ...rest] = textIndexes;
|
|
2645
|
-
const dropped = new Set(rest);
|
|
2646
|
-
return content.map((part, index) => index === first ? { ...part, text } : part).filter((_part, index) => !dropped.has(index));
|
|
2647
|
-
}
|
|
2648
|
-
function normalizeSource(source) {
|
|
2649
|
-
if (!source || typeof source !== "object") return {};
|
|
2650
|
-
const src = source;
|
|
2651
|
-
if (src.type === "base64" && typeof src.data === "string") {
|
|
2652
|
-
return {
|
|
2653
|
-
sourceType: "base64",
|
|
2654
|
-
mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0,
|
|
2655
|
-
// base64 encodes 3 bytes per 4 characters; padding makes this an upper bound.
|
|
2656
|
-
approxBytes: Math.floor(src.data.length * 3 / 4)
|
|
2657
|
-
};
|
|
2658
|
-
}
|
|
2659
|
-
if (src.type === "url" && typeof src.url === "string") {
|
|
2660
|
-
return {
|
|
2661
|
-
sourceType: "url",
|
|
2662
|
-
url: src.url,
|
|
2663
|
-
mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0
|
|
2664
|
-
};
|
|
2665
|
-
}
|
|
2666
|
-
return {};
|
|
2667
|
-
}
|
|
2668
|
-
function collectAttachments(content) {
|
|
2669
|
-
if (!Array.isArray(content)) return [];
|
|
2670
|
-
const attachments = [];
|
|
2671
|
-
content.forEach((part, index) => {
|
|
2672
|
-
if (!part || typeof part !== "object") return;
|
|
2673
|
-
const type = part.type;
|
|
2674
|
-
if (!type || type === "text") return;
|
|
2675
|
-
if (type === "image" || type === "audio" || type === "video" || type === "file") {
|
|
2676
|
-
attachments.push({
|
|
2677
|
-
index,
|
|
2678
|
-
kind: type,
|
|
2679
|
-
fileName: typeof part.fileName === "string" ? part.fileName : void 0,
|
|
2680
|
-
...normalizeSource(part.source)
|
|
2681
|
-
});
|
|
2682
|
-
return;
|
|
2683
|
-
}
|
|
2684
|
-
if (type === "image_url") {
|
|
2685
|
-
const image = part.image_url;
|
|
2686
|
-
const url = typeof image === "string" ? image : image?.url;
|
|
2687
|
-
if (typeof url === "string" && url.startsWith("data:")) {
|
|
2688
|
-
const comma = url.indexOf(",");
|
|
2689
|
-
const header = url.slice(5, comma);
|
|
2690
|
-
attachments.push({
|
|
2691
|
-
index,
|
|
2692
|
-
kind: "image",
|
|
2693
|
-
sourceType: "base64",
|
|
2694
|
-
mediaType: header.split(";")[0] || void 0,
|
|
2695
|
-
approxBytes: Math.floor((url.length - comma - 1) * 3 / 4)
|
|
2696
|
-
});
|
|
2697
|
-
} else {
|
|
2698
|
-
attachments.push({ index, kind: "image", sourceType: "url", url });
|
|
2699
|
-
}
|
|
2700
|
-
return;
|
|
2701
|
-
}
|
|
2702
|
-
attachments.push({ index, kind: "unknown" });
|
|
2703
|
-
});
|
|
2704
|
-
return attachments;
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
// src/smart/toolArgCoercion.ts
|
|
2708
|
-
var PLACEHOLDER_TEXT = /* @__PURE__ */ new Set(["", "null", "undefined", "none", "nil", "n/a", "na"]);
|
|
2709
|
-
var ENVELOPE_KEYS = ["input", "args", "arguments", "params", "parameters", "payload"];
|
|
2710
|
-
function defOf(schema) {
|
|
2711
|
-
const def = schema?._def;
|
|
2712
|
-
return def && typeof def === "object" ? def : null;
|
|
2713
|
-
}
|
|
2714
|
-
function isPlaceholder(value) {
|
|
2715
|
-
return typeof value === "string" && PLACEHOLDER_TEXT.has(value.trim().toLowerCase());
|
|
2716
|
-
}
|
|
2717
|
-
function parseJsonish(value) {
|
|
2718
|
-
const text = value.trim();
|
|
2719
|
-
if (!text.startsWith("{") && !text.startsWith("[")) return { ok: false };
|
|
2986
|
+
// src/smart/toolArgCoercion.ts
|
|
2987
|
+
var PLACEHOLDER_TEXT = /* @__PURE__ */ new Set(["", "null", "undefined", "none", "nil", "n/a", "na"]);
|
|
2988
|
+
var ENVELOPE_KEYS = ["input", "args", "arguments", "params", "parameters", "payload"];
|
|
2989
|
+
function defOf(schema) {
|
|
2990
|
+
const def = schema?._def;
|
|
2991
|
+
return def && typeof def === "object" ? def : null;
|
|
2992
|
+
}
|
|
2993
|
+
function isPlaceholder(value) {
|
|
2994
|
+
return typeof value === "string" && PLACEHOLDER_TEXT.has(value.trim().toLowerCase());
|
|
2995
|
+
}
|
|
2996
|
+
function parseJsonish(value) {
|
|
2997
|
+
const text = value.trim();
|
|
2998
|
+
if (!text.startsWith("{") && !text.startsWith("[")) return { ok: false };
|
|
2720
2999
|
try {
|
|
2721
3000
|
return { ok: true, value: JSON.parse(text) };
|
|
2722
3001
|
} catch {
|
|
@@ -3591,6 +3870,7 @@ function detectVolatileContent(text, limit = 20) {
|
|
|
3591
3870
|
|
|
3592
3871
|
// src/smart/contextPilot/jsonCrusher.ts
|
|
3593
3872
|
init_utilTokens();
|
|
3873
|
+
init_content();
|
|
3594
3874
|
var CONTEXT_PILOT_JSON_MARKER_KEY = "__contextPilotDropped";
|
|
3595
3875
|
function findCompressibleArray(output, minItems) {
|
|
3596
3876
|
if (Array.isArray(output)) {
|
|
@@ -3803,6 +4083,7 @@ function compressSearchOutput(text, config, ccrStore) {
|
|
|
3803
4083
|
|
|
3804
4084
|
// src/smart/contextPilot/pipeline.ts
|
|
3805
4085
|
init_utilTokens();
|
|
4086
|
+
init_content();
|
|
3806
4087
|
function createContextPilotRuntime(config) {
|
|
3807
4088
|
return {
|
|
3808
4089
|
ccrStore: new CCRStore({ ttlMs: config.ccr.ttlMs, maxEntries: config.ccr.maxEntries }),
|
|
@@ -4176,6 +4457,8 @@ function createAgentCoreNode(opts) {
|
|
|
4176
4457
|
const streamingEnabled = Boolean(state.ctx?.__streaming);
|
|
4177
4458
|
const cancellationToken = state.ctx?.__cancellationToken;
|
|
4178
4459
|
const abortSignal = state.ctx?.__abortSignal;
|
|
4460
|
+
const onProgress = state.ctx?.__onProgress;
|
|
4461
|
+
const cancelRequested = () => isCancellationRequested({ signal: abortSignal, cancellationToken });
|
|
4179
4462
|
const resolvedForCachePilot = getResolvedSmartConfig(opts, runtime);
|
|
4180
4463
|
if (resolvedForCachePilot.contextPilot.enabled && resolvedForCachePilot.contextPilot.cacheAlignment.enabled) {
|
|
4181
4464
|
const ctxForCacheWarning = state.ctx = state.ctx || {};
|
|
@@ -4334,6 +4617,29 @@ ${append}`;
|
|
|
4334
4617
|
let response;
|
|
4335
4618
|
let modelAttempt = 0;
|
|
4336
4619
|
const maxModelRetries = host?.maxModelRetries ?? 0;
|
|
4620
|
+
const finishCancelledTurn = (partialText) => {
|
|
4621
|
+
const reason = abortSignal?.aborted ? "aborted" : "cancelled";
|
|
4622
|
+
const ctx = state.ctx = state.ctx || {};
|
|
4623
|
+
ctx.__cancelled = { stage: "agent", reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4624
|
+
recordTraceEvent(traceSession, {
|
|
4625
|
+
type: "ai_call",
|
|
4626
|
+
label: "Assistant Cancelled",
|
|
4627
|
+
actor: { scope: "agent", name: actorName, role: "assistant", version: actorVersion },
|
|
4628
|
+
// "skipped", not "error": the call did not fail, the caller ended it —
|
|
4629
|
+
// and an "error" would flip the whole session to error.
|
|
4630
|
+
status: "skipped",
|
|
4631
|
+
durationMs: Date.now() - start,
|
|
4632
|
+
requestBytes: promptBytes,
|
|
4633
|
+
model: getModelName(runtime.model || opts.model),
|
|
4634
|
+
provider: getProviderName(runtime.model || opts.model),
|
|
4635
|
+
messageList: state.messages,
|
|
4636
|
+
toolDefinitions: traceToolDefinitions
|
|
4637
|
+
});
|
|
4638
|
+
onEvent?.({ type: "cancelled", stage: "agent", reason });
|
|
4639
|
+
onProgress?.({ stage: "agent", message: "Cancelled", detail: { reason } });
|
|
4640
|
+
const partial = partialText.trim().length > 0 ? [{ role: "assistant", content: partialText }] : [];
|
|
4641
|
+
return { messages: [...state.messages, ...partial], usage: state.usage };
|
|
4642
|
+
};
|
|
4337
4643
|
while (true) {
|
|
4338
4644
|
start = Date.now();
|
|
4339
4645
|
if (shortCircuited !== void 0) {
|
|
@@ -4349,19 +4655,25 @@ ${append}`;
|
|
|
4349
4655
|
toolDefinitions: traceToolDefinitions
|
|
4350
4656
|
});
|
|
4351
4657
|
} else {
|
|
4658
|
+
let streamedText = "";
|
|
4352
4659
|
try {
|
|
4353
4660
|
const normalizedMessages = normalizeBedrockToolPairing([...wireMessages]);
|
|
4354
4661
|
debugToolPairing(normalizedMessages);
|
|
4355
4662
|
if (streamingEnabled && typeof modelWithTools.stream === "function") {
|
|
4356
|
-
let streamedText = "";
|
|
4357
4663
|
let streamedMessage;
|
|
4664
|
+
let cutByCancel = false;
|
|
4358
4665
|
for await (const chunk of modelWithTools.stream(normalizedMessages, invokeParams)) {
|
|
4359
|
-
if (
|
|
4666
|
+
if (cancelRequested()) {
|
|
4667
|
+
cutByCancel = true;
|
|
4360
4668
|
break;
|
|
4361
4669
|
}
|
|
4362
4670
|
if (chunk && typeof chunk === "object" && chunk.role) {
|
|
4363
4671
|
streamedMessage = chunk;
|
|
4364
4672
|
}
|
|
4673
|
+
if (chunk && typeof chunk === "object" && chunk.type === "reasoning" && typeof chunk.reasoning_content === "string") {
|
|
4674
|
+
onEvent?.({ type: "reasoning", text: chunk.reasoning_content });
|
|
4675
|
+
continue;
|
|
4676
|
+
}
|
|
4365
4677
|
const text = extractText2(chunk);
|
|
4366
4678
|
const isAssembledMessage = Boolean(chunk && typeof chunk === "object" && chunk.role) && text.length > 0 && text === streamedText;
|
|
4367
4679
|
if (text && !isAssembledMessage) {
|
|
@@ -4370,6 +4682,7 @@ ${append}`;
|
|
|
4370
4682
|
onEvent?.({ type: "stream", text });
|
|
4371
4683
|
}
|
|
4372
4684
|
}
|
|
4685
|
+
if (cutByCancel) return finishCancelledTurn(streamedText);
|
|
4373
4686
|
if (streamedMessage) {
|
|
4374
4687
|
response = { ...streamedMessage };
|
|
4375
4688
|
if (response.content == null || response.content === "") {
|
|
@@ -4382,6 +4695,7 @@ ${append}`;
|
|
|
4382
4695
|
response = await modelWithTools.invoke(normalizedMessages, invokeParams);
|
|
4383
4696
|
}
|
|
4384
4697
|
} catch (err) {
|
|
4698
|
+
if (cancelRequested()) return finishCancelledTurn(streamedText);
|
|
4385
4699
|
const durationMs2 = Date.now() - start;
|
|
4386
4700
|
recordTraceEvent(traceSession, {
|
|
4387
4701
|
type: "ai_call",
|
|
@@ -4479,6 +4793,7 @@ ${append}`;
|
|
|
4479
4793
|
}
|
|
4480
4794
|
|
|
4481
4795
|
// src/utils/traceSections.ts
|
|
4796
|
+
init_content();
|
|
4482
4797
|
function truncate(text, max = 500) {
|
|
4483
4798
|
if (!text) return "";
|
|
4484
4799
|
return text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
@@ -6369,6 +6684,9 @@ function isCompactedToolContent(content) {
|
|
|
6369
6684
|
return typeof content === "string" && (content === "SUMMARIZED" || content.startsWith("SUMMARIZED_TOOL_RESPONSE") || content.startsWith("ARCHIVED_TOOL_RESPONSE") || content.startsWith("STRUCTURED_TOOL_RESPONSE") || content.startsWith("DROPPED_TOOL_RESPONSE"));
|
|
6370
6685
|
}
|
|
6371
6686
|
|
|
6687
|
+
// src/agent.ts
|
|
6688
|
+
init_content();
|
|
6689
|
+
|
|
6372
6690
|
// src/structuredOutput/manager.ts
|
|
6373
6691
|
var StructuredOutputManager = class {
|
|
6374
6692
|
constructor(schema, strategy, config) {
|
|
@@ -6601,6 +6919,7 @@ function buildSubagentCatalogBlock(subagents, policy, toolNames) {
|
|
|
6601
6919
|
}
|
|
6602
6920
|
|
|
6603
6921
|
// src/plugins/host.ts
|
|
6922
|
+
init_content();
|
|
6604
6923
|
var DEFAULT_PRIORITY = 100;
|
|
6605
6924
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
6606
6925
|
var HOOK_SPECS = {
|
|
@@ -7137,6 +7456,7 @@ function performanceNow() {
|
|
|
7137
7456
|
}
|
|
7138
7457
|
|
|
7139
7458
|
// src/plugins/session.ts
|
|
7459
|
+
init_content();
|
|
7140
7460
|
async function openPluginSession(runHost, params) {
|
|
7141
7461
|
let messages = params.messages;
|
|
7142
7462
|
const startGate = await runHost.runGate("sessionStart", {
|
|
@@ -7606,6 +7926,7 @@ function createAgent(opts) {
|
|
|
7606
7926
|
onProgress?.({ stage: "agent", message: "Invoking model" });
|
|
7607
7927
|
if (cancelIfRequested("before_agent")) break;
|
|
7608
7928
|
state = publish({ ...state, ...await agentCore(state) });
|
|
7929
|
+
if (state.ctx?.__cancelled) break;
|
|
7609
7930
|
onProgress?.({ stage: "agent", message: "Model response received" });
|
|
7610
7931
|
if (checkpointIfRequested("after_agent")) break;
|
|
7611
7932
|
const postGuardrails = getGuardrailConfig(state);
|
|
@@ -7676,7 +7997,7 @@ function createAgent(opts) {
|
|
|
7676
7997
|
if (toolCalls.length === 0) {
|
|
7677
7998
|
if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize && !state.ctx?.__guardrailBlocked) {
|
|
7678
7999
|
if (soManager.strategy.kind === "native") {
|
|
7679
|
-
const assistantText =
|
|
8000
|
+
const assistantText = extractMessageText(lastMsg);
|
|
7680
8001
|
if (assistantText) {
|
|
7681
8002
|
const parsed = soManager.parseFromContent(assistantText);
|
|
7682
8003
|
if (parsed.success) {
|
|
@@ -7732,7 +8053,7 @@ function createAgent(opts) {
|
|
|
7732
8053
|
if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !structuredFinalizeBlocked) {
|
|
7733
8054
|
const lastForParse = state.messages[state.messages.length - 1];
|
|
7734
8055
|
if (lastForParse?.role === "assistant") {
|
|
7735
|
-
const assistantText =
|
|
8056
|
+
const assistantText = extractMessageText(lastForParse);
|
|
7736
8057
|
if (assistantText) {
|
|
7737
8058
|
const parsed = soManager.parseFromContent(assistantText);
|
|
7738
8059
|
if (parsed.success) {
|
|
@@ -7994,7 +8315,7 @@ function createAgent(opts) {
|
|
|
7994
8315
|
const isTransientSummarizationExit = !!res.ctx?.__needsSummarization;
|
|
7995
8316
|
const finalAssistantMsg = isTransientSummarizationExit ? void 0 : getLastAssistantMessage(res.messages);
|
|
7996
8317
|
const finalIsSyntheticSummary = !isTransientSummarizationExit && finalAssistantMsg && isSyntheticSummaryMessage(finalAssistantMsg);
|
|
7997
|
-
let content = isTransientSummarizationExit || finalIsSyntheticSummary ? "" :
|
|
8318
|
+
let content = isTransientSummarizationExit || finalIsSyntheticSummary ? "" : extractMessageText(finalAssistantMsg);
|
|
7998
8319
|
const finalAnswerBlocked = Boolean(
|
|
7999
8320
|
isTransientSummarizationExit || res.ctx?.__awaitingApproval || res.ctx?.__awaitingUserQuestion || res.ctx?.__cancelled || res.ctx?.__paused || res.ctx?.__limitBreached
|
|
8000
8321
|
);
|
|
@@ -8253,9 +8574,11 @@ function createAgentNode(opts) {
|
|
|
8253
8574
|
|
|
8254
8575
|
// src/nodes/contextSummarize.ts
|
|
8255
8576
|
init_utilTokens();
|
|
8577
|
+
init_content();
|
|
8256
8578
|
|
|
8257
8579
|
// src/smart/contextPolicy.ts
|
|
8258
8580
|
init_utilTokens();
|
|
8581
|
+
init_content();
|
|
8259
8582
|
function collectRecentTurns(messages, lastTurnsToKeep) {
|
|
8260
8583
|
if (lastTurnsToKeep <= 0) return [];
|
|
8261
8584
|
const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
|
|
@@ -8317,7 +8640,7 @@ function isClampProtected(message, index, firstUserIndex) {
|
|
|
8317
8640
|
function clampToBudget(messages, maxContextTokens) {
|
|
8318
8641
|
let working = [...messages];
|
|
8319
8642
|
while (working.length > 2) {
|
|
8320
|
-
const tokenCount = countApproxTokens(working.map(
|
|
8643
|
+
const tokenCount = countApproxTokens(working.map(extractMessageText).join("\n"));
|
|
8321
8644
|
if (tokenCount <= maxContextTokens) return working;
|
|
8322
8645
|
const firstUserIndex = working.findIndex((message) => message.role === "user");
|
|
8323
8646
|
const firstNonSystem = working.findIndex((message, index) => !isClampProtected(message, index, firstUserIndex));
|
|
@@ -8657,7 +8980,7 @@ function createContextSummarizeNode(opts) {
|
|
|
8657
8980
|
let content = "";
|
|
8658
8981
|
if (typeof m.content === "string") content = m.content;
|
|
8659
8982
|
else if (Array.isArray(m.content)) {
|
|
8660
|
-
content = m.content.map((c) => c
|
|
8983
|
+
content = m.content.map((c) => contentPartToText(c, (part) => JSON.stringify(part))).join(" ");
|
|
8661
8984
|
}
|
|
8662
8985
|
if (m.role === "tool" && isSummarizedToolPlaceholder(content)) {
|
|
8663
8986
|
const historyEntry = findToolHistoryEntry(state, m.tool_call_id, m.name);
|
|
@@ -9502,6 +9825,9 @@ async function writeSummaryFactsToMemory(store, config, summary, sourceTurn) {
|
|
|
9502
9825
|
return store.get(config.memory.scope, { limit: config.context.lastTurnsToKeep + summary.stable_facts.length });
|
|
9503
9826
|
}
|
|
9504
9827
|
|
|
9828
|
+
// src/smart/index.ts
|
|
9829
|
+
init_content();
|
|
9830
|
+
|
|
9505
9831
|
// src/smart/skills/types.ts
|
|
9506
9832
|
var DEFAULT_SKILL_POLICY = {
|
|
9507
9833
|
maxOpenSkills: 4,
|
|
@@ -10516,7 +10842,7 @@ function createSmartAgent(opts) {
|
|
|
10516
10842
|
].join("\n");
|
|
10517
10843
|
function latestUserPrompt(messages) {
|
|
10518
10844
|
const latest = [...messages].reverse().find((message) => message.role === "user");
|
|
10519
|
-
return latest ?
|
|
10845
|
+
return latest ? extractMessageText(latest) : "";
|
|
10520
10846
|
}
|
|
10521
10847
|
async function syncMemory(state) {
|
|
10522
10848
|
const query = latestUserPrompt(state.messages || []);
|
|
@@ -11227,6 +11553,182 @@ async function runSmartAgentEvalHarness(params) {
|
|
|
11227
11553
|
}
|
|
11228
11554
|
return results;
|
|
11229
11555
|
}
|
|
11556
|
+
|
|
11557
|
+
// src/providers/types.ts
|
|
11558
|
+
function emptyUsage() {
|
|
11559
|
+
return {
|
|
11560
|
+
inputTokens: 0,
|
|
11561
|
+
outputTokens: 0,
|
|
11562
|
+
totalTokens: 0,
|
|
11563
|
+
cachedInputTokens: 0,
|
|
11564
|
+
cachedWriteTokens: 0,
|
|
11565
|
+
cachedOutputTokens: 0,
|
|
11566
|
+
reasoningTokens: 0
|
|
11567
|
+
};
|
|
11568
|
+
}
|
|
11569
|
+
var ProviderError = class extends Error {
|
|
11570
|
+
constructor(message, provider, statusCode, responseBody) {
|
|
11571
|
+
super(message);
|
|
11572
|
+
this.provider = provider;
|
|
11573
|
+
this.statusCode = statusCode;
|
|
11574
|
+
this.responseBody = responseBody;
|
|
11575
|
+
this.name = "ProviderError";
|
|
11576
|
+
}
|
|
11577
|
+
};
|
|
11578
|
+
|
|
11579
|
+
// src/providers/base.ts
|
|
11580
|
+
var DEFAULT_RETRY = {
|
|
11581
|
+
maxRetries: 3,
|
|
11582
|
+
baseDelayMs: 500,
|
|
11583
|
+
maxDelayMs: 8e3
|
|
11584
|
+
};
|
|
11585
|
+
var BaseProvider = class {
|
|
11586
|
+
constructor() {
|
|
11587
|
+
this.retryPolicy = DEFAULT_RETRY;
|
|
11588
|
+
}
|
|
11589
|
+
/** Applies the transport options shared by every provider config. */
|
|
11590
|
+
configureTransport(config) {
|
|
11591
|
+
this.fetchImpl = config.fetch;
|
|
11592
|
+
this.timeoutMs = typeof config.timeoutMs === "number" && config.timeoutMs > 0 ? config.timeoutMs : void 0;
|
|
11593
|
+
}
|
|
11594
|
+
/**
|
|
11595
|
+
* The single HTTP entry point for every provider call site. Uses the
|
|
11596
|
+
* configured `fetch` and applies `timeoutMs`: the timer covers the time until
|
|
11597
|
+
* the response HEADERS arrive and is cleared after that, so a long-running
|
|
11598
|
+
* stream is never cut by it (use the request `signal` to cancel a stream).
|
|
11599
|
+
* A timeout surfaces as a `ProviderError` with status 408. The caller's
|
|
11600
|
+
* `signal` still aborts the request as before.
|
|
11601
|
+
*/
|
|
11602
|
+
async httpFetch(url, init = {}) {
|
|
11603
|
+
const doFetch = this.fetchImpl ?? fetch;
|
|
11604
|
+
const timeoutMs = this.timeoutMs;
|
|
11605
|
+
if (!timeoutMs) return doFetch(url, init);
|
|
11606
|
+
const controller = new AbortController();
|
|
11607
|
+
const outer = init.signal ?? void 0;
|
|
11608
|
+
let timedOut = false;
|
|
11609
|
+
const onOuterAbort = () => controller.abort(outer?.reason);
|
|
11610
|
+
if (outer) {
|
|
11611
|
+
if (outer.aborted) controller.abort(outer.reason);
|
|
11612
|
+
else outer.addEventListener("abort", onOuterAbort, { once: true });
|
|
11613
|
+
}
|
|
11614
|
+
const timer = setTimeout(() => {
|
|
11615
|
+
timedOut = true;
|
|
11616
|
+
controller.abort(new Error(`timed out after ${timeoutMs}ms`));
|
|
11617
|
+
}, timeoutMs);
|
|
11618
|
+
try {
|
|
11619
|
+
const res = await doFetch(url, { ...init, signal: controller.signal });
|
|
11620
|
+
return res;
|
|
11621
|
+
} catch (err) {
|
|
11622
|
+
if (timedOut) {
|
|
11623
|
+
throw new ProviderError(`${this.providerName} request timed out after ${timeoutMs}ms`, this.providerName, 408);
|
|
11624
|
+
}
|
|
11625
|
+
throw err;
|
|
11626
|
+
} finally {
|
|
11627
|
+
clearTimeout(timer);
|
|
11628
|
+
}
|
|
11629
|
+
}
|
|
11630
|
+
/**
|
|
11631
|
+
* Embeds `request.input`. Implemented by the providers that have an
|
|
11632
|
+
* embeddings endpoint (OpenAI, Azure, openai-compatible, Bedrock, Vertex);
|
|
11633
|
+
* the others reject with a 400 ProviderError.
|
|
11634
|
+
*/
|
|
11635
|
+
async embed(_request) {
|
|
11636
|
+
throw new ProviderError(`${this.providerName} does not support embeddings`, this.providerName, 400);
|
|
11637
|
+
}
|
|
11638
|
+
/**
|
|
11639
|
+
* Helper concrete providers can wrap their fetch with. Retries 429/5xx
|
|
11640
|
+
* with exponential backoff and respects Retry-After if the response
|
|
11641
|
+
* carries one.
|
|
11642
|
+
*
|
|
11643
|
+
* `signal` is the call's cancellation: a run that is cancelled while the
|
|
11644
|
+
* provider is backing off must not sit out the delay, nor start another attempt.
|
|
11645
|
+
*/
|
|
11646
|
+
async doFetchWithRetry(execute, signal) {
|
|
11647
|
+
const policy = this.retryPolicy;
|
|
11648
|
+
const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
|
|
11649
|
+
const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
|
|
11650
|
+
const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
|
|
11651
|
+
let lastResp;
|
|
11652
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
11653
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
11654
|
+
const res = await execute();
|
|
11655
|
+
const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
|
|
11656
|
+
if (!isRetryable) return res;
|
|
11657
|
+
const predicate = policy.shouldRetry;
|
|
11658
|
+
if (predicate && !predicate({ status: res.status }, attempt)) return res;
|
|
11659
|
+
if (attempt === maxRetries) {
|
|
11660
|
+
lastResp = res;
|
|
11661
|
+
break;
|
|
11662
|
+
}
|
|
11663
|
+
const retryAfter = res.headers.get("retry-after");
|
|
11664
|
+
let delay2 = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
|
|
11665
|
+
if (retryAfter) {
|
|
11666
|
+
const asNumber = Number(retryAfter);
|
|
11667
|
+
if (!Number.isNaN(asNumber)) delay2 = Math.min(maxDelay, Math.max(delay2, asNumber * 1e3));
|
|
11668
|
+
}
|
|
11669
|
+
try {
|
|
11670
|
+
await res.text();
|
|
11671
|
+
} catch {
|
|
11672
|
+
}
|
|
11673
|
+
await abortableSleep(delay2, signal);
|
|
11674
|
+
}
|
|
11675
|
+
return lastResp;
|
|
11676
|
+
}
|
|
11677
|
+
};
|
|
11678
|
+
|
|
11679
|
+
// src/providers/jev.ts
|
|
11680
|
+
var JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
|
|
11681
|
+
var JevProvider = class extends BaseProvider {
|
|
11682
|
+
constructor(config) {
|
|
11683
|
+
super();
|
|
11684
|
+
this.providerName = "jev";
|
|
11685
|
+
this.configureTransport(config);
|
|
11686
|
+
this.apiKey = config.apiKey;
|
|
11687
|
+
this.baseURL = (config.baseURL ?? JEV_DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
11688
|
+
this.defaultModel = config.defaultModel;
|
|
11689
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
11690
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
11691
|
+
}
|
|
11692
|
+
/** Ask System One questions about `state`. */
|
|
11693
|
+
async systemOne(request, options) {
|
|
11694
|
+
const model = request.model ?? this.defaultModel;
|
|
11695
|
+
const body = {
|
|
11696
|
+
...model ? { model } : {},
|
|
11697
|
+
state: request.state,
|
|
11698
|
+
questions: request.questions
|
|
11699
|
+
};
|
|
11700
|
+
const res = await this.doFetchWithRetry(
|
|
11701
|
+
() => this.httpFetch(`${this.baseURL}/v1/systemone`, {
|
|
11702
|
+
method: "POST",
|
|
11703
|
+
headers: {
|
|
11704
|
+
"Content-Type": "application/json",
|
|
11705
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
11706
|
+
...this.defaultHeaders
|
|
11707
|
+
},
|
|
11708
|
+
body: JSON.stringify(body),
|
|
11709
|
+
signal: options?.signal
|
|
11710
|
+
})
|
|
11711
|
+
);
|
|
11712
|
+
if (!res.ok) {
|
|
11713
|
+
const text = await res.text().catch(() => "");
|
|
11714
|
+
throw new ProviderError(`Jev API error ${res.status}: ${text}`, this.providerName, res.status, text);
|
|
11715
|
+
}
|
|
11716
|
+
return await res.json();
|
|
11717
|
+
}
|
|
11718
|
+
async complete(_request) {
|
|
11719
|
+
throw new ProviderError(
|
|
11720
|
+
'Jev is a decision model and has no chat completion; use systemOne() or agentVerdictRule({ engine: "jev" })',
|
|
11721
|
+
this.providerName
|
|
11722
|
+
);
|
|
11723
|
+
}
|
|
11724
|
+
// eslint-disable-next-line require-yield
|
|
11725
|
+
async *completeStream(_request) {
|
|
11726
|
+
throw new ProviderError("Jev does not stream; use systemOne()", this.providerName);
|
|
11727
|
+
}
|
|
11728
|
+
};
|
|
11729
|
+
|
|
11730
|
+
// src/guardrails/checks.ts
|
|
11731
|
+
init_content();
|
|
11230
11732
|
var Ajv = AjvModule__default.default?.default ?? AjvModule__default.default;
|
|
11231
11733
|
var defaultAjv = new Ajv({ allErrors: true });
|
|
11232
11734
|
defaultAjv.opts.strict = false;
|
|
@@ -11376,14 +11878,82 @@ function defaultPayload(context) {
|
|
|
11376
11878
|
2
|
|
11377
11879
|
);
|
|
11378
11880
|
}
|
|
11881
|
+
var DEFAULT_JEV_THRESHOLD = 0.5;
|
|
11882
|
+
var JEV_QUESTION_KEY = "shouldBlock";
|
|
11883
|
+
var DEFAULT_JEV_QUESTION = "Does this conversation payload pose a safety, security, or policy risk that should stop the agent from continuing? Answer yes when the latest message or action should be blocked, and no when it is safe to allow.";
|
|
11884
|
+
var DEFAULT_JEV_CRITERIA = {
|
|
11885
|
+
true: "The payload is unsafe, non-compliant, or otherwise warrants blocking.",
|
|
11886
|
+
false: "The payload is benign and the agent should be allowed to continue."
|
|
11887
|
+
};
|
|
11888
|
+
function resolveJevClient(config) {
|
|
11889
|
+
if (config.client) return config.client;
|
|
11890
|
+
const apiKey = config.apiKey ?? process.env.TYPESAFE_API_KEY;
|
|
11891
|
+
if (!apiKey) {
|
|
11892
|
+
throw new Error(
|
|
11893
|
+
"Jev engine requires an API key: pass `jev.apiKey`, set TYPESAFE_API_KEY, or pass a `jev.client`."
|
|
11894
|
+
);
|
|
11895
|
+
}
|
|
11896
|
+
return new JevProvider({
|
|
11897
|
+
provider: "jev",
|
|
11898
|
+
apiKey,
|
|
11899
|
+
baseURL: config.baseURL ?? process.env.TYPESAFE_BASE_URL
|
|
11900
|
+
});
|
|
11901
|
+
}
|
|
11379
11902
|
function agentVerdictRule(options) {
|
|
11380
11903
|
const payloadBuilder = options.payloadBuilder || defaultPayload;
|
|
11381
11904
|
const fallbackDisposition = options.dispositionFallback || "block";
|
|
11905
|
+
const engine = options.engine || "llm";
|
|
11906
|
+
const jevOptions = options.jev || {};
|
|
11907
|
+
const jevThreshold = typeof jevOptions.threshold === "number" ? jevOptions.threshold : DEFAULT_JEV_THRESHOLD;
|
|
11908
|
+
let jevClient;
|
|
11909
|
+
async function evaluateWithJev(context) {
|
|
11910
|
+
const client = jevClient ?? (jevClient = resolveJevClient(jevOptions));
|
|
11911
|
+
const instructions = jevOptions.question ? jevOptions.question(context) : DEFAULT_JEV_QUESTION;
|
|
11912
|
+
const result = await client.systemOne({
|
|
11913
|
+
state: payloadBuilder(context),
|
|
11914
|
+
questions: {
|
|
11915
|
+
[JEV_QUESTION_KEY]: {
|
|
11916
|
+
type: "noul",
|
|
11917
|
+
instructions,
|
|
11918
|
+
criteria: jevOptions.criteria ?? DEFAULT_JEV_CRITERIA
|
|
11919
|
+
}
|
|
11920
|
+
},
|
|
11921
|
+
...jevOptions.model ? { model: jevOptions.model } : {}
|
|
11922
|
+
});
|
|
11923
|
+
const answer = result?.answers?.[JEV_QUESTION_KEY];
|
|
11924
|
+
const probability = typeof answer?.noul === "number" ? answer.noul : void 0;
|
|
11925
|
+
if (probability === void 0) {
|
|
11926
|
+
return {
|
|
11927
|
+
passed: false,
|
|
11928
|
+
disposition: fallbackDisposition,
|
|
11929
|
+
reason: "Jev engine did not return a noul probability.",
|
|
11930
|
+
details: { jevModel: result?.model, jevUsage: result?.usage }
|
|
11931
|
+
};
|
|
11932
|
+
}
|
|
11933
|
+
const passed = probability < jevThreshold;
|
|
11934
|
+
return {
|
|
11935
|
+
passed,
|
|
11936
|
+
disposition: passed ? "allow" : fallbackDisposition,
|
|
11937
|
+
reason: passed ? void 0 : `Jev risk probability ${probability.toFixed(3)} met or exceeded threshold ${jevThreshold}.`,
|
|
11938
|
+
details: {
|
|
11939
|
+
jevProbability: probability,
|
|
11940
|
+
jevThreshold,
|
|
11941
|
+
jevModel: result?.model,
|
|
11942
|
+
jevUsage: result?.usage
|
|
11943
|
+
}
|
|
11944
|
+
};
|
|
11945
|
+
}
|
|
11382
11946
|
return {
|
|
11383
11947
|
id: options.id,
|
|
11384
11948
|
title: options.title || "Agent-based guardrail",
|
|
11385
11949
|
description: options.description,
|
|
11386
11950
|
async evaluate(context) {
|
|
11951
|
+
if (engine === "jev") {
|
|
11952
|
+
return evaluateWithJev(context);
|
|
11953
|
+
}
|
|
11954
|
+
if (!options.agent) {
|
|
11955
|
+
throw new Error("agentVerdictRule requires an `agent` when using the default 'llm' engine.");
|
|
11956
|
+
}
|
|
11387
11957
|
const payload = payloadBuilder(context);
|
|
11388
11958
|
const systemPrompt = options.systemPrompt || "You are a safety auditor. Review the provided conversation payload and decide whether it should continue. Return JSON with keys allow (boolean), optional disposition ('allow' | 'warn' | 'block'), optional reason (string), and optional details (object).";
|
|
11389
11959
|
const review = await options.agent.invoke({
|
|
@@ -11599,6 +12169,7 @@ function isHookImplemented(hook) {
|
|
|
11599
12169
|
}
|
|
11600
12170
|
|
|
11601
12171
|
// src/plugins/builtin/guardrail.ts
|
|
12172
|
+
init_content();
|
|
11602
12173
|
var ALLOW = { action: "allow" };
|
|
11603
12174
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
11604
12175
|
var RetryableGuardrailError = class extends Error {
|
|
@@ -11885,7 +12456,7 @@ function createGuardrailPlugin(options) {
|
|
|
11885
12456
|
return { decision: "deny", reason: verdict.message ?? "Request blocked by guardrail policy." };
|
|
11886
12457
|
},
|
|
11887
12458
|
postModelCall: async ({ message }, ctx) => {
|
|
11888
|
-
const text =
|
|
12459
|
+
const text = extractMessageText(message);
|
|
11889
12460
|
if (!text) return void 0;
|
|
11890
12461
|
const verdict = await check({ phase: "output", content: text, hook: "postModelCall" }, ctx);
|
|
11891
12462
|
if (!raiseIfFailed(verdict, ctx) || verdict.action === "allow") return void 0;
|
|
@@ -12155,6 +12726,7 @@ function portkeyGuardrail(config = {}) {
|
|
|
12155
12726
|
}
|
|
12156
12727
|
|
|
12157
12728
|
// src/plugins/builtin/piiRedaction.ts
|
|
12729
|
+
init_content();
|
|
12158
12730
|
function isValidTckn(value) {
|
|
12159
12731
|
const digits = value.replace(/\D/g, "");
|
|
12160
12732
|
if (digits.length !== 11 || digits[0] === "0") return false;
|
|
@@ -12274,7 +12846,7 @@ function piiRedaction(config = {}) {
|
|
|
12274
12846
|
},
|
|
12275
12847
|
postModelCall: ({ message }, ctx) => {
|
|
12276
12848
|
if (!applied.has("modelOutput")) return void 0;
|
|
12277
|
-
const text =
|
|
12849
|
+
const text = extractMessageText(message);
|
|
12278
12850
|
if (!text) return void 0;
|
|
12279
12851
|
const result = redactText(text, detectors, mask);
|
|
12280
12852
|
if (result.findings.length === 0) return void 0;
|
|
@@ -13362,12 +13934,12 @@ function responseCache(config = {}) {
|
|
|
13362
13934
|
};
|
|
13363
13935
|
}
|
|
13364
13936
|
function signRequest(req) {
|
|
13365
|
-
const now = /* @__PURE__ */ new Date();
|
|
13937
|
+
const now = req.now ?? /* @__PURE__ */ new Date();
|
|
13366
13938
|
const dateStamp = toDateStamp(now);
|
|
13367
13939
|
const amzDate = toAmzDate(now);
|
|
13368
13940
|
const parsed = new URL(req.url);
|
|
13369
13941
|
const host = parsed.host;
|
|
13370
|
-
const canonicalUri = parsed.pathname || "/";
|
|
13942
|
+
const canonicalUri = (parsed.pathname || "/").split("/").map((seg) => encodeURIComponent(seg).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase())).join("/");
|
|
13371
13943
|
const canonicalQuerystring = sortQueryParams(parsed.searchParams);
|
|
13372
13944
|
const headers = {
|
|
13373
13945
|
...req.headers,
|
|
@@ -14681,140 +15253,8 @@ function languageGuard(config) {
|
|
|
14681
15253
|
};
|
|
14682
15254
|
}
|
|
14683
15255
|
|
|
14684
|
-
// src/
|
|
14685
|
-
|
|
14686
|
-
return {
|
|
14687
|
-
inputTokens: 0,
|
|
14688
|
-
outputTokens: 0,
|
|
14689
|
-
totalTokens: 0,
|
|
14690
|
-
cachedInputTokens: 0,
|
|
14691
|
-
cachedWriteTokens: 0,
|
|
14692
|
-
cachedOutputTokens: 0,
|
|
14693
|
-
reasoningTokens: 0
|
|
14694
|
-
};
|
|
14695
|
-
}
|
|
14696
|
-
var ProviderError = class extends Error {
|
|
14697
|
-
constructor(message, provider, statusCode, responseBody) {
|
|
14698
|
-
super(message);
|
|
14699
|
-
this.provider = provider;
|
|
14700
|
-
this.statusCode = statusCode;
|
|
14701
|
-
this.responseBody = responseBody;
|
|
14702
|
-
this.name = "ProviderError";
|
|
14703
|
-
}
|
|
14704
|
-
};
|
|
14705
|
-
|
|
14706
|
-
// src/providers/base.ts
|
|
14707
|
-
var DEFAULT_RETRY = {
|
|
14708
|
-
maxRetries: 3,
|
|
14709
|
-
baseDelayMs: 500,
|
|
14710
|
-
maxDelayMs: 8e3
|
|
14711
|
-
};
|
|
14712
|
-
var BaseProvider = class {
|
|
14713
|
-
constructor() {
|
|
14714
|
-
this.retryPolicy = DEFAULT_RETRY;
|
|
14715
|
-
}
|
|
14716
|
-
/**
|
|
14717
|
-
* Helper concrete providers can wrap their fetch with. Retries 429/5xx
|
|
14718
|
-
* with exponential backoff and respects Retry-After if the response
|
|
14719
|
-
* carries one.
|
|
14720
|
-
*/
|
|
14721
|
-
async doFetchWithRetry(execute) {
|
|
14722
|
-
const policy = this.retryPolicy;
|
|
14723
|
-
const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
|
|
14724
|
-
const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
|
|
14725
|
-
const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
|
|
14726
|
-
let lastResp;
|
|
14727
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
14728
|
-
const res = await execute();
|
|
14729
|
-
const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
|
|
14730
|
-
if (!isRetryable) return res;
|
|
14731
|
-
const predicate = policy.shouldRetry;
|
|
14732
|
-
if (predicate && !predicate({ status: res.status }, attempt)) return res;
|
|
14733
|
-
if (attempt === maxRetries) {
|
|
14734
|
-
lastResp = res;
|
|
14735
|
-
break;
|
|
14736
|
-
}
|
|
14737
|
-
const retryAfter = res.headers.get("retry-after");
|
|
14738
|
-
let delay2 = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
|
|
14739
|
-
if (retryAfter) {
|
|
14740
|
-
const asNumber = Number(retryAfter);
|
|
14741
|
-
if (!Number.isNaN(asNumber)) delay2 = Math.min(maxDelay, Math.max(delay2, asNumber * 1e3));
|
|
14742
|
-
}
|
|
14743
|
-
try {
|
|
14744
|
-
await res.text();
|
|
14745
|
-
} catch {
|
|
14746
|
-
}
|
|
14747
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
14748
|
-
}
|
|
14749
|
-
return lastResp;
|
|
14750
|
-
}
|
|
14751
|
-
};
|
|
14752
|
-
|
|
14753
|
-
// src/providers/utils/sse.ts
|
|
14754
|
-
async function* parseSSEStream(body, signal) {
|
|
14755
|
-
const reader = body.getReader();
|
|
14756
|
-
const decoder = new TextDecoder();
|
|
14757
|
-
let buffer = "";
|
|
14758
|
-
try {
|
|
14759
|
-
while (true) {
|
|
14760
|
-
if (signal?.aborted) ;
|
|
14761
|
-
const { done, value } = await reader.read();
|
|
14762
|
-
if (done) break;
|
|
14763
|
-
buffer += decoder.decode(value, { stream: true });
|
|
14764
|
-
const parts = buffer.split(/\r?\n\r?\n/);
|
|
14765
|
-
buffer = parts.pop() ?? "";
|
|
14766
|
-
for (const part of parts) {
|
|
14767
|
-
if (!part.trim()) continue;
|
|
14768
|
-
const event = parseSSEBlock(part);
|
|
14769
|
-
if (event) yield event;
|
|
14770
|
-
}
|
|
14771
|
-
}
|
|
14772
|
-
if (buffer.trim()) {
|
|
14773
|
-
const event = parseSSEBlock(buffer);
|
|
14774
|
-
if (event) yield event;
|
|
14775
|
-
}
|
|
14776
|
-
} finally {
|
|
14777
|
-
reader.releaseLock();
|
|
14778
|
-
}
|
|
14779
|
-
}
|
|
14780
|
-
function parseSSEBlock(block) {
|
|
14781
|
-
let event;
|
|
14782
|
-
let id;
|
|
14783
|
-
let retry;
|
|
14784
|
-
const dataLines = [];
|
|
14785
|
-
for (const line of block.split(/\r?\n/)) {
|
|
14786
|
-
if (line.startsWith(":")) continue;
|
|
14787
|
-
const colonIdx = line.indexOf(":");
|
|
14788
|
-
let field;
|
|
14789
|
-
let value;
|
|
14790
|
-
if (colonIdx === -1) {
|
|
14791
|
-
field = line;
|
|
14792
|
-
value = "";
|
|
14793
|
-
} else {
|
|
14794
|
-
field = line.slice(0, colonIdx);
|
|
14795
|
-
value = line.slice(colonIdx + 1);
|
|
14796
|
-
if (value.startsWith(" ")) value = value.slice(1);
|
|
14797
|
-
}
|
|
14798
|
-
switch (field) {
|
|
14799
|
-
case "event":
|
|
14800
|
-
event = value;
|
|
14801
|
-
break;
|
|
14802
|
-
case "data":
|
|
14803
|
-
dataLines.push(value);
|
|
14804
|
-
break;
|
|
14805
|
-
case "id":
|
|
14806
|
-
id = value;
|
|
14807
|
-
break;
|
|
14808
|
-
case "retry": {
|
|
14809
|
-
const n = parseInt(value, 10);
|
|
14810
|
-
if (!isNaN(n)) retry = n;
|
|
14811
|
-
break;
|
|
14812
|
-
}
|
|
14813
|
-
}
|
|
14814
|
-
}
|
|
14815
|
-
if (dataLines.length === 0) return null;
|
|
14816
|
-
return { event, data: dataLines.join("\n"), id, retry };
|
|
14817
|
-
}
|
|
15256
|
+
// src/index.ts
|
|
15257
|
+
init_content();
|
|
14818
15258
|
|
|
14819
15259
|
// src/providers/utils/reasoning.ts
|
|
14820
15260
|
var ANTHROPIC_EFFORT_BUDGET = {
|
|
@@ -14837,6 +15277,7 @@ var GEMINI_EFFORT_BUDGET = {
|
|
|
14837
15277
|
high: 12e3
|
|
14838
15278
|
};
|
|
14839
15279
|
var THINKING_OUTPUT_RESERVE = 1024;
|
|
15280
|
+
var MIN_THINKING_BUDGET = 1024;
|
|
14840
15281
|
var THINKING_MIN_MAX_TOKENS = 4096;
|
|
14841
15282
|
function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
|
|
14842
15283
|
if (!reasoning) return;
|
|
@@ -14852,8 +15293,48 @@ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
|
|
|
14852
15293
|
if (reasoning.providerExtras) Object.assign(body, reasoning.providerExtras);
|
|
14853
15294
|
}
|
|
14854
15295
|
}
|
|
14855
|
-
function
|
|
15296
|
+
function supportsAdaptiveThinking(model) {
|
|
15297
|
+
if (typeof model !== "string") return false;
|
|
15298
|
+
const id = model.toLowerCase();
|
|
15299
|
+
const at = id.indexOf("claude-");
|
|
15300
|
+
if (at < 0) return false;
|
|
15301
|
+
for (const tok of id.slice(at + 7).split("-")) {
|
|
15302
|
+
if (tok === "fable") return true;
|
|
15303
|
+
const m = /^(\d+)(?:@|$)/.exec(tok);
|
|
15304
|
+
if (m) {
|
|
15305
|
+
if (m[1].length <= 2) return Number(m[1]) >= 5;
|
|
15306
|
+
return false;
|
|
15307
|
+
}
|
|
15308
|
+
}
|
|
15309
|
+
return false;
|
|
15310
|
+
}
|
|
15311
|
+
function resolveThinkingForm(model, mode) {
|
|
15312
|
+
if (mode === "adaptive" || mode === "budget") return mode;
|
|
15313
|
+
return supportsAdaptiveThinking(model) ? "adaptive" : "budget";
|
|
15314
|
+
}
|
|
15315
|
+
function applyAdaptiveThinking(_body, reasoning, setThinking, setOutputConfig) {
|
|
15316
|
+
const requested = normalizeEffort(reasoning.effort);
|
|
15317
|
+
if (requested === "none") return false;
|
|
15318
|
+
let effort;
|
|
15319
|
+
if (requested) effort = requested === "minimal" ? "low" : requested;
|
|
15320
|
+
else if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
|
|
15321
|
+
effort = reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.low ? "low" : reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.medium ? "medium" : "high";
|
|
15322
|
+
}
|
|
15323
|
+
if (!effort) return false;
|
|
15324
|
+
const thinking = { type: "adaptive" };
|
|
15325
|
+
if (reasoning.includeThoughts !== false) thinking.display = "summarized";
|
|
15326
|
+
if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
|
|
15327
|
+
setThinking({ thinking });
|
|
15328
|
+
setOutputConfig({ effort });
|
|
15329
|
+
return true;
|
|
15330
|
+
}
|
|
15331
|
+
function applyAnthropicReasoning(body, reasoning, opts) {
|
|
14856
15332
|
if (!reasoning) return;
|
|
15333
|
+
if (resolveThinkingForm(opts?.model ?? body.model, opts?.mode) === "adaptive") {
|
|
15334
|
+
const on = applyAdaptiveThinking(body, reasoning, (extra) => body.thinking = extra.thinking, (o) => body.output_config = { ...body.output_config ?? {}, ...o });
|
|
15335
|
+
if (on) stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
|
|
15336
|
+
return;
|
|
15337
|
+
}
|
|
14857
15338
|
let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
|
|
14858
15339
|
if (!budget) return;
|
|
14859
15340
|
const currentMax = typeof body.max_tokens === "number" ? body.max_tokens : 0;
|
|
@@ -14861,14 +15342,20 @@ function applyAnthropicReasoning(body, reasoning) {
|
|
|
14861
15342
|
if (currentMax < requiredMax) {
|
|
14862
15343
|
body.max_tokens = requiredMax;
|
|
14863
15344
|
}
|
|
14864
|
-
budget = clampBudget(budget, body.max_tokens);
|
|
15345
|
+
budget = Math.max(MIN_THINKING_BUDGET, clampBudget(budget, body.max_tokens));
|
|
14865
15346
|
const thinking = { type: "enabled", budget_tokens: budget };
|
|
14866
15347
|
if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
|
|
14867
15348
|
body.thinking = thinking;
|
|
14868
15349
|
stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
|
|
14869
15350
|
}
|
|
14870
|
-
function applyBedrockReasoning(body, reasoning) {
|
|
15351
|
+
function applyBedrockReasoning(body, reasoning, opts) {
|
|
14871
15352
|
if (!reasoning) return;
|
|
15353
|
+
if (resolveThinkingForm(opts?.model ?? body.modelId, opts?.mode) === "adaptive") {
|
|
15354
|
+
const fields = () => body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
|
|
15355
|
+
const on = applyAdaptiveThinking(body, reasoning, (extra2) => fields().thinking = extra2.thinking, (o) => fields().output_config = { ...fields().output_config ?? {}, ...o });
|
|
15356
|
+
if (on && body.inferenceConfig) stripSamplingParams(body.inferenceConfig, ["temperature", "topP"]);
|
|
15357
|
+
return;
|
|
15358
|
+
}
|
|
14872
15359
|
let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
|
|
14873
15360
|
if (!budget) return;
|
|
14874
15361
|
const inference = body.inferenceConfig ?? (body.inferenceConfig = {});
|
|
@@ -14877,7 +15364,7 @@ function applyBedrockReasoning(body, reasoning) {
|
|
|
14877
15364
|
if (currentMax < requiredMax) {
|
|
14878
15365
|
inference.maxTokens = requiredMax;
|
|
14879
15366
|
}
|
|
14880
|
-
budget = clampBudget(budget, inference.maxTokens);
|
|
15367
|
+
budget = Math.max(MIN_THINKING_BUDGET, clampBudget(budget, inference.maxTokens));
|
|
14881
15368
|
const extra = body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
|
|
14882
15369
|
const thinking = { type: "enabled", budget_tokens: budget };
|
|
14883
15370
|
if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
|
|
@@ -14887,8 +15374,9 @@ function applyBedrockReasoning(body, reasoning) {
|
|
|
14887
15374
|
function applyGeminiReasoning(generationConfig, reasoning) {
|
|
14888
15375
|
if (!reasoning) return;
|
|
14889
15376
|
const budget = resolveBudget(reasoning, GEMINI_EFFORT_BUDGET);
|
|
14890
|
-
|
|
14891
|
-
|
|
15377
|
+
const level = typeof reasoning.thinkingLevel === "string" && reasoning.thinkingLevel ? reasoning.thinkingLevel : void 0;
|
|
15378
|
+
if (budget == null && !level) return;
|
|
15379
|
+
const thinkingConfig = level && budget !== 0 ? { thinkingLevel: level } : { thinkingBudget: budget ?? 0 };
|
|
14892
15380
|
if (reasoning.includeThoughts) thinkingConfig.includeThoughts = true;
|
|
14893
15381
|
if (reasoning.providerExtras) Object.assign(thinkingConfig, reasoning.providerExtras);
|
|
14894
15382
|
generationConfig.thinkingConfig = thinkingConfig;
|
|
@@ -14919,6 +15407,73 @@ function resolveBudget(reasoning, effortBudget) {
|
|
|
14919
15407
|
return void 0;
|
|
14920
15408
|
}
|
|
14921
15409
|
|
|
15410
|
+
// src/providers/utils/sse.ts
|
|
15411
|
+
async function* parseSSEStream(body, signal) {
|
|
15412
|
+
const reader = body.getReader();
|
|
15413
|
+
const decoder = new TextDecoder();
|
|
15414
|
+
let buffer = "";
|
|
15415
|
+
try {
|
|
15416
|
+
while (true) {
|
|
15417
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
15418
|
+
const { done, value } = await reader.read();
|
|
15419
|
+
if (done) break;
|
|
15420
|
+
buffer += decoder.decode(value, { stream: true });
|
|
15421
|
+
const parts = buffer.split(/\r?\n\r?\n/);
|
|
15422
|
+
buffer = parts.pop() ?? "";
|
|
15423
|
+
for (const part of parts) {
|
|
15424
|
+
if (!part.trim()) continue;
|
|
15425
|
+
const event = parseSSEBlock(part);
|
|
15426
|
+
if (event) yield event;
|
|
15427
|
+
}
|
|
15428
|
+
}
|
|
15429
|
+
if (buffer.trim()) {
|
|
15430
|
+
const event = parseSSEBlock(buffer);
|
|
15431
|
+
if (event) yield event;
|
|
15432
|
+
}
|
|
15433
|
+
} finally {
|
|
15434
|
+
reader.cancel().catch(() => void 0);
|
|
15435
|
+
reader.releaseLock();
|
|
15436
|
+
}
|
|
15437
|
+
}
|
|
15438
|
+
function parseSSEBlock(block) {
|
|
15439
|
+
let event;
|
|
15440
|
+
let id;
|
|
15441
|
+
let retry;
|
|
15442
|
+
const dataLines = [];
|
|
15443
|
+
for (const line of block.split(/\r?\n/)) {
|
|
15444
|
+
if (line.startsWith(":")) continue;
|
|
15445
|
+
const colonIdx = line.indexOf(":");
|
|
15446
|
+
let field;
|
|
15447
|
+
let value;
|
|
15448
|
+
if (colonIdx === -1) {
|
|
15449
|
+
field = line;
|
|
15450
|
+
value = "";
|
|
15451
|
+
} else {
|
|
15452
|
+
field = line.slice(0, colonIdx);
|
|
15453
|
+
value = line.slice(colonIdx + 1);
|
|
15454
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
15455
|
+
}
|
|
15456
|
+
switch (field) {
|
|
15457
|
+
case "event":
|
|
15458
|
+
event = value;
|
|
15459
|
+
break;
|
|
15460
|
+
case "data":
|
|
15461
|
+
dataLines.push(value);
|
|
15462
|
+
break;
|
|
15463
|
+
case "id":
|
|
15464
|
+
id = value;
|
|
15465
|
+
break;
|
|
15466
|
+
case "retry": {
|
|
15467
|
+
const n = parseInt(value, 10);
|
|
15468
|
+
if (!isNaN(n)) retry = n;
|
|
15469
|
+
break;
|
|
15470
|
+
}
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
if (dataLines.length === 0) return null;
|
|
15474
|
+
return { event, data: dataLines.join("\n"), id, retry };
|
|
15475
|
+
}
|
|
15476
|
+
|
|
14922
15477
|
// src/providers/utils/media.ts
|
|
14923
15478
|
var EXTENSION_MIME = {
|
|
14924
15479
|
jpg: "image/jpeg",
|
|
@@ -14994,12 +15549,12 @@ function sanitizeBedrockDocName(name, fallback = "document") {
|
|
|
14994
15549
|
// src/providers/utils/jsonSchemaRefs.ts
|
|
14995
15550
|
var DEFINITION_KEYS = ["definitions", "$defs"];
|
|
14996
15551
|
function inlineJsonSchemaRefs(schema) {
|
|
14997
|
-
if (!
|
|
15552
|
+
if (!isObject2(schema) || !containsRef(schema)) return schema;
|
|
14998
15553
|
const root = structuredClone(schema);
|
|
14999
15554
|
const selfReaching = /* @__PURE__ */ new Map();
|
|
15000
15555
|
const targetOf = (ref) => {
|
|
15001
15556
|
const target = resolvePointer(root, ref);
|
|
15002
|
-
return
|
|
15557
|
+
return isObject2(target) ? target : void 0;
|
|
15003
15558
|
};
|
|
15004
15559
|
const reachesItself = (target) => {
|
|
15005
15560
|
const known = selfReaching.get(target);
|
|
@@ -15007,7 +15562,7 @@ function inlineJsonSchemaRefs(schema) {
|
|
|
15007
15562
|
const seen = /* @__PURE__ */ new Set();
|
|
15008
15563
|
const walk = (node) => {
|
|
15009
15564
|
if (Array.isArray(node)) return node.some(walk);
|
|
15010
|
-
if (!
|
|
15565
|
+
if (!isObject2(node)) return false;
|
|
15011
15566
|
if (typeof node.$ref === "string") {
|
|
15012
15567
|
const next = targetOf(node.$ref);
|
|
15013
15568
|
if (next === target) return true;
|
|
@@ -15025,7 +15580,7 @@ function inlineJsonSchemaRefs(schema) {
|
|
|
15025
15580
|
const keptRefs = /* @__PURE__ */ new Set();
|
|
15026
15581
|
const expand = (node) => {
|
|
15027
15582
|
if (Array.isArray(node)) return node.map(expand);
|
|
15028
|
-
if (!
|
|
15583
|
+
if (!isObject2(node)) return node;
|
|
15029
15584
|
if (typeof node.$ref === "string") {
|
|
15030
15585
|
const target = targetOf(node.$ref);
|
|
15031
15586
|
if (target && !reachesItself(target)) {
|
|
@@ -15047,7 +15602,7 @@ function inlineJsonSchemaRefs(schema) {
|
|
|
15047
15602
|
for (const key of DEFINITION_KEYS) delete result[key];
|
|
15048
15603
|
for (const key of DEFINITION_KEYS) {
|
|
15049
15604
|
const block = root[key];
|
|
15050
|
-
if (!
|
|
15605
|
+
if (!isObject2(block)) continue;
|
|
15051
15606
|
const needed = neededEntries(key, block, keptRefs);
|
|
15052
15607
|
if (needed.length === 0) continue;
|
|
15053
15608
|
const kept = {};
|
|
@@ -15075,13 +15630,13 @@ function collectRefs(node, into) {
|
|
|
15075
15630
|
node.forEach((child) => collectRefs(child, into));
|
|
15076
15631
|
return;
|
|
15077
15632
|
}
|
|
15078
|
-
if (!
|
|
15633
|
+
if (!isObject2(node)) return;
|
|
15079
15634
|
if (typeof node.$ref === "string") into.push(node.$ref);
|
|
15080
15635
|
for (const [k, value] of Object.entries(node)) if (k !== "$ref") collectRefs(value, into);
|
|
15081
15636
|
}
|
|
15082
15637
|
function containsRef(node) {
|
|
15083
15638
|
if (Array.isArray(node)) return node.some(containsRef);
|
|
15084
|
-
if (!
|
|
15639
|
+
if (!isObject2(node)) return false;
|
|
15085
15640
|
if (typeof node.$ref === "string") return true;
|
|
15086
15641
|
return Object.values(node).some(containsRef);
|
|
15087
15642
|
}
|
|
@@ -15098,7 +15653,7 @@ function resolvePointer(document, ref) {
|
|
|
15098
15653
|
if (Array.isArray(current)) {
|
|
15099
15654
|
if (!/^\d+$/.test(segment)) return void 0;
|
|
15100
15655
|
current = current[Number(segment)];
|
|
15101
|
-
} else if (
|
|
15656
|
+
} else if (isObject2(current) && Object.prototype.hasOwnProperty.call(current, segment)) {
|
|
15102
15657
|
current = current[segment];
|
|
15103
15658
|
} else {
|
|
15104
15659
|
return void 0;
|
|
@@ -15106,15 +15661,68 @@ function resolvePointer(document, ref) {
|
|
|
15106
15661
|
}
|
|
15107
15662
|
return current;
|
|
15108
15663
|
}
|
|
15109
|
-
function
|
|
15664
|
+
function isObject2(value) {
|
|
15110
15665
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15111
15666
|
}
|
|
15112
15667
|
|
|
15668
|
+
// src/providers/utils/embeddings.ts
|
|
15669
|
+
function chunkArray(items, size) {
|
|
15670
|
+
const n = Math.max(1, Math.floor(size));
|
|
15671
|
+
const out = [];
|
|
15672
|
+
for (let i = 0; i < items.length; i += n) out.push(items.slice(i, i + n));
|
|
15673
|
+
return out;
|
|
15674
|
+
}
|
|
15675
|
+
async function mapWithConcurrency(items, limit, fn, signal) {
|
|
15676
|
+
const results = new Array(items.length);
|
|
15677
|
+
let next = 0;
|
|
15678
|
+
let failure;
|
|
15679
|
+
let failed = false;
|
|
15680
|
+
const worker = async () => {
|
|
15681
|
+
while (!failed) {
|
|
15682
|
+
if (signal?.aborted) throw signal.reason ?? new Error("aborted");
|
|
15683
|
+
const i = next++;
|
|
15684
|
+
if (i >= items.length) return;
|
|
15685
|
+
try {
|
|
15686
|
+
results[i] = await fn(items[i], i);
|
|
15687
|
+
} catch (err) {
|
|
15688
|
+
failed = true;
|
|
15689
|
+
failure = err;
|
|
15690
|
+
return;
|
|
15691
|
+
}
|
|
15692
|
+
}
|
|
15693
|
+
};
|
|
15694
|
+
await Promise.all(Array.from({ length: Math.min(Math.max(1, limit), items.length) }, worker));
|
|
15695
|
+
if (failed) throw failure;
|
|
15696
|
+
return results;
|
|
15697
|
+
}
|
|
15698
|
+
function decodeBase64Vector(b64) {
|
|
15699
|
+
const buf = Buffer.from(b64, "base64");
|
|
15700
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
15701
|
+
const out = new Array(Math.floor(buf.byteLength / 4));
|
|
15702
|
+
for (let i = 0; i < out.length; i++) out[i] = view.getFloat32(i * 4, true);
|
|
15703
|
+
return out;
|
|
15704
|
+
}
|
|
15705
|
+
function assertVectorCount(provider, vectors, expected) {
|
|
15706
|
+
if (vectors.length !== expected) {
|
|
15707
|
+
throw new ProviderError(
|
|
15708
|
+
`${provider} embeddings returned ${vectors.length} vectors for ${expected} inputs`,
|
|
15709
|
+
provider,
|
|
15710
|
+
502
|
|
15711
|
+
);
|
|
15712
|
+
}
|
|
15713
|
+
}
|
|
15714
|
+
function requireEmbeddingModel(provider, model) {
|
|
15715
|
+
if (!model) throw new ProviderError(`${provider} embeddings require a model`, provider, 400);
|
|
15716
|
+
return model;
|
|
15717
|
+
}
|
|
15718
|
+
|
|
15113
15719
|
// src/providers/openai.ts
|
|
15114
|
-
var OpenAIProvider = class
|
|
15720
|
+
var OpenAIProvider = class extends BaseProvider {
|
|
15115
15721
|
constructor(config) {
|
|
15116
15722
|
super();
|
|
15117
15723
|
this.providerName = "openai";
|
|
15724
|
+
this.configureTransport(config);
|
|
15725
|
+
this.maxTokensParam = config.maxTokensParam ?? "auto";
|
|
15118
15726
|
this.apiKey = config.apiKey;
|
|
15119
15727
|
this.baseURL = (config.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
15120
15728
|
this.defaultModel = config.defaultModel ?? "gpt-4o";
|
|
@@ -15122,10 +15730,14 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15122
15730
|
...config.defaultHeaders,
|
|
15123
15731
|
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
15124
15732
|
};
|
|
15125
|
-
|
|
15126
|
-
const
|
|
15127
|
-
this.
|
|
15128
|
-
this.
|
|
15733
|
+
const legacy = config.responsesApi;
|
|
15734
|
+
const explicit = config.api === "chat" || config.api === "responses" ? config.api : void 0;
|
|
15735
|
+
this.responsesApi = explicit === "responses" ? "always" : explicit === "chat" ? "never" : legacy ?? (config._autoMeansChat ? "never" : "auto");
|
|
15736
|
+
this.apiExplicitChat = explicit === "chat" || explicit === void 0 && legacy === "never";
|
|
15737
|
+
this.apiExplicitResponses = this.responsesApi === "always";
|
|
15738
|
+
this.apiResponsesNative = explicit === "responses";
|
|
15739
|
+
this.responsesApiModels = [...config.responsesApiModels ?? []];
|
|
15740
|
+
this.responsesStreaming = config.responsesStreaming ?? this.apiResponsesNative;
|
|
15129
15741
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
15130
15742
|
}
|
|
15131
15743
|
/** True when the caller listed this request's model in `responsesApiModels`. */
|
|
@@ -15151,9 +15763,10 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15151
15763
|
* A model listed in `responsesApiModels` is the caller stating the answer
|
|
15152
15764
|
* for that model alone; `never` still overrides it. */
|
|
15153
15765
|
useResponsesApi(request) {
|
|
15154
|
-
if (this.
|
|
15155
|
-
if (this.
|
|
15766
|
+
if (this.apiExplicitChat) return false;
|
|
15767
|
+
if (this.apiExplicitResponses) return true;
|
|
15156
15768
|
if (this.isResponsesApiModel(request)) return true;
|
|
15769
|
+
if (this.responsesApi !== "auto") return false;
|
|
15157
15770
|
if (!request.reasoning) return false;
|
|
15158
15771
|
return isReasoningModel(request.model || this.defaultModel);
|
|
15159
15772
|
}
|
|
@@ -15162,13 +15775,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15162
15775
|
return this.completeResponses(request);
|
|
15163
15776
|
}
|
|
15164
15777
|
const body = this.buildRequestBody(request, false);
|
|
15165
|
-
const res = await this.doFetch(body);
|
|
15778
|
+
const res = await this.doFetch(body, request.signal);
|
|
15166
15779
|
const json = await res.json();
|
|
15167
15780
|
return this.parseResponse(json);
|
|
15168
15781
|
}
|
|
15169
15782
|
async *completeStream(request) {
|
|
15170
15783
|
if (this.useResponsesApi(request)) {
|
|
15171
|
-
if (this.responsesStreaming && this.isResponsesApiModel(request)) {
|
|
15784
|
+
if (this.responsesStreaming && (this.apiResponsesNative || this.isResponsesApiModel(request))) {
|
|
15172
15785
|
yield* this.streamResponses(request);
|
|
15173
15786
|
return;
|
|
15174
15787
|
}
|
|
@@ -15187,13 +15800,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15187
15800
|
return;
|
|
15188
15801
|
}
|
|
15189
15802
|
const body = this.buildRequestBody(request, true);
|
|
15190
|
-
const res = await this.doFetch(body);
|
|
15803
|
+
const res = await this.doFetch(body, request.signal);
|
|
15191
15804
|
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
15192
15805
|
let id = "";
|
|
15193
15806
|
let model = "";
|
|
15194
15807
|
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
15195
15808
|
let usage;
|
|
15196
|
-
for await (const event of parseSSEStream(res.body)) {
|
|
15809
|
+
for await (const event of parseSSEStream(res.body, request.signal)) {
|
|
15197
15810
|
if (event.data === "[DONE]") break;
|
|
15198
15811
|
let chunk;
|
|
15199
15812
|
try {
|
|
@@ -15218,9 +15831,14 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15218
15831
|
delta: {},
|
|
15219
15832
|
finishReason: mapFinishReason(choice.finish_reason)
|
|
15220
15833
|
};
|
|
15834
|
+
if (typeof choice.finish_reason === "string") out.rawFinishReason = choice.finish_reason;
|
|
15835
|
+
const fields = pickProviderFields(chunk, delta, choice);
|
|
15836
|
+
if (fields) out.providerFields = fields;
|
|
15221
15837
|
if (delta.content) {
|
|
15222
15838
|
out.delta.content = delta.content;
|
|
15223
15839
|
}
|
|
15840
|
+
const reasoningDelta = extractReasoningText(delta);
|
|
15841
|
+
if (reasoningDelta) out.delta.reasoning = { summary: reasoningDelta };
|
|
15224
15842
|
if (delta.tool_calls) {
|
|
15225
15843
|
const partials = [];
|
|
15226
15844
|
for (const tc of delta.tool_calls) {
|
|
@@ -15232,7 +15850,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15232
15850
|
if (tc.id) buf.id = tc.id;
|
|
15233
15851
|
if (tc.function?.name) buf.name = tc.function.name;
|
|
15234
15852
|
if (tc.function?.arguments) buf.arguments += tc.function.arguments;
|
|
15235
|
-
partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments });
|
|
15853
|
+
partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments, index: idx });
|
|
15236
15854
|
}
|
|
15237
15855
|
out.delta.toolCalls = partials;
|
|
15238
15856
|
}
|
|
@@ -15240,6 +15858,51 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15240
15858
|
yield out;
|
|
15241
15859
|
}
|
|
15242
15860
|
}
|
|
15861
|
+
// ─── Embeddings ──────────────────────────────────────────────────────────
|
|
15862
|
+
/** POST /embeddings, batched (default 2048 inputs per request, the OpenAI cap). */
|
|
15863
|
+
async embed(request) {
|
|
15864
|
+
const model = requireEmbeddingModel(this.providerName, request.model);
|
|
15865
|
+
const batches = chunkArray(request.input, request.batchSize ?? 2048);
|
|
15866
|
+
const vectors = [];
|
|
15867
|
+
const raw = [];
|
|
15868
|
+
let inputTokens = 0;
|
|
15869
|
+
let totalTokens = 0;
|
|
15870
|
+
let responseModel = model;
|
|
15871
|
+
for (const batch of batches) {
|
|
15872
|
+
const body = { model, input: batch };
|
|
15873
|
+
if (request.dimensions != null) body.dimensions = request.dimensions;
|
|
15874
|
+
if (request.encodingFormat) body.encoding_format = request.encodingFormat;
|
|
15875
|
+
if (request.extra) Object.assign(body, request.extra);
|
|
15876
|
+
const res = await this.embeddingsFetch(body, request.signal);
|
|
15877
|
+
const json = await res.json();
|
|
15878
|
+
raw.push(json);
|
|
15879
|
+
const data = Array.isArray(json.data) ? [...json.data] : [];
|
|
15880
|
+
data.sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
15881
|
+
for (const d of data) {
|
|
15882
|
+
vectors.push(typeof d.embedding === "string" ? decodeBase64Vector(d.embedding) : d.embedding);
|
|
15883
|
+
}
|
|
15884
|
+
inputTokens += json.usage?.prompt_tokens ?? 0;
|
|
15885
|
+
totalTokens += json.usage?.total_tokens ?? json.usage?.prompt_tokens ?? 0;
|
|
15886
|
+
responseModel = json.model ?? responseModel;
|
|
15887
|
+
}
|
|
15888
|
+
assertVectorCount(this.providerName, vectors, request.input.length);
|
|
15889
|
+
return { model: responseModel, vectors, usage: { inputTokens, totalTokens }, raw };
|
|
15890
|
+
}
|
|
15891
|
+
async embeddingsFetch(body, signal) {
|
|
15892
|
+
const url = `${this.baseURL}/embeddings`;
|
|
15893
|
+
const headers = {
|
|
15894
|
+
"Content-Type": "application/json",
|
|
15895
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
15896
|
+
...this.defaultHeaders
|
|
15897
|
+
};
|
|
15898
|
+
const payload = JSON.stringify(body);
|
|
15899
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
15900
|
+
if (!res.ok) {
|
|
15901
|
+
const text = await res.text().catch(() => "");
|
|
15902
|
+
throw new ProviderError(`OpenAI embeddings API error ${res.status}: ${text}`, this.providerName, res.status, text);
|
|
15903
|
+
}
|
|
15904
|
+
return res;
|
|
15905
|
+
}
|
|
15243
15906
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
15244
15907
|
buildRequestBody(request, stream) {
|
|
15245
15908
|
const body = {
|
|
@@ -15247,15 +15910,27 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15247
15910
|
messages: request.messages.map((m) => this.toOpenAIMessage(m)),
|
|
15248
15911
|
stream
|
|
15249
15912
|
};
|
|
15250
|
-
if (stream) {
|
|
15913
|
+
if (stream && request.streamUsage !== false) {
|
|
15251
15914
|
body.stream_options = { include_usage: true };
|
|
15252
15915
|
}
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
if (request.
|
|
15916
|
+
const modelId = String(body.model);
|
|
15917
|
+
const reasoningModel = isReasoningModel(modelId) && !/chat/i.test(modelId);
|
|
15918
|
+
if (request.temperature != null && !reasoningModel) body.temperature = request.temperature;
|
|
15919
|
+
if (request.maxTokens != null) {
|
|
15920
|
+
const useCompletionTokens = this.maxTokensParam === "max_completion_tokens" || this.maxTokensParam === "auto" && isReasoningModel(modelId);
|
|
15921
|
+
body[useCompletionTokens ? "max_completion_tokens" : "max_tokens"] = request.maxTokens;
|
|
15922
|
+
}
|
|
15923
|
+
if (request.topP != null && !reasoningModel) body.top_p = request.topP;
|
|
15256
15924
|
if (request.stop) body.stop = request.stop;
|
|
15925
|
+
if (request.presencePenalty != null) body.presence_penalty = request.presencePenalty;
|
|
15926
|
+
if (request.frequencyPenalty != null) body.frequency_penalty = request.frequencyPenalty;
|
|
15927
|
+
if (request.seed != null) body.seed = request.seed;
|
|
15928
|
+
if (request.n != null) body.n = request.n;
|
|
15929
|
+
if (request.logprobs != null) body.logprobs = request.logprobs;
|
|
15930
|
+
if (request.topLogprobs != null) body.top_logprobs = request.topLogprobs;
|
|
15257
15931
|
if (request.tools?.length) {
|
|
15258
15932
|
body.tools = request.tools.map((t) => this.toOpenAITool(t));
|
|
15933
|
+
if (request.parallelToolCalls != null) body.parallel_tool_calls = request.parallelToolCalls;
|
|
15259
15934
|
}
|
|
15260
15935
|
if (request.toolChoice != null && request.tools?.length) {
|
|
15261
15936
|
if (typeof request.toolChoice === "string") {
|
|
@@ -15287,8 +15962,8 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15287
15962
|
// ─── Responses API (reasoning models) ──────────────────────────────────────
|
|
15288
15963
|
async completeResponses(request) {
|
|
15289
15964
|
const body = this.buildResponsesBody(request);
|
|
15290
|
-
if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
|
|
15291
|
-
const res = await this.responsesFetch(body);
|
|
15965
|
+
if (this.apiResponsesNative || this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
|
|
15966
|
+
const res = await this.responsesFetch(body, request.signal);
|
|
15292
15967
|
const json = await res.json();
|
|
15293
15968
|
return this.parseResponsesResponse(json, request.model || this.defaultModel);
|
|
15294
15969
|
}
|
|
@@ -15304,13 +15979,14 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15304
15979
|
const body = this.buildResponsesBody(request);
|
|
15305
15980
|
adaptChatExtrasForResponses(body, request.extra);
|
|
15306
15981
|
body.stream = true;
|
|
15307
|
-
const res = await this.responsesFetch(body);
|
|
15982
|
+
const res = await this.responsesFetch(body, request.signal);
|
|
15308
15983
|
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
15309
15984
|
let id = "";
|
|
15310
15985
|
let model = modelId;
|
|
15311
15986
|
const calls = /* @__PURE__ */ new Map();
|
|
15312
15987
|
let final;
|
|
15313
|
-
|
|
15988
|
+
let streamedSummary = false;
|
|
15989
|
+
for await (const event of parseSSEStream(res.body, request.signal)) {
|
|
15314
15990
|
if (event.data === "[DONE]") break;
|
|
15315
15991
|
let data;
|
|
15316
15992
|
try {
|
|
@@ -15327,19 +16003,28 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15327
16003
|
case "response.output_text.delta":
|
|
15328
16004
|
if (data.delta) yield { id, model, delta: { content: data.delta } };
|
|
15329
16005
|
break;
|
|
16006
|
+
case "response.reasoning_summary_part.added":
|
|
16007
|
+
if (streamedSummary) yield { id, model, delta: { reasoning: { summary: "\n\n" } } };
|
|
16008
|
+
break;
|
|
16009
|
+
case "response.reasoning_summary_text.delta":
|
|
16010
|
+
if (typeof data.delta === "string" && data.delta) {
|
|
16011
|
+
streamedSummary = true;
|
|
16012
|
+
yield { id, model, delta: { reasoning: { summary: data.delta } } };
|
|
16013
|
+
}
|
|
16014
|
+
break;
|
|
15330
16015
|
case "response.output_item.added": {
|
|
15331
16016
|
const item = data.item;
|
|
15332
16017
|
if (item?.type !== "function_call") break;
|
|
15333
|
-
const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "" };
|
|
16018
|
+
const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "", index: calls.size };
|
|
15334
16019
|
calls.set(item.id ?? call.callId, call);
|
|
15335
|
-
yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments }] } };
|
|
16020
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments, index: call.index }] } };
|
|
15336
16021
|
break;
|
|
15337
16022
|
}
|
|
15338
16023
|
case "response.function_call_arguments.delta": {
|
|
15339
16024
|
const call = calls.get(data.item_id);
|
|
15340
16025
|
if (!call || !data.delta) break;
|
|
15341
16026
|
call.arguments += data.delta;
|
|
15342
|
-
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta }] } };
|
|
16027
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta, index: call.index }] } };
|
|
15343
16028
|
break;
|
|
15344
16029
|
}
|
|
15345
16030
|
case "response.output_item.done": {
|
|
@@ -15348,13 +16033,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15348
16033
|
const full = typeof item.arguments === "string" ? item.arguments : "";
|
|
15349
16034
|
const call = calls.get(item.id ?? item.call_id);
|
|
15350
16035
|
if (!call) {
|
|
15351
|
-
const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full };
|
|
16036
|
+
const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full, index: calls.size };
|
|
15352
16037
|
calls.set(item.id ?? added.callId, added);
|
|
15353
|
-
yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full }] } };
|
|
16038
|
+
yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full, index: added.index }] } };
|
|
15354
16039
|
} else if (full.length > call.arguments.length && full.startsWith(call.arguments)) {
|
|
15355
16040
|
const rest = full.slice(call.arguments.length);
|
|
15356
16041
|
call.arguments = full;
|
|
15357
|
-
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest }] } };
|
|
16042
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest, index: call.index }] } };
|
|
15358
16043
|
}
|
|
15359
16044
|
break;
|
|
15360
16045
|
}
|
|
@@ -15377,7 +16062,8 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15377
16062
|
yield {
|
|
15378
16063
|
id: final?.id || id,
|
|
15379
16064
|
model: final?.model || model,
|
|
15380
|
-
|
|
16065
|
+
// Summary text already went out as deltas; do not repeat it.
|
|
16066
|
+
delta: final?.reasoning && !streamedSummary ? { reasoning: final.reasoning } : {},
|
|
15381
16067
|
usage: final?.usage,
|
|
15382
16068
|
finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
|
|
15383
16069
|
};
|
|
@@ -15478,7 +16164,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15478
16164
|
});
|
|
15479
16165
|
return { role, content };
|
|
15480
16166
|
}
|
|
15481
|
-
async responsesFetch(body) {
|
|
16167
|
+
async responsesFetch(body, signal) {
|
|
15482
16168
|
body.input = expandResponsesInput(body.input);
|
|
15483
16169
|
const url = `${this.baseURL}/responses`;
|
|
15484
16170
|
const headers = {
|
|
@@ -15487,7 +16173,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15487
16173
|
...this.defaultHeaders
|
|
15488
16174
|
};
|
|
15489
16175
|
const payload = JSON.stringify(body);
|
|
15490
|
-
const res = await this.doFetchWithRetry(() =>
|
|
16176
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
15491
16177
|
if (!res.ok) {
|
|
15492
16178
|
const text = await res.text().catch(() => "");
|
|
15493
16179
|
throw new ProviderError(
|
|
@@ -15608,7 +16294,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15608
16294
|
}
|
|
15609
16295
|
return { type: "function", function: fn };
|
|
15610
16296
|
}
|
|
15611
|
-
async doFetch(body) {
|
|
16297
|
+
async doFetch(body, signal) {
|
|
15612
16298
|
const url = `${this.baseURL}/chat/completions`;
|
|
15613
16299
|
const headers = {
|
|
15614
16300
|
"Content-Type": "application/json",
|
|
@@ -15616,7 +16302,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15616
16302
|
...this.defaultHeaders
|
|
15617
16303
|
};
|
|
15618
16304
|
const payload = JSON.stringify(body);
|
|
15619
|
-
const res = await this.doFetchWithRetry(() =>
|
|
16305
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
15620
16306
|
if (!res.ok) {
|
|
15621
16307
|
const text = await res.text().catch(() => "");
|
|
15622
16308
|
throw new ProviderError(
|
|
@@ -15636,6 +16322,8 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15636
16322
|
name: tc.function?.name ?? "",
|
|
15637
16323
|
arguments: tc.function?.arguments ?? "{}"
|
|
15638
16324
|
}));
|
|
16325
|
+
const reasoningText = extractReasoningText(message);
|
|
16326
|
+
const providerFields = pickProviderFields(json, message, choice);
|
|
15639
16327
|
return {
|
|
15640
16328
|
id: json.id ?? "",
|
|
15641
16329
|
model: json.model ?? "",
|
|
@@ -15643,6 +16331,9 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15643
16331
|
toolCalls,
|
|
15644
16332
|
usage: this.extractUsage(json.usage),
|
|
15645
16333
|
finishReason: mapFinishReason(choice?.finish_reason),
|
|
16334
|
+
...typeof choice?.finish_reason === "string" ? { rawFinishReason: choice.finish_reason } : {},
|
|
16335
|
+
...providerFields ? { providerFields } : {},
|
|
16336
|
+
...reasoningText ? { reasoning: { summary: reasoningText } } : {},
|
|
15646
16337
|
raw: json
|
|
15647
16338
|
};
|
|
15648
16339
|
}
|
|
@@ -15661,6 +16352,23 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
|
15661
16352
|
};
|
|
15662
16353
|
}
|
|
15663
16354
|
};
|
|
16355
|
+
function extractReasoningText(src) {
|
|
16356
|
+
if (!src || typeof src !== "object") return void 0;
|
|
16357
|
+
for (const key of ["reasoning_content", "reasoning"]) {
|
|
16358
|
+
const v = src[key];
|
|
16359
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
16360
|
+
}
|
|
16361
|
+
return void 0;
|
|
16362
|
+
}
|
|
16363
|
+
function pickProviderFields(top, message, choice) {
|
|
16364
|
+
const out = {};
|
|
16365
|
+
if (top?.system_fingerprint != null) out.system_fingerprint = top.system_fingerprint;
|
|
16366
|
+
if (top?.service_tier != null) out.service_tier = top.service_tier;
|
|
16367
|
+
if (message?.annotations != null) out.annotations = message.annotations;
|
|
16368
|
+
if (message?.refusal != null) out.refusal = message.refusal;
|
|
16369
|
+
if (choice?.logprobs != null) out.logprobs = choice.logprobs;
|
|
16370
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
16371
|
+
}
|
|
15664
16372
|
function isReasoningModel(model) {
|
|
15665
16373
|
if (!model) return false;
|
|
15666
16374
|
const m = model.toLowerCase();
|
|
@@ -15737,38 +16445,88 @@ function mapFinishReason(reason) {
|
|
|
15737
16445
|
}
|
|
15738
16446
|
}
|
|
15739
16447
|
|
|
16448
|
+
// src/providers/utils/toolChoice.ts
|
|
16449
|
+
function supportsForcedToolChoice(model) {
|
|
16450
|
+
if (typeof model !== "string") return true;
|
|
16451
|
+
const id = model.toLowerCase();
|
|
16452
|
+
const at = id.indexOf("claude-");
|
|
16453
|
+
if (at < 0) return true;
|
|
16454
|
+
const toks = id.slice(at + 7).split("-");
|
|
16455
|
+
let family = false;
|
|
16456
|
+
for (const tok of toks) {
|
|
16457
|
+
if (tok === "sonnet" || tok === "opus") {
|
|
16458
|
+
family = true;
|
|
16459
|
+
continue;
|
|
16460
|
+
}
|
|
16461
|
+
const m = /^(\d+)(?:@|:|$)/.exec(tok);
|
|
16462
|
+
if (m) {
|
|
16463
|
+
if (m[1].length > 2) continue;
|
|
16464
|
+
if (Number(m[1]) < 5) return true;
|
|
16465
|
+
if (family) return false;
|
|
16466
|
+
return !toks.some((t) => t === "sonnet" || t === "opus");
|
|
16467
|
+
}
|
|
16468
|
+
}
|
|
16469
|
+
return true;
|
|
16470
|
+
}
|
|
16471
|
+
function forcedToolChoiceRejected(text) {
|
|
16472
|
+
const t = text.toLowerCase();
|
|
16473
|
+
return t.indexOf("tool_choice") >= 0 && t.indexOf("not supported for this model") >= 0;
|
|
16474
|
+
}
|
|
16475
|
+
|
|
15740
16476
|
// src/providers/anthropic.ts
|
|
16477
|
+
var DEFAULT_MAX_TOKENS = 4096;
|
|
16478
|
+
function normalizeExplicitThinking(body) {
|
|
16479
|
+
const t = body.thinking;
|
|
16480
|
+
if (t == null) {
|
|
16481
|
+
delete body.thinking;
|
|
16482
|
+
return;
|
|
16483
|
+
}
|
|
16484
|
+
if (t.type !== "enabled") return;
|
|
16485
|
+
const budget = typeof t.budget_tokens === "number" ? t.budget_tokens : 0;
|
|
16486
|
+
if (budget > 0 && (typeof body.max_tokens !== "number" || body.max_tokens <= budget)) {
|
|
16487
|
+
body.max_tokens = budget + 1024;
|
|
16488
|
+
}
|
|
16489
|
+
for (const k of ["temperature", "top_p", "top_k"]) delete body[k];
|
|
16490
|
+
}
|
|
16491
|
+
function thinkingFormRejected(text) {
|
|
16492
|
+
const t = text.toLowerCase();
|
|
16493
|
+
if (t.indexOf("thinking.type.enabled") >= 0 && t.indexOf("not supported") >= 0) return "enabled";
|
|
16494
|
+
if (t.indexOf("thinking.type.adaptive") >= 0 && t.indexOf("not supported") >= 0 && t.indexOf("thinking.type.enabled") < 0) return "adaptive";
|
|
16495
|
+
return void 0;
|
|
16496
|
+
}
|
|
15741
16497
|
var AnthropicProvider = class extends BaseProvider {
|
|
15742
16498
|
constructor(config) {
|
|
15743
16499
|
super();
|
|
15744
16500
|
this.providerName = "anthropic";
|
|
16501
|
+
this.configureTransport(config);
|
|
15745
16502
|
this.apiKey = config.apiKey;
|
|
15746
16503
|
this.baseURL = (config.baseURL ?? "https://api.anthropic.com").replace(/\/$/, "");
|
|
15747
16504
|
this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
|
|
15748
16505
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
15749
16506
|
this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
|
|
16507
|
+
this.thinkingMode = config.thinkingMode ?? "auto";
|
|
15750
16508
|
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
15751
16509
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
15752
16510
|
}
|
|
15753
16511
|
async complete(request) {
|
|
15754
|
-
const
|
|
15755
|
-
const res = await this.doFetch(body);
|
|
16512
|
+
const res = await this.send(request, false);
|
|
15756
16513
|
const json = await res.json();
|
|
15757
16514
|
return this.parseResponse(json);
|
|
15758
16515
|
}
|
|
15759
16516
|
async *completeStream(request) {
|
|
15760
|
-
const
|
|
15761
|
-
const res = await this.doFetch(body);
|
|
16517
|
+
const res = await this.send(request, true);
|
|
15762
16518
|
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
15763
16519
|
let id = "";
|
|
15764
16520
|
let model = "";
|
|
15765
16521
|
let inputTokens = 0;
|
|
15766
16522
|
let outputTokens = 0;
|
|
16523
|
+
let thinkingTokens = 0;
|
|
15767
16524
|
let cacheReadInputTokens = 0;
|
|
15768
16525
|
let cacheCreationInputTokens = 0;
|
|
15769
16526
|
const currentToolCalls = /* @__PURE__ */ new Map();
|
|
16527
|
+
let toolOrdinal = 0;
|
|
15770
16528
|
const reasoningBlocks = /* @__PURE__ */ new Map();
|
|
15771
|
-
for await (const event of parseSSEStream(res.body)) {
|
|
16529
|
+
for await (const event of parseSSEStream(res.body, request.signal)) {
|
|
15772
16530
|
let data;
|
|
15773
16531
|
try {
|
|
15774
16532
|
data = JSON.parse(event.data);
|
|
@@ -15795,8 +16553,11 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
15795
16553
|
currentToolCalls.set(idx, {
|
|
15796
16554
|
id: block.id ?? "",
|
|
15797
16555
|
name: block.name ?? "",
|
|
15798
|
-
arguments: ""
|
|
16556
|
+
arguments: "",
|
|
16557
|
+
ordinal: toolOrdinal++
|
|
15799
16558
|
});
|
|
16559
|
+
const opened = currentToolCalls.get(idx);
|
|
16560
|
+
yield { id, model, delta: { toolCalls: [{ id: opened.id, name: opened.name, arguments: "", index: opened.ordinal }] } };
|
|
15800
16561
|
} else if (block?.type === "thinking") {
|
|
15801
16562
|
reasoningBlocks.set(idx, { type: "thinking", text: block.thinking ?? "", signature: block.signature });
|
|
15802
16563
|
} else if (block?.type === "redacted_thinking") {
|
|
@@ -15834,7 +16595,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
15834
16595
|
id,
|
|
15835
16596
|
model,
|
|
15836
16597
|
delta: {
|
|
15837
|
-
toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json }]
|
|
16598
|
+
toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json, index: tc.ordinal }]
|
|
15838
16599
|
}
|
|
15839
16600
|
};
|
|
15840
16601
|
}
|
|
@@ -15847,8 +16608,10 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
15847
16608
|
const delta = data.delta;
|
|
15848
16609
|
if (data.usage) {
|
|
15849
16610
|
outputTokens = data.usage.output_tokens ?? outputTokens;
|
|
16611
|
+
thinkingTokens = data.usage.output_tokens_details?.thinking_tokens ?? thinkingTokens;
|
|
15850
16612
|
}
|
|
15851
16613
|
const finishReason = mapAnthropicStopReason(delta?.stop_reason);
|
|
16614
|
+
const rawFinishReason = typeof delta?.stop_reason === "string" ? delta.stop_reason : void 0;
|
|
15852
16615
|
const usage = {
|
|
15853
16616
|
inputTokens,
|
|
15854
16617
|
outputTokens,
|
|
@@ -15856,7 +16619,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
15856
16619
|
cachedInputTokens: cacheReadInputTokens,
|
|
15857
16620
|
cachedWriteTokens: cacheCreationInputTokens,
|
|
15858
16621
|
cachedOutputTokens: 0,
|
|
15859
|
-
reasoningTokens:
|
|
16622
|
+
reasoningTokens: thinkingTokens
|
|
15860
16623
|
};
|
|
15861
16624
|
const blocks = [...reasoningBlocks.values()].filter(
|
|
15862
16625
|
(b) => b.type === "thinking" && b.signature || b.type === "redacted_thinking" && b.data
|
|
@@ -15866,6 +16629,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
15866
16629
|
model,
|
|
15867
16630
|
delta: blocks.length > 0 ? { reasoning: { blocks } } : {},
|
|
15868
16631
|
finishReason,
|
|
16632
|
+
...rawFinishReason ? { rawFinishReason } : {},
|
|
15869
16633
|
usage
|
|
15870
16634
|
};
|
|
15871
16635
|
break;
|
|
@@ -15880,12 +16644,12 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
15880
16644
|
}
|
|
15881
16645
|
}
|
|
15882
16646
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
15883
|
-
buildRequestBody(request, stream) {
|
|
16647
|
+
buildRequestBody(request, stream, modeOverride, downgradeToolChoice = false) {
|
|
15884
16648
|
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
15885
16649
|
const body = {
|
|
15886
16650
|
model: request.model || this.defaultModel,
|
|
15887
16651
|
messages: this.toAnthropicMessages(messages),
|
|
15888
|
-
max_tokens: request.maxTokens ??
|
|
16652
|
+
max_tokens: request.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
15889
16653
|
stream
|
|
15890
16654
|
};
|
|
15891
16655
|
if (system) {
|
|
@@ -15906,18 +16670,27 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
15906
16670
|
body.tools = tools;
|
|
15907
16671
|
}
|
|
15908
16672
|
if (request.toolChoice != null) {
|
|
16673
|
+
const forceOk = !downgradeToolChoice && supportsForcedToolChoice(body.model);
|
|
15909
16674
|
if (request.toolChoice === "auto") {
|
|
15910
16675
|
body.tool_choice = { type: "auto" };
|
|
15911
16676
|
} else if (request.toolChoice === "required") {
|
|
15912
|
-
body.tool_choice = { type: "any" };
|
|
16677
|
+
body.tool_choice = forceOk ? { type: "any" } : { type: "auto" };
|
|
15913
16678
|
} else if (request.toolChoice === "none") ; else {
|
|
15914
|
-
body.tool_choice = { type: "tool", name: request.toolChoice.name };
|
|
16679
|
+
body.tool_choice = forceOk ? { type: "tool", name: request.toolChoice.name } : { type: "auto" };
|
|
15915
16680
|
}
|
|
15916
16681
|
}
|
|
15917
16682
|
if (request.extra) {
|
|
15918
16683
|
Object.assign(body, request.extra);
|
|
15919
16684
|
}
|
|
15920
|
-
|
|
16685
|
+
const explicitThinking = request.extra && typeof request.extra === "object" && "thinking" in request.extra;
|
|
16686
|
+
if (explicitThinking) {
|
|
16687
|
+
normalizeExplicitThinking(body);
|
|
16688
|
+
} else {
|
|
16689
|
+
applyAnthropicReasoning(body, request.reasoning, { model: body.model, mode: modeOverride ?? this.thinkingMode });
|
|
16690
|
+
}
|
|
16691
|
+
if (typeof body.max_tokens !== "number" || !(body.max_tokens > 0)) {
|
|
16692
|
+
body.max_tokens = DEFAULT_MAX_TOKENS;
|
|
16693
|
+
}
|
|
15921
16694
|
return body;
|
|
15922
16695
|
}
|
|
15923
16696
|
splitSystemMessages(messages) {
|
|
@@ -16048,7 +16821,35 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16048
16821
|
input_schema: t.parameters
|
|
16049
16822
|
};
|
|
16050
16823
|
}
|
|
16051
|
-
|
|
16824
|
+
/** Builds and sends the request; retries once with the other thinking form
|
|
16825
|
+
* when the upstream rejects the one we picked. */
|
|
16826
|
+
async send(request, stream) {
|
|
16827
|
+
let mode;
|
|
16828
|
+
let downgrade = false;
|
|
16829
|
+
let flippedThinking = false;
|
|
16830
|
+
for (; ; ) {
|
|
16831
|
+
const body = this.buildRequestBody(request, stream, mode, downgrade);
|
|
16832
|
+
try {
|
|
16833
|
+
return await this.doFetch(body, request.signal);
|
|
16834
|
+
} catch (err) {
|
|
16835
|
+
if (!(err instanceof ProviderError) || err.statusCode !== 400) throw err;
|
|
16836
|
+
const text = String(err.responseBody ?? err.message);
|
|
16837
|
+
if (!downgrade && body.tool_choice && body.tool_choice.type !== "auto" && forcedToolChoiceRejected(text)) {
|
|
16838
|
+
downgrade = true;
|
|
16839
|
+
continue;
|
|
16840
|
+
}
|
|
16841
|
+
const explicit = request.extra && typeof request.extra === "object" && "thinking" in request.extra;
|
|
16842
|
+
if (flippedThinking || explicit || !request.reasoning) throw err;
|
|
16843
|
+
const wrong = thinkingFormRejected(text);
|
|
16844
|
+
if (!wrong) throw err;
|
|
16845
|
+
const used = resolveThinkingForm(request.model ?? this.defaultModel, this.thinkingMode);
|
|
16846
|
+
if (wrong === "enabled" && used !== "budget" || wrong === "adaptive" && used !== "adaptive") throw err;
|
|
16847
|
+
mode = wrong === "enabled" ? "adaptive" : "budget";
|
|
16848
|
+
flippedThinking = true;
|
|
16849
|
+
}
|
|
16850
|
+
}
|
|
16851
|
+
}
|
|
16852
|
+
async doFetch(body, signal) {
|
|
16052
16853
|
const url = `${this.baseURL}/v1/messages`;
|
|
16053
16854
|
const headers = {
|
|
16054
16855
|
"Content-Type": "application/json",
|
|
@@ -16057,7 +16858,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16057
16858
|
...this.defaultHeaders
|
|
16058
16859
|
};
|
|
16059
16860
|
const payload = JSON.stringify(body);
|
|
16060
|
-
const res = await this.doFetchWithRetry(() =>
|
|
16861
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, { method: "POST", headers, body: payload, signal }), signal);
|
|
16061
16862
|
if (!res.ok) {
|
|
16062
16863
|
const text = await res.text().catch(() => "");
|
|
16063
16864
|
throw new ProviderError(
|
|
@@ -16102,10 +16903,9 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16102
16903
|
cachedInputTokens: usage.cache_read_input_tokens ?? 0,
|
|
16103
16904
|
cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
|
|
16104
16905
|
cachedOutputTokens: 0,
|
|
16105
|
-
//
|
|
16106
|
-
//
|
|
16107
|
-
|
|
16108
|
-
reasoningTokens: 0
|
|
16906
|
+
// Thinking tokens are billed inside `output_tokens`; newer models also
|
|
16907
|
+
// report the split as `output_tokens_details.thinking_tokens`.
|
|
16908
|
+
reasoningTokens: usage.output_tokens_details?.thinking_tokens ?? 0
|
|
16109
16909
|
};
|
|
16110
16910
|
return {
|
|
16111
16911
|
id: json.id ?? "",
|
|
@@ -16114,6 +16914,7 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
16114
16914
|
toolCalls,
|
|
16115
16915
|
usage: tokenUsage,
|
|
16116
16916
|
finishReason: mapAnthropicStopReason(json.stop_reason),
|
|
16917
|
+
...typeof json.stop_reason === "string" ? { rawFinishReason: json.stop_reason } : {},
|
|
16117
16918
|
reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
|
|
16118
16919
|
raw: json
|
|
16119
16920
|
};
|
|
@@ -16159,7 +16960,15 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16159
16960
|
provider: "openai",
|
|
16160
16961
|
apiKey: config.apiKey,
|
|
16161
16962
|
defaultModel: config.defaultModel ?? config.deploymentName ?? "gpt-4o",
|
|
16162
|
-
defaultHeaders: config.defaultHeaders
|
|
16963
|
+
defaultHeaders: config.defaultHeaders,
|
|
16964
|
+
retry: config.retry,
|
|
16965
|
+
fetch: config.fetch,
|
|
16966
|
+
timeoutMs: config.timeoutMs,
|
|
16967
|
+
maxTokensParam: config.maxTokensParam,
|
|
16968
|
+
api: config.api,
|
|
16969
|
+
responsesApi: config.responsesApi,
|
|
16970
|
+
responsesApiModels: config.responsesApiModels,
|
|
16971
|
+
responsesStreaming: config.responsesStreaming
|
|
16163
16972
|
});
|
|
16164
16973
|
this.providerName = "azure";
|
|
16165
16974
|
this.endpoint = config.endpoint.replace(/\/$/, "");
|
|
@@ -16167,22 +16976,24 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16167
16976
|
this.deploymentName = config.deploymentName;
|
|
16168
16977
|
this.azureApiKey = config.apiKey;
|
|
16169
16978
|
}
|
|
16170
|
-
async doFetch(body) {
|
|
16979
|
+
async doFetch(body, signal) {
|
|
16171
16980
|
const deployment = this.deploymentName ?? body.model;
|
|
16172
16981
|
if (!deployment) {
|
|
16173
16982
|
throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
|
|
16174
16983
|
}
|
|
16175
16984
|
const url = `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.apiVersion}`;
|
|
16176
16985
|
const { model: _model, ...bodyWithoutModel } = body;
|
|
16177
|
-
const
|
|
16986
|
+
const payload = JSON.stringify(bodyWithoutModel);
|
|
16987
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
|
|
16178
16988
|
method: "POST",
|
|
16179
16989
|
headers: {
|
|
16180
16990
|
"Content-Type": "application/json",
|
|
16181
16991
|
"api-key": this.azureApiKey,
|
|
16182
16992
|
...this.defaultHeaders
|
|
16183
16993
|
},
|
|
16184
|
-
body:
|
|
16185
|
-
|
|
16994
|
+
body: payload,
|
|
16995
|
+
signal
|
|
16996
|
+
}), signal);
|
|
16186
16997
|
if (!res.ok) {
|
|
16187
16998
|
const text = await res.text().catch(() => "");
|
|
16188
16999
|
throw new ProviderError(
|
|
@@ -16194,10 +17005,36 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16194
17005
|
}
|
|
16195
17006
|
return res;
|
|
16196
17007
|
}
|
|
17008
|
+
/** Azure embeddings: the deployment named by the request's model (falling back
|
|
17009
|
+
* to the configured deployment) in the path, api-key auth. */
|
|
17010
|
+
async embeddingsFetch(body, signal) {
|
|
17011
|
+
const deployment = body.model || this.deploymentName;
|
|
17012
|
+
if (!deployment) {
|
|
17013
|
+
throw new ProviderError("Azure embeddings require a deployment name or model", this.providerName, 400);
|
|
17014
|
+
}
|
|
17015
|
+
const url = `${this.endpoint}/openai/deployments/${deployment}/embeddings?api-version=${this.apiVersion}`;
|
|
17016
|
+
const { model: _model, ...bodyWithoutModel } = body;
|
|
17017
|
+
const payload = JSON.stringify(bodyWithoutModel);
|
|
17018
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
|
|
17019
|
+
method: "POST",
|
|
17020
|
+
headers: {
|
|
17021
|
+
"Content-Type": "application/json",
|
|
17022
|
+
"api-key": this.azureApiKey,
|
|
17023
|
+
...this.defaultHeaders
|
|
17024
|
+
},
|
|
17025
|
+
body: payload,
|
|
17026
|
+
signal
|
|
17027
|
+
}), signal);
|
|
17028
|
+
if (!res.ok) {
|
|
17029
|
+
const text = await res.text().catch(() => "");
|
|
17030
|
+
throw new ProviderError(`Azure OpenAI embeddings API error ${res.status}: ${text}`, this.providerName, res.status, text);
|
|
17031
|
+
}
|
|
17032
|
+
return res;
|
|
17033
|
+
}
|
|
16197
17034
|
/** Azure exposes the Responses API at /openai/responses with api-key auth.
|
|
16198
17035
|
* Reasoning summaries require a preview api-version; callers can override via
|
|
16199
17036
|
* the `apiVersion` config. */
|
|
16200
|
-
async responsesFetch(body) {
|
|
17037
|
+
async responsesFetch(body, signal) {
|
|
16201
17038
|
body.input = expandAzureResponsesInput(body.input);
|
|
16202
17039
|
const deployment = this.deploymentName ?? body.model;
|
|
16203
17040
|
if (!deployment) {
|
|
@@ -16205,15 +17042,16 @@ var AzureProvider = class extends OpenAIProvider {
|
|
|
16205
17042
|
}
|
|
16206
17043
|
const url = `${this.endpoint}/openai/responses?api-version=${this.apiVersion}`;
|
|
16207
17044
|
const payload = JSON.stringify({ ...body, model: deployment });
|
|
16208
|
-
const res = await
|
|
17045
|
+
const res = await this.doFetchWithRetry(() => this.httpFetch(url, {
|
|
16209
17046
|
method: "POST",
|
|
16210
17047
|
headers: {
|
|
16211
17048
|
"Content-Type": "application/json",
|
|
16212
17049
|
"api-key": this.azureApiKey,
|
|
16213
17050
|
...this.defaultHeaders
|
|
16214
17051
|
},
|
|
16215
|
-
body: payload
|
|
16216
|
-
|
|
17052
|
+
body: payload,
|
|
17053
|
+
signal
|
|
17054
|
+
}), signal);
|
|
16217
17055
|
if (!res.ok) {
|
|
16218
17056
|
const text = await res.text().catch(() => "");
|
|
16219
17057
|
throw new ProviderError(
|
|
@@ -16249,20 +17087,155 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
|
16249
17087
|
apiKey: config.apiKey,
|
|
16250
17088
|
baseURL: config.baseURL,
|
|
16251
17089
|
defaultModel: config.defaultModel,
|
|
16252
|
-
defaultHeaders: config.defaultHeaders
|
|
17090
|
+
defaultHeaders: config.defaultHeaders,
|
|
17091
|
+
organization: config.organization,
|
|
17092
|
+
retry: config.retry,
|
|
17093
|
+
fetch: config.fetch,
|
|
17094
|
+
timeoutMs: config.timeoutMs,
|
|
17095
|
+
maxTokensParam: config.maxTokensParam,
|
|
17096
|
+
api: config.api,
|
|
17097
|
+
// Unlike OpenAI / Azure, "auto" on a compatible server means Chat
|
|
17098
|
+
// Completions (the 0.11 default): the model name is the operator's.
|
|
17099
|
+
// `responsesApiModels` still applies; `responsesApi: "auto"` opts in.
|
|
17100
|
+
_autoMeansChat: true,
|
|
17101
|
+
responsesApi: config.responsesApi,
|
|
17102
|
+
responsesApiModels: config.responsesApiModels,
|
|
17103
|
+
responsesStreaming: config.responsesStreaming
|
|
16253
17104
|
});
|
|
16254
17105
|
this.providerName = "openai-compatible";
|
|
16255
17106
|
}
|
|
16256
17107
|
};
|
|
16257
17108
|
|
|
17109
|
+
// src/providers/utils/eventStream.ts
|
|
17110
|
+
var EventStreamError = class extends Error {
|
|
17111
|
+
constructor(message) {
|
|
17112
|
+
super(message);
|
|
17113
|
+
this.name = "EventStreamError";
|
|
17114
|
+
}
|
|
17115
|
+
};
|
|
17116
|
+
var CRC_TABLE = (() => {
|
|
17117
|
+
const table = new Uint32Array(256);
|
|
17118
|
+
for (let n = 0; n < 256; n++) {
|
|
17119
|
+
let c = n;
|
|
17120
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
17121
|
+
table[n] = c >>> 0;
|
|
17122
|
+
}
|
|
17123
|
+
return table;
|
|
17124
|
+
})();
|
|
17125
|
+
function crc32(bytes, start = 0, end = bytes.length) {
|
|
17126
|
+
let c = 4294967295;
|
|
17127
|
+
for (let i = start; i < end; i++) c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
|
|
17128
|
+
return (c ^ 4294967295) >>> 0;
|
|
17129
|
+
}
|
|
17130
|
+
function decodeFrame(frame) {
|
|
17131
|
+
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
17132
|
+
const total = view.getUint32(0);
|
|
17133
|
+
const headersLen = view.getUint32(4);
|
|
17134
|
+
if (total !== frame.byteLength) throw new EventStreamError("event-stream frame length mismatch");
|
|
17135
|
+
if (crc32(frame, 0, 8) !== view.getUint32(8)) throw new EventStreamError("event-stream prelude CRC mismatch");
|
|
17136
|
+
if (crc32(frame, 0, total - 4) !== view.getUint32(total - 4)) throw new EventStreamError("event-stream message CRC mismatch");
|
|
17137
|
+
if (12 + headersLen > total - 4) throw new EventStreamError("event-stream headers overflow frame");
|
|
17138
|
+
const headers = {};
|
|
17139
|
+
const td = new TextDecoder();
|
|
17140
|
+
let o = 12;
|
|
17141
|
+
const headersEnd = 12 + headersLen;
|
|
17142
|
+
while (o < headersEnd) {
|
|
17143
|
+
const nameLen = frame[o++];
|
|
17144
|
+
const name = td.decode(frame.subarray(o, o + nameLen));
|
|
17145
|
+
o += nameLen;
|
|
17146
|
+
const type = frame[o++];
|
|
17147
|
+
switch (type) {
|
|
17148
|
+
case 0:
|
|
17149
|
+
headers[name] = true;
|
|
17150
|
+
break;
|
|
17151
|
+
case 1:
|
|
17152
|
+
headers[name] = false;
|
|
17153
|
+
break;
|
|
17154
|
+
case 2:
|
|
17155
|
+
headers[name] = view.getInt8(o);
|
|
17156
|
+
o += 1;
|
|
17157
|
+
break;
|
|
17158
|
+
case 3:
|
|
17159
|
+
headers[name] = view.getInt16(o);
|
|
17160
|
+
o += 2;
|
|
17161
|
+
break;
|
|
17162
|
+
case 4:
|
|
17163
|
+
headers[name] = view.getInt32(o);
|
|
17164
|
+
o += 4;
|
|
17165
|
+
break;
|
|
17166
|
+
case 5:
|
|
17167
|
+
headers[name] = view.getBigInt64(o);
|
|
17168
|
+
o += 8;
|
|
17169
|
+
break;
|
|
17170
|
+
case 6: {
|
|
17171
|
+
const len = view.getUint16(o);
|
|
17172
|
+
o += 2;
|
|
17173
|
+
headers[name] = frame.slice(o, o + len);
|
|
17174
|
+
o += len;
|
|
17175
|
+
break;
|
|
17176
|
+
}
|
|
17177
|
+
case 7: {
|
|
17178
|
+
const len = view.getUint16(o);
|
|
17179
|
+
o += 2;
|
|
17180
|
+
headers[name] = td.decode(frame.subarray(o, o + len));
|
|
17181
|
+
o += len;
|
|
17182
|
+
break;
|
|
17183
|
+
}
|
|
17184
|
+
case 8:
|
|
17185
|
+
headers[name] = view.getBigInt64(o);
|
|
17186
|
+
o += 8;
|
|
17187
|
+
break;
|
|
17188
|
+
case 9:
|
|
17189
|
+
headers[name] = frame.slice(o, o + 16);
|
|
17190
|
+
o += 16;
|
|
17191
|
+
break;
|
|
17192
|
+
default:
|
|
17193
|
+
throw new EventStreamError(`unknown event-stream header type ${type}`);
|
|
17194
|
+
}
|
|
17195
|
+
}
|
|
17196
|
+
return { headers, payload: frame.slice(headersEnd, total - 4) };
|
|
17197
|
+
}
|
|
17198
|
+
async function* parseEventStream(body, signal) {
|
|
17199
|
+
const reader = body.getReader();
|
|
17200
|
+
let buf = new Uint8Array(0);
|
|
17201
|
+
try {
|
|
17202
|
+
while (true) {
|
|
17203
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
17204
|
+
const { done, value } = await reader.read();
|
|
17205
|
+
if (done) break;
|
|
17206
|
+
if (!value?.length) continue;
|
|
17207
|
+
const merged = new Uint8Array(buf.length + value.length);
|
|
17208
|
+
merged.set(buf, 0);
|
|
17209
|
+
merged.set(value, buf.length);
|
|
17210
|
+
buf = merged;
|
|
17211
|
+
while (buf.length >= 12) {
|
|
17212
|
+
const total = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(0);
|
|
17213
|
+
if (total < 16) throw new EventStreamError("invalid event-stream frame length");
|
|
17214
|
+
if (buf.length < total) break;
|
|
17215
|
+
yield decodeFrame(buf.subarray(0, total));
|
|
17216
|
+
buf = buf.subarray(total);
|
|
17217
|
+
}
|
|
17218
|
+
}
|
|
17219
|
+
if (buf.length > 0) throw new EventStreamError("event-stream ended inside a frame");
|
|
17220
|
+
} finally {
|
|
17221
|
+
reader.cancel().catch(() => void 0);
|
|
17222
|
+
try {
|
|
17223
|
+
reader.releaseLock();
|
|
17224
|
+
} catch {
|
|
17225
|
+
}
|
|
17226
|
+
}
|
|
17227
|
+
}
|
|
17228
|
+
|
|
16258
17229
|
// src/providers/bedrock.ts
|
|
16259
17230
|
var BedrockProvider = class extends BaseProvider {
|
|
16260
17231
|
constructor(config) {
|
|
16261
17232
|
super();
|
|
16262
17233
|
this.providerName = "bedrock";
|
|
17234
|
+
this.configureTransport(config);
|
|
16263
17235
|
this.region = config.region;
|
|
16264
17236
|
this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
|
|
16265
17237
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
17238
|
+
this.thinkingMode = config.thinkingMode ?? "auto";
|
|
16266
17239
|
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
16267
17240
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
16268
17241
|
this.credentials = {
|
|
@@ -16279,25 +17252,202 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16279
17252
|
}
|
|
16280
17253
|
async complete(request) {
|
|
16281
17254
|
const modelId = request.model || this.defaultModel;
|
|
16282
|
-
const
|
|
16283
|
-
|
|
16284
|
-
return this.parseResponse(json, modelId);
|
|
17255
|
+
const res = await this.converseFetch(modelId, request, false);
|
|
17256
|
+
return this.parseResponse(await res.json(), modelId);
|
|
16285
17257
|
}
|
|
17258
|
+
/**
|
|
17259
|
+
* Real streaming over ConverseStream. The response is an AWS event-stream
|
|
17260
|
+
* (binary frames); each frame carries one JSON event: messageStart,
|
|
17261
|
+
* contentBlockStart/Delta/Stop (text, toolUse, reasoningContent),
|
|
17262
|
+
* messageStop and, last, metadata (usage). Text, tool-call argument and
|
|
17263
|
+
* reasoning deltas are yielded as they arrive; one closing chunk carries the
|
|
17264
|
+
* finish reason, usage and the signed reasoning blocks.
|
|
17265
|
+
*/
|
|
16286
17266
|
async *completeStream(request) {
|
|
16287
|
-
const
|
|
17267
|
+
const modelId = request.model || this.defaultModel;
|
|
17268
|
+
const res = await this.converseFetch(modelId, request, true);
|
|
17269
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
17270
|
+
const id = res.headers.get("x-amzn-requestid") ?? "";
|
|
17271
|
+
const toolsByIndex = /* @__PURE__ */ new Map();
|
|
17272
|
+
const reasoningByIndex = /* @__PURE__ */ new Map();
|
|
17273
|
+
let finishReason;
|
|
17274
|
+
let rawFinishReason;
|
|
17275
|
+
let toolOrdinal = 0;
|
|
17276
|
+
let usage;
|
|
17277
|
+
for await (const msg of parseEventStream(res.body, request.signal)) {
|
|
17278
|
+
const messageType = String(msg.headers[":message-type"] ?? "event");
|
|
17279
|
+
const eventType = String(msg.headers[":event-type"] ?? msg.headers[":exception-type"] ?? "");
|
|
17280
|
+
let data = {};
|
|
17281
|
+
const text = new TextDecoder().decode(msg.payload);
|
|
17282
|
+
if (text) {
|
|
17283
|
+
try {
|
|
17284
|
+
data = JSON.parse(text);
|
|
17285
|
+
} catch {
|
|
17286
|
+
data = { message: text };
|
|
17287
|
+
}
|
|
17288
|
+
}
|
|
17289
|
+
if (messageType === "exception" || messageType === "error") {
|
|
17290
|
+
const code = String(msg.headers[":exception-type"] ?? msg.headers[":error-code"] ?? eventType);
|
|
17291
|
+
throw new ProviderError(
|
|
17292
|
+
`Bedrock stream error ${code}: ${data.message ?? text}`,
|
|
17293
|
+
this.providerName,
|
|
17294
|
+
bedrockExceptionStatus(code),
|
|
17295
|
+
data
|
|
17296
|
+
);
|
|
17297
|
+
}
|
|
17298
|
+
switch (eventType) {
|
|
17299
|
+
case "contentBlockStart": {
|
|
17300
|
+
const idx = data.contentBlockIndex ?? 0;
|
|
17301
|
+
const tu = data.start?.toolUse;
|
|
17302
|
+
if (tu) {
|
|
17303
|
+
const ordinal = toolOrdinal++;
|
|
17304
|
+
toolsByIndex.set(idx, { id: tu.toolUseId ?? "", name: tu.name ?? "", ordinal });
|
|
17305
|
+
yield { id, model: modelId, delta: { toolCalls: [{ id: tu.toolUseId, name: tu.name, arguments: "", index: ordinal }] } };
|
|
17306
|
+
}
|
|
17307
|
+
break;
|
|
17308
|
+
}
|
|
17309
|
+
case "contentBlockDelta": {
|
|
17310
|
+
const idx = data.contentBlockIndex ?? 0;
|
|
17311
|
+
const d = data.delta ?? {};
|
|
17312
|
+
if (typeof d.text === "string" && d.text) {
|
|
17313
|
+
yield { id, model: modelId, delta: { content: d.text } };
|
|
17314
|
+
} else if (d.toolUse) {
|
|
17315
|
+
const tc = toolsByIndex.get(idx);
|
|
17316
|
+
if (tc && typeof d.toolUse.input === "string" && d.toolUse.input) {
|
|
17317
|
+
yield { id, model: modelId, delta: { toolCalls: [{ id: tc.id, name: tc.name, arguments: d.toolUse.input, index: tc.ordinal }] } };
|
|
17318
|
+
}
|
|
17319
|
+
} else if (d.reasoningContent) {
|
|
17320
|
+
const rc = d.reasoningContent;
|
|
17321
|
+
const blk = reasoningByIndex.get(idx) ?? { type: "thinking", text: "" };
|
|
17322
|
+
if (typeof rc.text === "string" && rc.text) {
|
|
17323
|
+
blk.text = (blk.text ?? "") + rc.text;
|
|
17324
|
+
reasoningByIndex.set(idx, blk);
|
|
17325
|
+
yield { id, model: modelId, delta: { reasoning: { summary: rc.text } } };
|
|
17326
|
+
}
|
|
17327
|
+
if (typeof rc.signature === "string" && rc.signature) {
|
|
17328
|
+
blk.signature = (blk.signature ?? "") + rc.signature;
|
|
17329
|
+
reasoningByIndex.set(idx, blk);
|
|
17330
|
+
}
|
|
17331
|
+
if (rc.redactedContent) {
|
|
17332
|
+
reasoningByIndex.set(idx, { type: "redacted_thinking", data: rc.redactedContent });
|
|
17333
|
+
}
|
|
17334
|
+
}
|
|
17335
|
+
break;
|
|
17336
|
+
}
|
|
17337
|
+
case "messageStop":
|
|
17338
|
+
finishReason = mapBedrockStopReason(data.stopReason);
|
|
17339
|
+
if (typeof data.stopReason === "string") rawFinishReason = data.stopReason;
|
|
17340
|
+
break;
|
|
17341
|
+
case "metadata": {
|
|
17342
|
+
const u = data.usage ?? {};
|
|
17343
|
+
usage = {
|
|
17344
|
+
inputTokens: u.inputTokens ?? 0,
|
|
17345
|
+
outputTokens: u.outputTokens ?? 0,
|
|
17346
|
+
totalTokens: u.totalTokens ?? (u.inputTokens ?? 0) + (u.outputTokens ?? 0),
|
|
17347
|
+
cachedInputTokens: u.cacheReadInputTokens ?? u.cacheReadInputTokenCount ?? 0,
|
|
17348
|
+
cachedWriteTokens: u.cacheWriteInputTokens ?? u.cacheWriteInputTokenCount ?? 0,
|
|
17349
|
+
cachedOutputTokens: 0,
|
|
17350
|
+
reasoningTokens: 0
|
|
17351
|
+
};
|
|
17352
|
+
break;
|
|
17353
|
+
}
|
|
17354
|
+
}
|
|
17355
|
+
}
|
|
17356
|
+
const blocks = [...reasoningByIndex.values()].filter(
|
|
17357
|
+
(b) => b.type === "thinking" && b.signature || b.type === "redacted_thinking" && b.data
|
|
17358
|
+
);
|
|
16288
17359
|
yield {
|
|
16289
|
-
id
|
|
16290
|
-
model:
|
|
16291
|
-
delta: {
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
usage: response.usage,
|
|
16296
|
-
finishReason: response.finishReason
|
|
17360
|
+
id,
|
|
17361
|
+
model: modelId,
|
|
17362
|
+
delta: blocks.length > 0 ? { reasoning: { blocks } } : {},
|
|
17363
|
+
finishReason: finishReason ?? "stop",
|
|
17364
|
+
...rawFinishReason ? { rawFinishReason } : {},
|
|
17365
|
+
usage
|
|
16297
17366
|
};
|
|
16298
17367
|
}
|
|
17368
|
+
// ─── Embeddings ──────────────────────────────────────────────────────────
|
|
17369
|
+
/**
|
|
17370
|
+
* InvokeModel embeddings. Supported families (by model id, inference-profile
|
|
17371
|
+
* prefixes such as `us.` allowed):
|
|
17372
|
+
* - Amazon Titan Text Embeddings (v1, v2): one text per call, `dimensions`
|
|
17373
|
+
* (v2: 256/512/1024) and `normalize` via `extra`.
|
|
17374
|
+
* - Cohere Embed (v3, v4): a batch of texts per call (default 96), `inputType`
|
|
17375
|
+
* maps to `input_type`, `dimensions` to `output_dimension` (v4).
|
|
17376
|
+
*/
|
|
17377
|
+
async embed(request) {
|
|
17378
|
+
const model = requireEmbeddingModel(this.providerName, request.model);
|
|
17379
|
+
const id = model.toLowerCase();
|
|
17380
|
+
const raw = [];
|
|
17381
|
+
let inputTokens = 0;
|
|
17382
|
+
let vectors = [];
|
|
17383
|
+
if (id.includes("titan-embed")) {
|
|
17384
|
+
const results = await mapWithConcurrency(request.input, 4, async (text) => {
|
|
17385
|
+
const body = { inputText: text };
|
|
17386
|
+
if (request.dimensions != null && !id.includes("titan-embed-text-v1")) body.dimensions = request.dimensions;
|
|
17387
|
+
if (request.extra) Object.assign(body, request.extra);
|
|
17388
|
+
const res = await this.invokeModel(model, body, request.signal);
|
|
17389
|
+
const json = await res.json();
|
|
17390
|
+
return { json, tokens: json.inputTextTokenCount ?? Number(res.headers.get("x-amzn-bedrock-input-token-count") ?? 0) };
|
|
17391
|
+
}, request.signal);
|
|
17392
|
+
for (const r of results) {
|
|
17393
|
+
raw.push(r.json);
|
|
17394
|
+
inputTokens += r.tokens || 0;
|
|
17395
|
+
vectors.push(r.json.embedding ?? r.json.embeddingsByType?.float);
|
|
17396
|
+
}
|
|
17397
|
+
} else if (id.includes("cohere.embed")) {
|
|
17398
|
+
for (const batch of chunkArray(request.input, request.batchSize ?? 96)) {
|
|
17399
|
+
const body = {
|
|
17400
|
+
texts: batch,
|
|
17401
|
+
input_type: request.inputType ?? "search_document",
|
|
17402
|
+
embedding_types: ["float"]
|
|
17403
|
+
};
|
|
17404
|
+
if (request.dimensions != null) body.output_dimension = request.dimensions;
|
|
17405
|
+
if (request.extra) Object.assign(body, request.extra);
|
|
17406
|
+
const res = await this.invokeModel(model, body, request.signal);
|
|
17407
|
+
const json = await res.json();
|
|
17408
|
+
raw.push(json);
|
|
17409
|
+
const emb = Array.isArray(json.embeddings) ? json.embeddings : json.embeddings?.float;
|
|
17410
|
+
vectors.push(...emb ?? []);
|
|
17411
|
+
inputTokens += Number(res.headers.get("x-amzn-bedrock-input-token-count") ?? 0) || 0;
|
|
17412
|
+
}
|
|
17413
|
+
} else {
|
|
17414
|
+
throw new ProviderError(
|
|
17415
|
+
`Bedrock embeddings are supported for Amazon Titan and Cohere Embed models, not "${model}"`,
|
|
17416
|
+
this.providerName,
|
|
17417
|
+
400
|
|
17418
|
+
);
|
|
17419
|
+
}
|
|
17420
|
+
assertVectorCount(this.providerName, vectors, request.input.length);
|
|
17421
|
+
return { model, vectors, usage: { inputTokens, totalTokens: inputTokens }, raw };
|
|
17422
|
+
}
|
|
17423
|
+
async invokeModel(modelId, body, signal) {
|
|
17424
|
+
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
17425
|
+
const url = `https://${host}/model/${encodeURIComponent(modelId)}/invoke`;
|
|
17426
|
+
const bodyStr = JSON.stringify(body);
|
|
17427
|
+
const res = await this.doFetchWithRetry(() => {
|
|
17428
|
+
const headers = signRequest({
|
|
17429
|
+
method: "POST",
|
|
17430
|
+
url,
|
|
17431
|
+
headers: {
|
|
17432
|
+
"Content-Type": "application/json",
|
|
17433
|
+
Accept: "application/json",
|
|
17434
|
+
...this.defaultHeaders
|
|
17435
|
+
},
|
|
17436
|
+
body: bodyStr,
|
|
17437
|
+
region: this.region,
|
|
17438
|
+
service: "bedrock",
|
|
17439
|
+
credentials: this.credentials
|
|
17440
|
+
});
|
|
17441
|
+
return this.httpFetch(url, { method: "POST", headers, body: bodyStr, signal });
|
|
17442
|
+
}, signal);
|
|
17443
|
+
if (!res.ok) {
|
|
17444
|
+
const text = await res.text().catch(() => "");
|
|
17445
|
+
throw new ProviderError(`Bedrock API error ${res.status}: ${text}`, this.providerName, res.status, text);
|
|
17446
|
+
}
|
|
17447
|
+
return res;
|
|
17448
|
+
}
|
|
16299
17449
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
16300
|
-
buildConverseBody(request) {
|
|
17450
|
+
buildConverseBody(request, downgradeToolChoice = false) {
|
|
16301
17451
|
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
16302
17452
|
const body = {
|
|
16303
17453
|
messages: this.toBedrockMessages(messages)
|
|
@@ -16321,8 +17471,9 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16321
17471
|
body.toolConfig = {
|
|
16322
17472
|
tools
|
|
16323
17473
|
};
|
|
17474
|
+
const forceOk = !downgradeToolChoice && supportsForcedToolChoice(request.model || this.defaultModel);
|
|
16324
17475
|
if (request.toolChoice != null) {
|
|
16325
|
-
if (request.toolChoice === "auto") {
|
|
17476
|
+
if (request.toolChoice === "auto" || !forceOk && request.toolChoice !== "none") {
|
|
16326
17477
|
body.toolConfig.toolChoice = { auto: {} };
|
|
16327
17478
|
} else if (request.toolChoice === "required") {
|
|
16328
17479
|
body.toolConfig.toolChoice = { any: {} };
|
|
@@ -16332,9 +17483,17 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16332
17483
|
}
|
|
16333
17484
|
}
|
|
16334
17485
|
if (request.extra) {
|
|
16335
|
-
|
|
17486
|
+
const fields = { ...request.extra.additionalModelRequestFields ?? {} };
|
|
17487
|
+
const topLevel = { ...request.extra.__top ?? {} };
|
|
17488
|
+
for (const [key, value] of Object.entries(request.extra)) {
|
|
17489
|
+
if (key === "additionalModelRequestFields" || key === "__top") continue;
|
|
17490
|
+
if (CONVERSE_TOP_LEVEL_FIELDS.has(key)) body[key] = value;
|
|
17491
|
+
else fields[key] = value;
|
|
17492
|
+
}
|
|
17493
|
+
Object.assign(body, topLevel);
|
|
17494
|
+
if (Object.keys(fields).length > 0) body.additionalModelRequestFields = fields;
|
|
16336
17495
|
}
|
|
16337
|
-
applyBedrockReasoning(body, request.reasoning);
|
|
17496
|
+
applyBedrockReasoning(body, request.reasoning, { model: request.model || this.defaultModel, mode: this.thinkingMode });
|
|
16338
17497
|
return body;
|
|
16339
17498
|
}
|
|
16340
17499
|
splitSystemMessages(messages) {
|
|
@@ -16457,9 +17616,22 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16457
17616
|
}
|
|
16458
17617
|
};
|
|
16459
17618
|
}
|
|
16460
|
-
|
|
17619
|
+
/** Sends the Converse call; retries once with toolChoice auto when the
|
|
17620
|
+
* model rejects forced tool use. */
|
|
17621
|
+
async converseFetch(modelId, request, stream) {
|
|
17622
|
+
const body = this.buildConverseBody(request);
|
|
17623
|
+
try {
|
|
17624
|
+
return await this.doFetch(modelId, body, request.signal, stream);
|
|
17625
|
+
} catch (err) {
|
|
17626
|
+
const tc = body.toolConfig?.toolChoice;
|
|
17627
|
+
if (!(err instanceof ProviderError) || err.statusCode !== 400 || !tc || tc.auto) throw err;
|
|
17628
|
+
if (!forcedToolChoiceRejected(String(err.responseBody ?? err.message))) throw err;
|
|
17629
|
+
return this.doFetch(modelId, this.buildConverseBody(request, true), request.signal, stream);
|
|
17630
|
+
}
|
|
17631
|
+
}
|
|
17632
|
+
async doFetch(modelId, body, signal, stream) {
|
|
16461
17633
|
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
16462
|
-
const url = `https://${host}/model/${encodeURIComponent(modelId)}
|
|
17634
|
+
const url = `https://${host}/model/${encodeURIComponent(modelId)}/${stream ? "converse-stream" : "converse"}`;
|
|
16463
17635
|
const bodyStr = JSON.stringify(body);
|
|
16464
17636
|
const res = await this.doFetchWithRetry(() => {
|
|
16465
17637
|
const headers = signRequest({
|
|
@@ -16467,7 +17639,7 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16467
17639
|
url,
|
|
16468
17640
|
headers: {
|
|
16469
17641
|
"Content-Type": "application/json",
|
|
16470
|
-
Accept: "application/json",
|
|
17642
|
+
Accept: stream ? "application/vnd.amazon.eventstream" : "application/json",
|
|
16471
17643
|
...this.defaultHeaders
|
|
16472
17644
|
},
|
|
16473
17645
|
body: bodyStr,
|
|
@@ -16475,8 +17647,8 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16475
17647
|
service: "bedrock",
|
|
16476
17648
|
credentials: this.credentials
|
|
16477
17649
|
});
|
|
16478
|
-
return
|
|
16479
|
-
});
|
|
17650
|
+
return this.httpFetch(url, { method: "POST", headers, body: bodyStr, signal });
|
|
17651
|
+
}, signal);
|
|
16480
17652
|
if (!res.ok) {
|
|
16481
17653
|
const text = await res.text().catch(() => "");
|
|
16482
17654
|
throw new ProviderError(
|
|
@@ -16486,7 +17658,7 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16486
17658
|
text
|
|
16487
17659
|
);
|
|
16488
17660
|
}
|
|
16489
|
-
return res
|
|
17661
|
+
return res;
|
|
16490
17662
|
}
|
|
16491
17663
|
parseResponse(json, modelId) {
|
|
16492
17664
|
const output = json.output?.message;
|
|
@@ -16522,8 +17694,8 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16522
17694
|
inputTokens: usage.inputTokens ?? 0,
|
|
16523
17695
|
outputTokens: usage.outputTokens ?? 0,
|
|
16524
17696
|
totalTokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
|
|
16525
|
-
cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
|
|
16526
|
-
cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
|
|
17697
|
+
cachedInputTokens: usage.cacheReadInputTokens ?? usage.cacheReadInputTokenCount ?? 0,
|
|
17698
|
+
cachedWriteTokens: usage.cacheWriteInputTokens ?? usage.cacheWriteInputTokenCount ?? 0,
|
|
16527
17699
|
cachedOutputTokens: 0,
|
|
16528
17700
|
// Bedrock bills thinking tokens inside outputTokens; no separate count.
|
|
16529
17701
|
reasoningTokens: 0
|
|
@@ -16535,11 +17707,26 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
16535
17707
|
toolCalls,
|
|
16536
17708
|
usage: tokenUsage,
|
|
16537
17709
|
finishReason: mapBedrockStopReason(json.stopReason),
|
|
17710
|
+
...typeof json.stopReason === "string" ? { rawFinishReason: json.stopReason } : {},
|
|
16538
17711
|
reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
|
|
16539
17712
|
raw: json
|
|
16540
17713
|
};
|
|
16541
17714
|
}
|
|
16542
17715
|
};
|
|
17716
|
+
var CONVERSE_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
|
|
17717
|
+
"messages",
|
|
17718
|
+
"system",
|
|
17719
|
+
"inferenceConfig",
|
|
17720
|
+
"toolConfig",
|
|
17721
|
+
"guardrailConfig",
|
|
17722
|
+
"additionalModelRequestFields",
|
|
17723
|
+
"additionalModelResponseFieldPaths",
|
|
17724
|
+
"promptVariables",
|
|
17725
|
+
"requestMetadata",
|
|
17726
|
+
"performanceConfig",
|
|
17727
|
+
"serviceTier",
|
|
17728
|
+
"outputConfig"
|
|
17729
|
+
]);
|
|
16543
17730
|
function appendBedrockReasoning(content, blocks) {
|
|
16544
17731
|
if (!blocks?.length) return;
|
|
16545
17732
|
for (const b of blocks) {
|
|
@@ -16554,6 +17741,23 @@ function appendBedrockReasoning(content, blocks) {
|
|
|
16554
17741
|
}
|
|
16555
17742
|
}
|
|
16556
17743
|
}
|
|
17744
|
+
function bedrockExceptionStatus(code) {
|
|
17745
|
+
switch (code) {
|
|
17746
|
+
case "throttlingException":
|
|
17747
|
+
return 429;
|
|
17748
|
+
case "validationException":
|
|
17749
|
+
return 400;
|
|
17750
|
+
case "modelTimeoutException":
|
|
17751
|
+
return 408;
|
|
17752
|
+
case "serviceUnavailableException":
|
|
17753
|
+
return 503;
|
|
17754
|
+
case "internalServerException":
|
|
17755
|
+
case "modelStreamErrorException":
|
|
17756
|
+
return 500;
|
|
17757
|
+
default:
|
|
17758
|
+
return void 0;
|
|
17759
|
+
}
|
|
17760
|
+
}
|
|
16557
17761
|
function mapBedrockStopReason(reason) {
|
|
16558
17762
|
switch (reason) {
|
|
16559
17763
|
case "end_turn":
|
|
@@ -16562,8 +17766,10 @@ function mapBedrockStopReason(reason) {
|
|
|
16562
17766
|
case "tool_use":
|
|
16563
17767
|
return "tool_calls";
|
|
16564
17768
|
case "max_tokens":
|
|
17769
|
+
case "model_context_window_exceeded":
|
|
16565
17770
|
return "length";
|
|
16566
17771
|
case "content_filtered":
|
|
17772
|
+
case "guardrail_intervened":
|
|
16567
17773
|
case "guardrail":
|
|
16568
17774
|
return "content_filter";
|
|
16569
17775
|
default:
|
|
@@ -16593,10 +17799,12 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16593
17799
|
super();
|
|
16594
17800
|
this.providerName = "vertex";
|
|
16595
17801
|
this.tokenExpiry = 0;
|
|
17802
|
+
this.configureTransport(config);
|
|
16596
17803
|
this.projectId = config.projectId;
|
|
16597
17804
|
this.location = config.location ?? "us-central1";
|
|
16598
17805
|
this.defaultModel = config.defaultModel ?? "gemini-2.0-flash";
|
|
16599
17806
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
17807
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
16600
17808
|
if (config.accessToken) {
|
|
16601
17809
|
this.accessToken = config.accessToken;
|
|
16602
17810
|
this.tokenExpiry = Date.now() + 36e5;
|
|
@@ -16609,7 +17817,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16609
17817
|
const modelId = request.model || this.defaultModel;
|
|
16610
17818
|
const body = this.buildGeminiBody(request);
|
|
16611
17819
|
const url = this.buildUrl(modelId, false);
|
|
16612
|
-
const res = await this.doFetch(url, body);
|
|
17820
|
+
const res = await this.doFetch(url, body, request.signal);
|
|
16613
17821
|
const json = await res.json();
|
|
16614
17822
|
return this.parseResponse(json, modelId);
|
|
16615
17823
|
}
|
|
@@ -16617,11 +17825,12 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16617
17825
|
const modelId = request.model || this.defaultModel;
|
|
16618
17826
|
const body = this.buildGeminiBody(request);
|
|
16619
17827
|
const url = this.buildUrl(modelId, true);
|
|
16620
|
-
const res = await this.doFetch(url, body);
|
|
17828
|
+
const res = await this.doFetch(url, body, request.signal);
|
|
16621
17829
|
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
17830
|
+
const counter = { next: 0 };
|
|
16622
17831
|
const contentType = res.headers.get("content-type") ?? "";
|
|
16623
17832
|
if (contentType.includes("text/event-stream")) {
|
|
16624
|
-
for await (const event of parseSSEStream(res.body)) {
|
|
17833
|
+
for await (const event of parseSSEStream(res.body, request.signal)) {
|
|
16625
17834
|
if (event.data === "[DONE]") break;
|
|
16626
17835
|
let chunk;
|
|
16627
17836
|
try {
|
|
@@ -16629,7 +17838,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16629
17838
|
} catch {
|
|
16630
17839
|
continue;
|
|
16631
17840
|
}
|
|
16632
|
-
yield this.parseStreamChunk(chunk, modelId);
|
|
17841
|
+
yield this.parseStreamChunk(chunk, modelId, counter);
|
|
16633
17842
|
}
|
|
16634
17843
|
} else {
|
|
16635
17844
|
const reader = res.body.getReader();
|
|
@@ -16647,7 +17856,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16647
17856
|
if (!trimmed || trimmed === "[" || trimmed === "]") continue;
|
|
16648
17857
|
try {
|
|
16649
17858
|
const chunk = JSON.parse(trimmed);
|
|
16650
|
-
yield this.parseStreamChunk(chunk, modelId);
|
|
17859
|
+
yield this.parseStreamChunk(chunk, modelId, counter);
|
|
16651
17860
|
} catch {
|
|
16652
17861
|
buffer = line + "\n" + buffer;
|
|
16653
17862
|
break;
|
|
@@ -16658,16 +17867,59 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16658
17867
|
if (remaining && remaining !== "[" && remaining !== "]") {
|
|
16659
17868
|
try {
|
|
16660
17869
|
const chunk = JSON.parse(remaining);
|
|
16661
|
-
yield this.parseStreamChunk(chunk, modelId);
|
|
17870
|
+
yield this.parseStreamChunk(chunk, modelId, counter);
|
|
16662
17871
|
} catch {
|
|
16663
17872
|
}
|
|
16664
17873
|
}
|
|
16665
17874
|
} finally {
|
|
17875
|
+
reader.cancel().catch(() => void 0);
|
|
16666
17876
|
reader.releaseLock();
|
|
16667
17877
|
}
|
|
16668
17878
|
}
|
|
16669
17879
|
}
|
|
16670
17880
|
// ─── Internals ───────────────────────────────────────────────────────────
|
|
17881
|
+
/**
|
|
17882
|
+
* Vertex `:predict` embeddings (text-embedding-00x, text-multilingual-embedding,
|
|
17883
|
+
* gemini-embedding-001). `inputType` maps to the task type; `dimensions` to
|
|
17884
|
+
* `outputDimensionality`. gemini-embedding takes one instance per request, the
|
|
17885
|
+
* text-embedding models up to `batchSize` (default 100).
|
|
17886
|
+
*/
|
|
17887
|
+
async embed(request) {
|
|
17888
|
+
const model = requireEmbeddingModel(this.providerName, request.model);
|
|
17889
|
+
const perRequest = request.batchSize ?? (/gemini-embedding/i.test(model) ? 1 : 100);
|
|
17890
|
+
const url = `https://${this.location}-aiplatform.googleapis.com/v1/projects/${this.projectId}/locations/${this.location}/publishers/google/models/${model}:predict`;
|
|
17891
|
+
const taskType = request.inputType ? Object.prototype.hasOwnProperty.call(GEMINI_TASK_TYPES, request.inputType) ? GEMINI_TASK_TYPES[request.inputType] : request.inputType.toUpperCase() : void 0;
|
|
17892
|
+
const vectors = [];
|
|
17893
|
+
const raw = [];
|
|
17894
|
+
let inputTokens = 0;
|
|
17895
|
+
for (const batch of chunkArray(request.input, perRequest)) {
|
|
17896
|
+
const body = {
|
|
17897
|
+
instances: batch.map((content) => ({ content, ...taskType ? { task_type: taskType } : {} }))
|
|
17898
|
+
};
|
|
17899
|
+
const { parameters: extraParameters, ...extraRest } = request.extra ?? {};
|
|
17900
|
+
const parameters = {
|
|
17901
|
+
...request.dimensions != null ? { outputDimensionality: request.dimensions } : {},
|
|
17902
|
+
// Caller-supplied parameters are merged in, not replacing the above (an
|
|
17903
|
+
// explicit extra.parameters.outputDimensionality still wins).
|
|
17904
|
+
...extraParameters && typeof extraParameters === "object" ? extraParameters : {}
|
|
17905
|
+
};
|
|
17906
|
+
if (Object.keys(parameters).length > 0) body.parameters = parameters;
|
|
17907
|
+
Object.assign(body, extraRest);
|
|
17908
|
+
const res = await this.doFetch(url, body, request.signal);
|
|
17909
|
+
const json = await res.json();
|
|
17910
|
+
raw.push(json);
|
|
17911
|
+
for (const p of json.predictions ?? []) {
|
|
17912
|
+
const values = p.embeddings?.values;
|
|
17913
|
+
if (!Array.isArray(values)) {
|
|
17914
|
+
throw new ProviderError("Vertex embeddings response has a prediction without an embeddings.values array", this.providerName, void 0, json);
|
|
17915
|
+
}
|
|
17916
|
+
vectors.push(values);
|
|
17917
|
+
inputTokens += p.embeddings?.statistics?.token_count ?? 0;
|
|
17918
|
+
}
|
|
17919
|
+
}
|
|
17920
|
+
assertVectorCount(this.providerName, vectors, request.input.length);
|
|
17921
|
+
return { model, vectors, usage: { inputTokens, totalTokens: inputTokens }, raw };
|
|
17922
|
+
}
|
|
16671
17923
|
buildUrl(modelId, stream) {
|
|
16672
17924
|
const base = `https://${this.location}-aiplatform.googleapis.com/v1`;
|
|
16673
17925
|
const path2 = `projects/${this.projectId}/locations/${this.location}/publishers/google/models/${modelId}`;
|
|
@@ -16765,7 +18017,9 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16765
18017
|
functionCall: {
|
|
16766
18018
|
name: tc.name,
|
|
16767
18019
|
args: safeJsonParse3(tc.arguments)
|
|
16768
|
-
}
|
|
18020
|
+
},
|
|
18021
|
+
// Gemini 3 requires the signature of each functionCall echoed back.
|
|
18022
|
+
...tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}
|
|
16769
18023
|
});
|
|
16770
18024
|
}
|
|
16771
18025
|
return { role: "model", parts: parts2 };
|
|
@@ -16862,7 +18116,7 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16862
18116
|
sign.update(signingInput);
|
|
16863
18117
|
const signature = sign.sign(sa.private_key, "base64url");
|
|
16864
18118
|
const jwt = `${signingInput}.${signature}`;
|
|
16865
|
-
const tokenRes = await
|
|
18119
|
+
const tokenRes = await this.httpFetch("https://oauth2.googleapis.com/token", {
|
|
16866
18120
|
method: "POST",
|
|
16867
18121
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
16868
18122
|
body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`
|
|
@@ -16874,17 +18128,22 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16874
18128
|
const tokenJson = await tokenRes.json();
|
|
16875
18129
|
return tokenJson.access_token;
|
|
16876
18130
|
}
|
|
16877
|
-
async doFetch(url, body) {
|
|
18131
|
+
async doFetch(url, body, signal) {
|
|
16878
18132
|
const token = await this.getAccessToken();
|
|
16879
|
-
const
|
|
16880
|
-
|
|
16881
|
-
|
|
16882
|
-
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
|
|
16886
|
-
|
|
16887
|
-
|
|
18133
|
+
const bodyStr = JSON.stringify(body);
|
|
18134
|
+
const res = await this.doFetchWithRetry(
|
|
18135
|
+
() => this.httpFetch(url, {
|
|
18136
|
+
method: "POST",
|
|
18137
|
+
headers: {
|
|
18138
|
+
"Content-Type": "application/json",
|
|
18139
|
+
Authorization: `Bearer ${token}`,
|
|
18140
|
+
...this.defaultHeaders
|
|
18141
|
+
},
|
|
18142
|
+
body: bodyStr,
|
|
18143
|
+
signal
|
|
18144
|
+
}),
|
|
18145
|
+
signal
|
|
18146
|
+
);
|
|
16888
18147
|
if (!res.ok) {
|
|
16889
18148
|
const text = await res.text().catch(() => "");
|
|
16890
18149
|
throw new ProviderError(
|
|
@@ -16914,7 +18173,8 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16914
18173
|
toolCalls.push({
|
|
16915
18174
|
id: `call_${toolCallIdx++}`,
|
|
16916
18175
|
name: part.functionCall.name,
|
|
16917
|
-
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
18176
|
+
arguments: JSON.stringify(part.functionCall.args ?? {}),
|
|
18177
|
+
...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {}
|
|
16918
18178
|
});
|
|
16919
18179
|
}
|
|
16920
18180
|
}
|
|
@@ -16935,11 +18195,12 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16935
18195
|
toolCalls,
|
|
16936
18196
|
usage: tokenUsage,
|
|
16937
18197
|
finishReason: mapGeminiFinishReason(candidate?.finishReason),
|
|
18198
|
+
...typeof candidate?.finishReason === "string" ? { rawFinishReason: candidate.finishReason } : {},
|
|
16938
18199
|
reasoning: thoughtParts.length > 0 ? { summary: thoughtParts.join("\n").trim() || void 0 } : void 0,
|
|
16939
18200
|
raw: json
|
|
16940
18201
|
};
|
|
16941
18202
|
}
|
|
16942
|
-
parseStreamChunk(chunk, modelId) {
|
|
18203
|
+
parseStreamChunk(chunk, modelId, counter) {
|
|
16943
18204
|
const candidate = chunk.candidates?.[0];
|
|
16944
18205
|
const parts = candidate?.content?.parts ?? [];
|
|
16945
18206
|
const delta = {};
|
|
@@ -16953,9 +18214,12 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16953
18214
|
} else if (part.functionCall) {
|
|
16954
18215
|
if (!delta.toolCalls) delta.toolCalls = [];
|
|
16955
18216
|
delta.toolCalls.push({
|
|
16956
|
-
|
|
18217
|
+
// Unique across the whole stream: the adapter merges chunks by id.
|
|
18218
|
+
id: `call_${counter.next}`,
|
|
18219
|
+
index: counter.next++,
|
|
16957
18220
|
name: part.functionCall.name,
|
|
16958
|
-
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
18221
|
+
arguments: JSON.stringify(part.functionCall.args ?? {}),
|
|
18222
|
+
...part.thoughtSignature ? { thoughtSignature: part.thoughtSignature } : {}
|
|
16959
18223
|
});
|
|
16960
18224
|
}
|
|
16961
18225
|
}
|
|
@@ -16977,10 +18241,21 @@ var VertexProvider = class extends BaseProvider {
|
|
|
16977
18241
|
model: modelId,
|
|
16978
18242
|
delta,
|
|
16979
18243
|
usage,
|
|
16980
|
-
finishReason: mapGeminiFinishReason(candidate?.finishReason)
|
|
18244
|
+
finishReason: mapGeminiFinishReason(candidate?.finishReason),
|
|
18245
|
+
...typeof candidate?.finishReason === "string" ? { rawFinishReason: candidate.finishReason } : {}
|
|
16981
18246
|
};
|
|
16982
18247
|
}
|
|
16983
18248
|
};
|
|
18249
|
+
var GEMINI_TASK_TYPES = {
|
|
18250
|
+
search_document: "RETRIEVAL_DOCUMENT",
|
|
18251
|
+
search_query: "RETRIEVAL_QUERY",
|
|
18252
|
+
classification: "CLASSIFICATION",
|
|
18253
|
+
clustering: "CLUSTERING",
|
|
18254
|
+
semantic_similarity: "SEMANTIC_SIMILARITY",
|
|
18255
|
+
question_answering: "QUESTION_ANSWERING",
|
|
18256
|
+
fact_verification: "FACT_VERIFICATION",
|
|
18257
|
+
code_retrieval_query: "CODE_RETRIEVAL_QUERY"
|
|
18258
|
+
};
|
|
16984
18259
|
function mapGeminiFinishReason(reason) {
|
|
16985
18260
|
switch (reason) {
|
|
16986
18261
|
case "STOP":
|
|
@@ -17022,6 +18297,8 @@ function createProvider(config) {
|
|
|
17022
18297
|
return new BedrockProvider(config);
|
|
17023
18298
|
case "vertex":
|
|
17024
18299
|
return new VertexProvider(config);
|
|
18300
|
+
case "jev":
|
|
18301
|
+
return new JevProvider(config);
|
|
17025
18302
|
default:
|
|
17026
18303
|
throw new Error(`Unknown provider: ${config.provider}`);
|
|
17027
18304
|
}
|
|
@@ -17049,6 +18326,7 @@ exports.HOOK_CONTRACT_VERSION = HOOK_CONTRACT_VERSION;
|
|
|
17049
18326
|
exports.HOOK_NAMES = HOOK_NAMES;
|
|
17050
18327
|
exports.HookTimeoutError = HookTimeoutError;
|
|
17051
18328
|
exports.InMemoryMemoryStore = InMemoryMemoryStore;
|
|
18329
|
+
exports.JevProvider = JevProvider;
|
|
17052
18330
|
exports.NativeJsonSchemaStrategy = NativeJsonSchemaStrategy;
|
|
17053
18331
|
exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
|
|
17054
18332
|
exports.OpenAIProvider = OpenAIProvider;
|
|
@@ -17088,6 +18366,7 @@ exports.compressLogOutput = compressLogOutput;
|
|
|
17088
18366
|
exports.compressSearchOutput = compressSearchOutput;
|
|
17089
18367
|
exports.compressTextOutput = compressTextOutput;
|
|
17090
18368
|
exports.compressToolOutput = compressToolOutput;
|
|
18369
|
+
exports.contentPartToText = contentPartToText;
|
|
17091
18370
|
exports.contentToString = contentToString;
|
|
17092
18371
|
exports.conversationHistory = conversationHistory;
|
|
17093
18372
|
exports.countApproxTokens = countApproxTokens;
|
|
@@ -17130,6 +18409,7 @@ exports.dedupeToolsByName = dedupeToolsByName;
|
|
|
17130
18409
|
exports.defaultTokenCounter = defaultTokenCounter;
|
|
17131
18410
|
exports.defineHook = defineHook;
|
|
17132
18411
|
exports.definePlugin = definePlugin;
|
|
18412
|
+
exports.describeContentPart = describeContentPart;
|
|
17133
18413
|
exports.detectLanguage = detectLanguage;
|
|
17134
18414
|
exports.detectTextFormat = detectTextFormat;
|
|
17135
18415
|
exports.detectVolatileContent = detectVolatileContent;
|
|
@@ -17138,7 +18418,7 @@ exports.digestToolInputValue = digestToolInputValue;
|
|
|
17138
18418
|
exports.emptyUsage = emptyUsage;
|
|
17139
18419
|
exports.evaluateGuardrails = evaluateGuardrails;
|
|
17140
18420
|
exports.extractLatestUserQuery = extractLatestUserQuery;
|
|
17141
|
-
exports.extractMessageText =
|
|
18421
|
+
exports.extractMessageText = extractMessageText;
|
|
17142
18422
|
exports.fileCheckpointStore = fileCheckpointStore;
|
|
17143
18423
|
exports.fileSink = fileSink;
|
|
17144
18424
|
exports.findSubagent = findSubagent;
|
|
@@ -17152,6 +18432,7 @@ exports.getModelCapabilities = getModelCapabilities;
|
|
|
17152
18432
|
exports.getNestedValue = getNestedValue;
|
|
17153
18433
|
exports.getResolvedSmartConfig = getResolvedSmartConfig;
|
|
17154
18434
|
exports.getTokenCounter = getTokenCounter;
|
|
18435
|
+
exports.hasMediaPart = hasMediaPart;
|
|
17155
18436
|
exports.hasToolResponseRecoveryReference = hasToolResponseRecoveryReference;
|
|
17156
18437
|
exports.httpGuardrail = httpGuardrail;
|
|
17157
18438
|
exports.httpSink = httpSink;
|
|
@@ -17171,6 +18452,7 @@ exports.loadSkillsFromDisk = loadSkillsFromDisk;
|
|
|
17171
18452
|
exports.mapConsoleHookVerdict = mapConsoleHookVerdict;
|
|
17172
18453
|
exports.mapTextParts = mapTextParts;
|
|
17173
18454
|
exports.mcp = mcp;
|
|
18455
|
+
exports.mediaKindOf = mediaKindOf;
|
|
17174
18456
|
exports.mediaPolicy = mediaPolicy;
|
|
17175
18457
|
exports.normalizeSmartAgentOptions = normalizeSmartAgentOptions;
|
|
17176
18458
|
exports.normalizeVerdicts = normalizeVerdicts;
|
|
@@ -17217,7 +18499,9 @@ exports.sessionMetrics = sessionMetrics;
|
|
|
17217
18499
|
exports.setTokenCounter = setTokenCounter;
|
|
17218
18500
|
exports.startStreamingSession = startStreamingSession;
|
|
17219
18501
|
exports.summarizeObject = summarizeObject;
|
|
18502
|
+
exports.supportsAdaptiveThinking = supportsAdaptiveThinking;
|
|
17220
18503
|
exports.textFromContent = textFromContent;
|
|
18504
|
+
exports.toMessageUsage = toMessageUsage;
|
|
17221
18505
|
exports.toStrictCompatible = toStrictCompatible;
|
|
17222
18506
|
exports.toStrictToolSchema = toStrictToolSchema;
|
|
17223
18507
|
exports.tokenize = tokenize;
|