extrait 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/extract-parse-hint.d.ts +3 -0
- package/dist/extract-shape.d.ts +21 -0
- package/dist/generate-debug.d.ts +26 -0
- package/dist/generate-model-call.d.ts +3 -0
- package/dist/generate-output.d.ts +10 -0
- package/dist/generate-shared.d.ts +12 -10
- package/dist/generate-tool-timeout.d.ts +3 -0
- package/dist/index.cjs +1301 -1156
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1301 -1156
- package/dist/parse.d.ts +1 -1
- package/dist/providers/mcp-runtime-debug.d.ts +3 -0
- package/dist/providers/mcp-runtime.d.ts +2 -2
- package/dist/structured-streaming.d.ts +1 -0
- package/dist/structured.d.ts +3 -3
- package/dist/type-definitions/llm.d.ts +246 -0
- package/dist/type-definitions/markdown.d.ts +10 -0
- package/dist/type-definitions/parse.d.ts +93 -0
- package/dist/type-definitions/structured.d.ts +123 -0
- package/dist/types.d.ts +4 -442
- package/package.json +8 -14
package/dist/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
3
|
|
|
4
|
-
// src/extract.ts
|
|
5
|
-
import { jsonrepair } from "jsonrepair";
|
|
6
|
-
|
|
7
4
|
// src/utils/common.ts
|
|
8
5
|
function toErrorMessage(error) {
|
|
9
6
|
if (error instanceof Error) {
|
|
@@ -221,184 +218,77 @@ function isOnlyWhitespace(value) {
|
|
|
221
218
|
return true;
|
|
222
219
|
}
|
|
223
220
|
|
|
224
|
-
// src/extract.ts
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
firstPassMin: 12,
|
|
230
|
-
firstPassCap: 30,
|
|
231
|
-
firstPassMultiplier: 6,
|
|
232
|
-
secondPassMin: 4,
|
|
233
|
-
secondPassCap: 8,
|
|
234
|
-
secondPassMultiplier: 2,
|
|
235
|
-
hintMaxLength: 50000
|
|
236
|
-
};
|
|
237
|
-
function extractJsonCandidates(input, options = {}) {
|
|
238
|
-
const maxCandidates = options.maxCandidates ?? 5;
|
|
239
|
-
const acceptArrays = options.acceptArrays ?? true;
|
|
240
|
-
const allowRepairHints = options.allowRepairHints ?? true;
|
|
241
|
-
const heuristics = resolveExtractionHeuristics(options.heuristics);
|
|
242
|
-
const extractionInput = input;
|
|
243
|
-
const candidates = [];
|
|
244
|
-
candidates.push(...extractFromMarkdown(extractionInput, acceptArrays));
|
|
245
|
-
candidates.push(...scanBalancedSegments(extractionInput, acceptArrays));
|
|
246
|
-
if (candidates.length === 0 && extractionInput.trim()) {
|
|
247
|
-
const content = extractionInput.trim();
|
|
248
|
-
candidates.push({
|
|
249
|
-
id: "raw:fallback",
|
|
250
|
-
source: "raw",
|
|
251
|
-
content,
|
|
252
|
-
start: 0,
|
|
253
|
-
end: extractionInput.length,
|
|
254
|
-
score: 10 + Math.floor(lengthScore(content.length) / 3)
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
const prefiltered = prefilterByJsonShape(candidates, acceptArrays);
|
|
258
|
-
const firstPassLimit = clamp(maxCandidates * heuristics.firstPassMultiplier, heuristics.firstPassMin, heuristics.firstPassCap);
|
|
259
|
-
const firstPass = prefiltered.slice(0, firstPassLimit);
|
|
260
|
-
const secondPassLimit = Math.min(firstPass.length, clamp(maxCandidates * heuristics.secondPassMultiplier, heuristics.secondPassMin, heuristics.secondPassCap));
|
|
261
|
-
for (let i = 0;i < secondPassLimit; i += 1) {
|
|
262
|
-
const candidate = firstPass[i];
|
|
263
|
-
if (!candidate) {
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
const parseHint = buildParseHint(candidate.content, allowRepairHints, heuristics.hintMaxLength);
|
|
267
|
-
if (!parseHint) {
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
candidate.parseHint = parseHint;
|
|
271
|
-
candidate.score += parseHintBonus(parseHint);
|
|
221
|
+
// src/extract-parse-hint.ts
|
|
222
|
+
import { jsonrepair } from "jsonrepair";
|
|
223
|
+
function buildParseHint(content, allowRepair, hintMaxLength) {
|
|
224
|
+
if (content.length > hintMaxLength) {
|
|
225
|
+
return null;
|
|
272
226
|
}
|
|
273
|
-
|
|
274
|
-
const deduped = dedupeCandidates(sorted);
|
|
275
|
-
return deduped.slice(0, maxCandidates).map((candidate, index) => ({
|
|
276
|
-
id: `${candidate.source}:${index}`,
|
|
277
|
-
source: candidate.source,
|
|
278
|
-
content: candidate.content,
|
|
279
|
-
language: candidate.language,
|
|
280
|
-
parseHint: candidate.parseHint,
|
|
281
|
-
start: candidate.start,
|
|
282
|
-
end: candidate.end,
|
|
283
|
-
score: candidate.score
|
|
284
|
-
}));
|
|
285
|
-
}
|
|
286
|
-
function prefilterByJsonShape(candidates, acceptArrays) {
|
|
287
|
-
const shaped = candidates.map((candidate) => {
|
|
288
|
-
const shapeScore = jsonShapeScore(candidate.content, acceptArrays);
|
|
227
|
+
try {
|
|
289
228
|
return {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
229
|
+
success: true,
|
|
230
|
+
parsed: JSON.parse(content),
|
|
231
|
+
repaired: null,
|
|
232
|
+
usedRepair: false,
|
|
233
|
+
stage: "parse",
|
|
234
|
+
error: ""
|
|
293
235
|
};
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
236
|
+
} catch (directError) {
|
|
237
|
+
if (!allowRepair) {
|
|
238
|
+
return {
|
|
239
|
+
success: false,
|
|
240
|
+
parsed: null,
|
|
241
|
+
repaired: null,
|
|
242
|
+
usedRepair: false,
|
|
243
|
+
stage: "parse",
|
|
244
|
+
error: toErrorMessage(directError)
|
|
245
|
+
};
|
|
302
246
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
247
|
+
let repaired;
|
|
248
|
+
try {
|
|
249
|
+
repaired = jsonrepair(content);
|
|
250
|
+
} catch (repairError) {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
parsed: null,
|
|
254
|
+
repaired: null,
|
|
255
|
+
usedRepair: true,
|
|
256
|
+
stage: "repair",
|
|
257
|
+
error: toErrorMessage(repairError)
|
|
258
|
+
};
|
|
314
259
|
}
|
|
315
|
-
|
|
316
|
-
return
|
|
260
|
+
try {
|
|
261
|
+
return {
|
|
262
|
+
success: true,
|
|
263
|
+
parsed: JSON.parse(repaired),
|
|
264
|
+
repaired,
|
|
265
|
+
usedRepair: true,
|
|
266
|
+
stage: "parse",
|
|
267
|
+
error: ""
|
|
268
|
+
};
|
|
269
|
+
} catch (parseError) {
|
|
270
|
+
return {
|
|
271
|
+
success: false,
|
|
272
|
+
parsed: null,
|
|
273
|
+
repaired,
|
|
274
|
+
usedRepair: true,
|
|
275
|
+
stage: "parse",
|
|
276
|
+
error: toErrorMessage(parseError || directError)
|
|
277
|
+
};
|
|
317
278
|
}
|
|
318
|
-
|
|
319
|
-
return [
|
|
320
|
-
{
|
|
321
|
-
id: `fenced:${index}`,
|
|
322
|
-
source: "fenced",
|
|
323
|
-
language,
|
|
324
|
-
content,
|
|
325
|
-
start: block.start,
|
|
326
|
-
end: block.end,
|
|
327
|
-
score: 260 + langBonus + lengthScore(content.length)
|
|
328
|
-
}
|
|
329
|
-
];
|
|
330
|
-
});
|
|
279
|
+
}
|
|
331
280
|
}
|
|
332
|
-
function
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
let inString = false;
|
|
336
|
-
let quote = null;
|
|
337
|
-
let escaped = false;
|
|
338
|
-
for (let i = 0;i < input.length; i += 1) {
|
|
339
|
-
const char = input[i];
|
|
340
|
-
if (!char) {
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
if (inString) {
|
|
344
|
-
if (escaped) {
|
|
345
|
-
escaped = false;
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
if (char === "\\") {
|
|
349
|
-
escaped = true;
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
if (char === quote) {
|
|
353
|
-
inString = false;
|
|
354
|
-
quote = null;
|
|
355
|
-
}
|
|
356
|
-
continue;
|
|
357
|
-
}
|
|
358
|
-
const allowSingleQuoted = stack.length > 0;
|
|
359
|
-
if (char === '"' || allowSingleQuoted && (char === "'" || char === "`")) {
|
|
360
|
-
inString = true;
|
|
361
|
-
quote = char;
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
if (char === "{" || char === "[") {
|
|
365
|
-
stack.push({ char, index: i });
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
if (char !== "}" && char !== "]") {
|
|
369
|
-
continue;
|
|
370
|
-
}
|
|
371
|
-
const expectedOpen = char === "}" ? "{" : "[";
|
|
372
|
-
while (stack.length > 0 && stack[stack.length - 1]?.char !== expectedOpen) {
|
|
373
|
-
stack.pop();
|
|
374
|
-
}
|
|
375
|
-
const opened = stack.pop();
|
|
376
|
-
if (!opened) {
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
if (stack.length > 0) {
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
if (!acceptArrays && opened.char === "[") {
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
const content = input.slice(opened.index, i + 1).trim();
|
|
386
|
-
if (!content) {
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
const rootBonus = opened.char === "{" ? 40 : 20;
|
|
390
|
-
const boundaryBonus = boundaryScore(input, opened.index, i + 1);
|
|
391
|
-
results.push({
|
|
392
|
-
id: `scan:${results.length}`,
|
|
393
|
-
source: "scan",
|
|
394
|
-
content,
|
|
395
|
-
start: opened.index,
|
|
396
|
-
end: i + 1,
|
|
397
|
-
score: 120 + rootBonus + boundaryBonus + lengthScore(content.length)
|
|
398
|
-
});
|
|
281
|
+
function parseHintBonus(hint) {
|
|
282
|
+
if (hint.success) {
|
|
283
|
+
return hint.usedRepair ? 70 : 120;
|
|
399
284
|
}
|
|
400
|
-
return
|
|
285
|
+
return hint.usedRepair ? -20 : -10;
|
|
401
286
|
}
|
|
287
|
+
|
|
288
|
+
// src/extract-shape.ts
|
|
289
|
+
var RE_EMPTY_OBJECT = /^\{\s*\}$/;
|
|
290
|
+
var RE_EMPTY_ARRAY = /^\[\s*\]$/;
|
|
291
|
+
var RE_BOUNDARY_CHAR = /[\s,.;:!?`"'()[\]{}<>]/;
|
|
402
292
|
function looksLikeJsonEnvelope(content, acceptArrays) {
|
|
403
293
|
const trimmed = content.trim();
|
|
404
294
|
if (trimmed.startsWith("{")) {
|
|
@@ -507,95 +397,6 @@ function passesShapeFilter(candidate) {
|
|
|
507
397
|
}
|
|
508
398
|
return candidate.shapeScore >= 35;
|
|
509
399
|
}
|
|
510
|
-
function buildParseHint(content, allowRepair, hintMaxLength) {
|
|
511
|
-
if (content.length > hintMaxLength) {
|
|
512
|
-
return null;
|
|
513
|
-
}
|
|
514
|
-
try {
|
|
515
|
-
return {
|
|
516
|
-
success: true,
|
|
517
|
-
parsed: JSON.parse(content),
|
|
518
|
-
repaired: null,
|
|
519
|
-
usedRepair: false,
|
|
520
|
-
stage: "parse",
|
|
521
|
-
error: ""
|
|
522
|
-
};
|
|
523
|
-
} catch (directError) {
|
|
524
|
-
if (!allowRepair) {
|
|
525
|
-
return {
|
|
526
|
-
success: false,
|
|
527
|
-
parsed: null,
|
|
528
|
-
repaired: null,
|
|
529
|
-
usedRepair: false,
|
|
530
|
-
stage: "parse",
|
|
531
|
-
error: toErrorMessage(directError)
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
let repaired;
|
|
535
|
-
try {
|
|
536
|
-
repaired = jsonrepair(content);
|
|
537
|
-
} catch (repairError) {
|
|
538
|
-
return {
|
|
539
|
-
success: false,
|
|
540
|
-
parsed: null,
|
|
541
|
-
repaired: null,
|
|
542
|
-
usedRepair: true,
|
|
543
|
-
stage: "repair",
|
|
544
|
-
error: toErrorMessage(repairError)
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
try {
|
|
548
|
-
return {
|
|
549
|
-
success: true,
|
|
550
|
-
parsed: JSON.parse(repaired),
|
|
551
|
-
repaired,
|
|
552
|
-
usedRepair: true,
|
|
553
|
-
stage: "parse",
|
|
554
|
-
error: ""
|
|
555
|
-
};
|
|
556
|
-
} catch (parseError) {
|
|
557
|
-
return {
|
|
558
|
-
success: false,
|
|
559
|
-
parsed: null,
|
|
560
|
-
repaired,
|
|
561
|
-
usedRepair: true,
|
|
562
|
-
stage: "parse",
|
|
563
|
-
error: toErrorMessage(parseError || directError)
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
function resolveExtractionHeuristics(input) {
|
|
569
|
-
const merged = {
|
|
570
|
-
...DEFAULT_EXTRACTION_HEURISTICS,
|
|
571
|
-
...input
|
|
572
|
-
};
|
|
573
|
-
const firstPassMin = normalizeInteger(merged.firstPassMin, DEFAULT_EXTRACTION_HEURISTICS.firstPassMin);
|
|
574
|
-
const firstPassCap = Math.max(firstPassMin, normalizeInteger(merged.firstPassCap, DEFAULT_EXTRACTION_HEURISTICS.firstPassCap));
|
|
575
|
-
const secondPassMin = normalizeInteger(merged.secondPassMin, DEFAULT_EXTRACTION_HEURISTICS.secondPassMin);
|
|
576
|
-
const secondPassCap = Math.max(secondPassMin, normalizeInteger(merged.secondPassCap, DEFAULT_EXTRACTION_HEURISTICS.secondPassCap));
|
|
577
|
-
return {
|
|
578
|
-
firstPassMin,
|
|
579
|
-
firstPassCap,
|
|
580
|
-
firstPassMultiplier: normalizeInteger(merged.firstPassMultiplier, DEFAULT_EXTRACTION_HEURISTICS.firstPassMultiplier),
|
|
581
|
-
secondPassMin,
|
|
582
|
-
secondPassCap,
|
|
583
|
-
secondPassMultiplier: normalizeInteger(merged.secondPassMultiplier, DEFAULT_EXTRACTION_HEURISTICS.secondPassMultiplier),
|
|
584
|
-
hintMaxLength: normalizeInteger(merged.hintMaxLength, DEFAULT_EXTRACTION_HEURISTICS.hintMaxLength)
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
function normalizeInteger(value, fallback) {
|
|
588
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
589
|
-
return fallback;
|
|
590
|
-
}
|
|
591
|
-
return Math.max(0, Math.floor(value));
|
|
592
|
-
}
|
|
593
|
-
function parseHintBonus(hint) {
|
|
594
|
-
if (hint.success) {
|
|
595
|
-
return hint.usedRepair ? 70 : 120;
|
|
596
|
-
}
|
|
597
|
-
return hint.usedRepair ? -20 : -10;
|
|
598
|
-
}
|
|
599
400
|
function hasBalancedJsonDelimiters(input) {
|
|
600
401
|
const stack = [];
|
|
601
402
|
let inString = false;
|
|
@@ -681,6 +482,207 @@ function dedupeCandidates(candidates) {
|
|
|
681
482
|
function clamp(value, min, max) {
|
|
682
483
|
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
683
484
|
}
|
|
485
|
+
function normalizeInteger(value, fallback) {
|
|
486
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
487
|
+
return fallback;
|
|
488
|
+
}
|
|
489
|
+
return Math.max(0, Math.floor(value));
|
|
490
|
+
}
|
|
491
|
+
function resolveExtractionHeuristics(input, defaults) {
|
|
492
|
+
const merged = {
|
|
493
|
+
...defaults,
|
|
494
|
+
...input
|
|
495
|
+
};
|
|
496
|
+
const firstPassMin = normalizeInteger(merged.firstPassMin, defaults.firstPassMin);
|
|
497
|
+
const firstPassCap = Math.max(firstPassMin, normalizeInteger(merged.firstPassCap, defaults.firstPassCap));
|
|
498
|
+
const secondPassMin = normalizeInteger(merged.secondPassMin, defaults.secondPassMin);
|
|
499
|
+
const secondPassCap = Math.max(secondPassMin, normalizeInteger(merged.secondPassCap, defaults.secondPassCap));
|
|
500
|
+
return {
|
|
501
|
+
firstPassMin,
|
|
502
|
+
firstPassCap,
|
|
503
|
+
firstPassMultiplier: normalizeInteger(merged.firstPassMultiplier, defaults.firstPassMultiplier),
|
|
504
|
+
secondPassMin,
|
|
505
|
+
secondPassCap,
|
|
506
|
+
secondPassMultiplier: normalizeInteger(merged.secondPassMultiplier, defaults.secondPassMultiplier),
|
|
507
|
+
hintMaxLength: normalizeInteger(merged.hintMaxLength, defaults.hintMaxLength)
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/extract.ts
|
|
512
|
+
var DEFAULT_EXTRACTION_HEURISTICS = {
|
|
513
|
+
firstPassMin: 12,
|
|
514
|
+
firstPassCap: 30,
|
|
515
|
+
firstPassMultiplier: 6,
|
|
516
|
+
secondPassMin: 4,
|
|
517
|
+
secondPassCap: 8,
|
|
518
|
+
secondPassMultiplier: 2,
|
|
519
|
+
hintMaxLength: 50000
|
|
520
|
+
};
|
|
521
|
+
function extractJsonCandidates(input, options = {}) {
|
|
522
|
+
const maxCandidates = options.maxCandidates ?? 5;
|
|
523
|
+
const acceptArrays = options.acceptArrays ?? true;
|
|
524
|
+
const allowRepairHints = options.allowRepairHints ?? true;
|
|
525
|
+
const heuristics = resolveExtractionHeuristics(options.heuristics, DEFAULT_EXTRACTION_HEURISTICS);
|
|
526
|
+
const extractionInput = input;
|
|
527
|
+
const candidates = [];
|
|
528
|
+
candidates.push(...extractFromMarkdown(extractionInput, acceptArrays));
|
|
529
|
+
candidates.push(...scanBalancedSegments(extractionInput, acceptArrays));
|
|
530
|
+
if (candidates.length === 0 && extractionInput.trim()) {
|
|
531
|
+
const content = extractionInput.trim();
|
|
532
|
+
candidates.push({
|
|
533
|
+
id: "raw:fallback",
|
|
534
|
+
source: "raw",
|
|
535
|
+
content,
|
|
536
|
+
start: 0,
|
|
537
|
+
end: extractionInput.length,
|
|
538
|
+
score: 10 + Math.floor(lengthScore(content.length) / 3)
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
const prefiltered = prefilterByJsonShape(candidates, acceptArrays);
|
|
542
|
+
const firstPassLimit = clamp(maxCandidates * heuristics.firstPassMultiplier, heuristics.firstPassMin, heuristics.firstPassCap);
|
|
543
|
+
const firstPass = prefiltered.slice(0, firstPassLimit);
|
|
544
|
+
const secondPassLimit = Math.min(firstPass.length, clamp(maxCandidates * heuristics.secondPassMultiplier, heuristics.secondPassMin, heuristics.secondPassCap));
|
|
545
|
+
for (let i = 0;i < secondPassLimit; i += 1) {
|
|
546
|
+
const candidate = firstPass[i];
|
|
547
|
+
if (!candidate) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const parseHint = buildParseHint(candidate.content, allowRepairHints, heuristics.hintMaxLength);
|
|
551
|
+
if (!parseHint) {
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
candidate.parseHint = parseHint;
|
|
555
|
+
candidate.score += parseHintBonus(parseHint);
|
|
556
|
+
}
|
|
557
|
+
const sorted = sortCandidates(firstPass);
|
|
558
|
+
const deduped = dedupeCandidates(sorted);
|
|
559
|
+
return deduped.slice(0, maxCandidates).map((candidate, index) => ({
|
|
560
|
+
id: `${candidate.source}:${index}`,
|
|
561
|
+
source: candidate.source,
|
|
562
|
+
content: candidate.content,
|
|
563
|
+
language: candidate.language,
|
|
564
|
+
parseHint: candidate.parseHint,
|
|
565
|
+
start: candidate.start,
|
|
566
|
+
end: candidate.end,
|
|
567
|
+
score: candidate.score
|
|
568
|
+
}));
|
|
569
|
+
}
|
|
570
|
+
function prefilterByJsonShape(candidates, acceptArrays) {
|
|
571
|
+
const shaped = candidates.map((candidate) => {
|
|
572
|
+
const shapeScore = jsonShapeScore(candidate.content, acceptArrays);
|
|
573
|
+
return {
|
|
574
|
+
...candidate,
|
|
575
|
+
shapeScore,
|
|
576
|
+
score: candidate.score + shapeScore
|
|
577
|
+
};
|
|
578
|
+
});
|
|
579
|
+
const sorted = sortCandidates(shaped);
|
|
580
|
+
const deduped = dedupeCandidates(sorted);
|
|
581
|
+
const filtered = deduped.filter((candidate) => passesShapeFilter(candidate));
|
|
582
|
+
if (filtered.length > 0) {
|
|
583
|
+
const fallback = deduped.find((candidate) => !passesShapeFilter(candidate));
|
|
584
|
+
if (fallback) {
|
|
585
|
+
filtered.push(fallback);
|
|
586
|
+
}
|
|
587
|
+
return sortCandidates(filtered);
|
|
588
|
+
}
|
|
589
|
+
return deduped.slice(0, Math.min(1, deduped.length));
|
|
590
|
+
}
|
|
591
|
+
function extractFromMarkdown(input, acceptArrays) {
|
|
592
|
+
const blocks = extractMarkdownCodeBlocks(input);
|
|
593
|
+
return blocks.flatMap((block, index) => {
|
|
594
|
+
const language = block.language || null;
|
|
595
|
+
const content = block.code.trim();
|
|
596
|
+
if (!content) {
|
|
597
|
+
return [];
|
|
598
|
+
}
|
|
599
|
+
if (!looksLikeJsonEnvelope(content, acceptArrays)) {
|
|
600
|
+
return [];
|
|
601
|
+
}
|
|
602
|
+
const langBonus = languageBonus(language);
|
|
603
|
+
return [
|
|
604
|
+
{
|
|
605
|
+
id: `fenced:${index}`,
|
|
606
|
+
source: "fenced",
|
|
607
|
+
language,
|
|
608
|
+
content,
|
|
609
|
+
start: block.start,
|
|
610
|
+
end: block.end,
|
|
611
|
+
score: 260 + langBonus + lengthScore(content.length)
|
|
612
|
+
}
|
|
613
|
+
];
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
function scanBalancedSegments(input, acceptArrays) {
|
|
617
|
+
const results = [];
|
|
618
|
+
const stack = [];
|
|
619
|
+
let inString = false;
|
|
620
|
+
let quote = null;
|
|
621
|
+
let escaped = false;
|
|
622
|
+
for (let i = 0;i < input.length; i += 1) {
|
|
623
|
+
const char = input[i];
|
|
624
|
+
if (!char) {
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (inString) {
|
|
628
|
+
if (escaped) {
|
|
629
|
+
escaped = false;
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
if (char === "\\") {
|
|
633
|
+
escaped = true;
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (char === quote) {
|
|
637
|
+
inString = false;
|
|
638
|
+
quote = null;
|
|
639
|
+
}
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
const allowSingleQuoted = stack.length > 0;
|
|
643
|
+
if (char === '"' || allowSingleQuoted && (char === "'" || char === "`")) {
|
|
644
|
+
inString = true;
|
|
645
|
+
quote = char;
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (char === "{" || char === "[") {
|
|
649
|
+
stack.push({ char, index: i });
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
if (char !== "}" && char !== "]") {
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
const expectedOpen = char === "}" ? "{" : "[";
|
|
656
|
+
while (stack.length > 0 && stack[stack.length - 1]?.char !== expectedOpen) {
|
|
657
|
+
stack.pop();
|
|
658
|
+
}
|
|
659
|
+
const opened = stack.pop();
|
|
660
|
+
if (!opened) {
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (stack.length > 0) {
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
if (!acceptArrays && opened.char === "[") {
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
const content = input.slice(opened.index, i + 1).trim();
|
|
670
|
+
if (!content) {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const rootBonus = opened.char === "{" ? 40 : 20;
|
|
674
|
+
const boundaryBonus = boundaryScore(input, opened.index, i + 1);
|
|
675
|
+
results.push({
|
|
676
|
+
id: `scan:${results.length}`,
|
|
677
|
+
source: "scan",
|
|
678
|
+
content,
|
|
679
|
+
start: opened.index,
|
|
680
|
+
end: i + 1,
|
|
681
|
+
score: 120 + rootBonus + boundaryBonus + lengthScore(content.length)
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
return results;
|
|
685
|
+
}
|
|
684
686
|
// src/schema.ts
|
|
685
687
|
var RE_SIMPLE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
686
688
|
var RE_WHITESPACE = /\s+/g;
|
|
@@ -700,36 +702,36 @@ function unwrap(schema) {
|
|
|
700
702
|
let optional = false;
|
|
701
703
|
let nullable = false;
|
|
702
704
|
while (true) {
|
|
703
|
-
const typeName = current?.
|
|
705
|
+
const typeName = current?.def?.type;
|
|
704
706
|
if (!typeName) {
|
|
705
707
|
break;
|
|
706
708
|
}
|
|
707
709
|
if (typeName === "optional") {
|
|
708
710
|
optional = true;
|
|
709
|
-
current = current.
|
|
711
|
+
current = current.def?.innerType ?? current;
|
|
710
712
|
continue;
|
|
711
713
|
}
|
|
712
714
|
if (typeName === "default") {
|
|
713
715
|
optional = true;
|
|
714
|
-
current = current.
|
|
716
|
+
current = current.def?.innerType ?? current;
|
|
715
717
|
continue;
|
|
716
718
|
}
|
|
717
719
|
if (typeName === "nullable") {
|
|
718
720
|
nullable = true;
|
|
719
|
-
current = current.
|
|
721
|
+
current = current.def?.innerType ?? current;
|
|
720
722
|
continue;
|
|
721
723
|
}
|
|
722
724
|
if (typeName === "pipe") {
|
|
723
|
-
const outType = current.
|
|
725
|
+
const outType = current.def?.out?.def?.type;
|
|
724
726
|
if (outType === "transform") {
|
|
725
|
-
current = current.
|
|
727
|
+
current = current.def?.in ?? current;
|
|
726
728
|
} else {
|
|
727
|
-
current = current.
|
|
729
|
+
current = current.def?.out ?? current;
|
|
728
730
|
}
|
|
729
731
|
continue;
|
|
730
732
|
}
|
|
731
733
|
if (typeName === "catch" || typeName === "readonly") {
|
|
732
|
-
current = current.
|
|
734
|
+
current = current.def?.innerType ?? current;
|
|
733
735
|
continue;
|
|
734
736
|
}
|
|
735
737
|
break;
|
|
@@ -745,7 +747,7 @@ function formatCore(schema, depth, seen) {
|
|
|
745
747
|
return "unknown";
|
|
746
748
|
}
|
|
747
749
|
seen.add(schema);
|
|
748
|
-
const typeName = schema?.
|
|
750
|
+
const typeName = schema?.def?.type;
|
|
749
751
|
switch (typeName) {
|
|
750
752
|
case "string":
|
|
751
753
|
return "string";
|
|
@@ -770,44 +772,44 @@ function formatCore(schema, depth, seen) {
|
|
|
770
772
|
case "void":
|
|
771
773
|
return "void";
|
|
772
774
|
case "literal": {
|
|
773
|
-
const value = schema.
|
|
775
|
+
const value = schema.def?.values?.[0];
|
|
774
776
|
return JSON.stringify(value);
|
|
775
777
|
}
|
|
776
778
|
case "enum": {
|
|
777
|
-
const entries = schema.
|
|
779
|
+
const entries = schema.def?.entries;
|
|
778
780
|
const values = Object.values(entries ?? {});
|
|
779
781
|
const unique = [...new Set(values.filter((v) => typeof v !== "string" || Number.isNaN(Number(v))))];
|
|
780
782
|
return unique.map((v) => JSON.stringify(v)).join(" | ") || "string";
|
|
781
783
|
}
|
|
782
784
|
case "array": {
|
|
783
|
-
const inner = formatType(schema.
|
|
785
|
+
const inner = formatType(schema.def?.element ?? schema, depth, seen);
|
|
784
786
|
return requiresParentheses(inner) ? `(${inner})[]` : `${inner}[]`;
|
|
785
787
|
}
|
|
786
788
|
case "tuple": {
|
|
787
|
-
const items = (schema.
|
|
789
|
+
const items = (schema.def?.items ?? []).map((item) => formatType(item, depth, seen));
|
|
788
790
|
return `[${items.join(", ")}]`;
|
|
789
791
|
}
|
|
790
792
|
case "union": {
|
|
791
|
-
const options = (schema.
|
|
793
|
+
const options = (schema.def?.options ?? []).map((option) => formatType(option, depth, seen));
|
|
792
794
|
return options.join(" | ") || "unknown";
|
|
793
795
|
}
|
|
794
796
|
case "intersection": {
|
|
795
|
-
const left = formatType(schema.
|
|
796
|
-
const right = formatType(schema.
|
|
797
|
+
const left = formatType(schema.def?.left ?? schema, depth, seen);
|
|
798
|
+
const right = formatType(schema.def?.right ?? schema, depth, seen);
|
|
797
799
|
return `${left} & ${right}`;
|
|
798
800
|
}
|
|
799
801
|
case "record": {
|
|
800
|
-
const keyType = formatType(schema.
|
|
801
|
-
const valueType = formatType(schema.
|
|
802
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
803
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
802
804
|
return `Record<${keyType}, ${valueType}>`;
|
|
803
805
|
}
|
|
804
806
|
case "map": {
|
|
805
|
-
const keyType = formatType(schema.
|
|
806
|
-
const valueType = formatType(schema.
|
|
807
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
808
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
807
809
|
return `Map<${keyType}, ${valueType}>`;
|
|
808
810
|
}
|
|
809
811
|
case "set": {
|
|
810
|
-
const valueType = formatType(schema.
|
|
812
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
811
813
|
return `Set<${valueType}>`;
|
|
812
814
|
}
|
|
813
815
|
case "object":
|
|
@@ -821,7 +823,7 @@ function formatCore(schema, depth, seen) {
|
|
|
821
823
|
function formatObject(schema, depth, seen) {
|
|
822
824
|
const indent = " ".repeat(depth);
|
|
823
825
|
const innerIndent = " ".repeat(depth + 1);
|
|
824
|
-
const rawShape = schema.
|
|
826
|
+
const rawShape = schema.def?.shape;
|
|
825
827
|
const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
|
|
826
828
|
const entries = Object.entries(shape);
|
|
827
829
|
if (entries.length === 0) {
|
|
@@ -847,27 +849,27 @@ function requiresParentheses(typeText) {
|
|
|
847
849
|
return typeText.includes(" | ") || typeText.includes(" & ");
|
|
848
850
|
}
|
|
849
851
|
function isIntegerNumber(schema) {
|
|
850
|
-
const checks = schema.
|
|
852
|
+
const checks = schema.def?.checks ?? [];
|
|
851
853
|
return checks.some((check) => check.isInt === true);
|
|
852
854
|
}
|
|
853
855
|
function readSchemaDescription(schema) {
|
|
854
856
|
let current = schema;
|
|
855
|
-
while (current?.
|
|
857
|
+
while (current?.def?.type) {
|
|
856
858
|
const desc = current.description;
|
|
857
859
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
858
860
|
return sanitizeDescription(desc);
|
|
859
861
|
}
|
|
860
|
-
const typeName = current.
|
|
862
|
+
const typeName = current.def.type;
|
|
861
863
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
862
|
-
current = current.
|
|
864
|
+
current = current.def.innerType ?? current;
|
|
863
865
|
continue;
|
|
864
866
|
}
|
|
865
867
|
if (typeName === "pipe") {
|
|
866
|
-
current = current.
|
|
868
|
+
current = current.def.in ?? current;
|
|
867
869
|
continue;
|
|
868
870
|
}
|
|
869
871
|
if (typeName === "catch" || typeName === "readonly") {
|
|
870
|
-
current = current.
|
|
872
|
+
current = current.def.innerType ?? current;
|
|
871
873
|
continue;
|
|
872
874
|
}
|
|
873
875
|
break;
|
|
@@ -1129,18 +1131,108 @@ function findSSEBoundary(buffer) {
|
|
|
1129
1131
|
return Math.max(crlfIndex, lfIndex);
|
|
1130
1132
|
}
|
|
1131
1133
|
|
|
1132
|
-
// src/providers/mcp-runtime.ts
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1134
|
+
// src/providers/mcp-runtime-debug.ts
|
|
1135
|
+
function formatToolExecutionDebugLine(execution) {
|
|
1136
|
+
const status = execution.error ? "error" : "ok";
|
|
1137
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1138
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1139
|
+
const duration = typeof execution.durationMs === "number" ? ` ${execution.durationMs}ms` : "";
|
|
1140
|
+
const base = `[tool:mcp:${status}] ${scope} ${toolRef}#${execution.callId}${duration}`;
|
|
1141
|
+
if (execution.error) {
|
|
1142
|
+
return `${base} -> ${execution.error}`;
|
|
1143
|
+
}
|
|
1144
|
+
return base;
|
|
1145
|
+
}
|
|
1146
|
+
function emitToolExecution(request, execution) {
|
|
1147
|
+
request.onToolExecution?.(execution);
|
|
1148
|
+
const debug = resolveToolDebugOptions(request.toolDebug);
|
|
1149
|
+
if (!debug.enabled) {
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
debug.logger(formatToolExecutionDebugLine(execution));
|
|
1153
|
+
if (debug.includeRequest) {
|
|
1154
|
+
debug.logger(formatToolExecutionRequestDebugLine(execution, debug));
|
|
1155
|
+
}
|
|
1156
|
+
if (debug.includeResult && (!execution.error || debug.includeResultOnError)) {
|
|
1157
|
+
debug.logger(formatToolExecutionResultDebugLine(execution, debug));
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function resolveToolDebugOptions(value) {
|
|
1161
|
+
if (value === true) {
|
|
1162
|
+
return {
|
|
1163
|
+
enabled: true,
|
|
1164
|
+
logger: defaultToolDebugLogger,
|
|
1165
|
+
includeRequest: true,
|
|
1166
|
+
includeResult: true,
|
|
1167
|
+
includeResultOnError: true,
|
|
1168
|
+
pretty: false
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
if (value === undefined || value === false) {
|
|
1172
|
+
return {
|
|
1173
|
+
enabled: false,
|
|
1174
|
+
logger: () => {
|
|
1175
|
+
return;
|
|
1176
|
+
},
|
|
1177
|
+
includeRequest: false,
|
|
1178
|
+
includeResult: false,
|
|
1179
|
+
includeResultOnError: false,
|
|
1180
|
+
pretty: false
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
return {
|
|
1184
|
+
enabled: value.enabled ?? true,
|
|
1185
|
+
logger: value.logger ?? defaultToolDebugLogger,
|
|
1186
|
+
includeRequest: value.includeRequest ?? true,
|
|
1187
|
+
includeResult: value.includeResult ?? true,
|
|
1188
|
+
includeResultOnError: value.includeResultOnError ?? true,
|
|
1189
|
+
pretty: value.pretty ?? false
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
function defaultToolDebugLogger(line) {
|
|
1193
|
+
const { log } = globalThis.console;
|
|
1194
|
+
log(line);
|
|
1195
|
+
}
|
|
1196
|
+
function formatToolExecutionRequestDebugLine(execution, debug) {
|
|
1197
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1198
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1199
|
+
const payload = formatDebugPayload(execution.arguments, debug.pretty);
|
|
1200
|
+
return `[tool:mcp:request] ${scope} ${toolRef}#${execution.callId} arguments=${payload}`;
|
|
1201
|
+
}
|
|
1202
|
+
function formatToolExecutionResultDebugLine(execution, debug) {
|
|
1203
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1204
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1205
|
+
if (execution.error) {
|
|
1206
|
+
const payload2 = formatDebugPayload({ error: execution.error }, debug.pretty);
|
|
1207
|
+
return `[tool:mcp:result:error] ${scope} ${toolRef}#${execution.callId} output=${payload2}`;
|
|
1208
|
+
}
|
|
1209
|
+
const payload = formatDebugPayload(execution.output, debug.pretty);
|
|
1210
|
+
return `[tool:mcp:result:ok] ${scope} ${toolRef}#${execution.callId} output=${payload}`;
|
|
1211
|
+
}
|
|
1212
|
+
function formatDebugPayload(value, pretty) {
|
|
1213
|
+
if (value === undefined) {
|
|
1214
|
+
return "undefined";
|
|
1215
|
+
}
|
|
1216
|
+
try {
|
|
1217
|
+
const serialized = JSON.stringify(value, null, pretty ? 2 : 0);
|
|
1218
|
+
return serialized ?? "undefined";
|
|
1219
|
+
} catch {
|
|
1220
|
+
return String(value);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// src/providers/mcp-runtime.ts
|
|
1225
|
+
var DEFAULT_MAX_TOOL_ROUNDS = 100;
|
|
1226
|
+
async function resolveMCPToolset(clients) {
|
|
1227
|
+
if (!Array.isArray(clients) || clients.length === 0) {
|
|
1228
|
+
return {
|
|
1229
|
+
tools: [],
|
|
1230
|
+
byName: new Map
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
const listed = [];
|
|
1234
|
+
for (const client of clients) {
|
|
1235
|
+
let cursor;
|
|
1144
1236
|
do {
|
|
1145
1237
|
const page = await client.listTools(cursor ? { cursor } : undefined);
|
|
1146
1238
|
for (const tool of page.tools ?? []) {
|
|
@@ -1321,90 +1413,6 @@ function stringifyToolOutput(value) {
|
|
|
1321
1413
|
}
|
|
1322
1414
|
return JSON.stringify(value ?? null);
|
|
1323
1415
|
}
|
|
1324
|
-
function formatToolExecutionDebugLine(execution) {
|
|
1325
|
-
const status = execution.error ? "error" : "ok";
|
|
1326
|
-
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1327
|
-
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1328
|
-
const duration = typeof execution.durationMs === "number" ? ` ${execution.durationMs}ms` : "";
|
|
1329
|
-
const base = `[tool:mcp:${status}] ${scope} ${toolRef}#${execution.callId}${duration}`;
|
|
1330
|
-
if (execution.error) {
|
|
1331
|
-
return `${base} -> ${execution.error}`;
|
|
1332
|
-
}
|
|
1333
|
-
return base;
|
|
1334
|
-
}
|
|
1335
|
-
function emitToolExecution(request, execution) {
|
|
1336
|
-
request.onToolExecution?.(execution);
|
|
1337
|
-
const debug = resolveToolDebugOptions(request.toolDebug);
|
|
1338
|
-
if (!debug.enabled) {
|
|
1339
|
-
return;
|
|
1340
|
-
}
|
|
1341
|
-
debug.logger(formatToolExecutionDebugLine(execution));
|
|
1342
|
-
if (debug.includeRequest) {
|
|
1343
|
-
debug.logger(formatToolExecutionRequestDebugLine(execution, debug));
|
|
1344
|
-
}
|
|
1345
|
-
if (debug.includeResult && (!execution.error || debug.includeResultOnError)) {
|
|
1346
|
-
debug.logger(formatToolExecutionResultDebugLine(execution, debug));
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
function resolveToolDebugOptions(value) {
|
|
1350
|
-
if (value === true) {
|
|
1351
|
-
return {
|
|
1352
|
-
enabled: true,
|
|
1353
|
-
logger: (line) => console.log(line),
|
|
1354
|
-
includeRequest: true,
|
|
1355
|
-
includeResult: true,
|
|
1356
|
-
includeResultOnError: true,
|
|
1357
|
-
pretty: false
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
if (value === undefined || value === false) {
|
|
1361
|
-
return {
|
|
1362
|
-
enabled: false,
|
|
1363
|
-
logger: () => {
|
|
1364
|
-
return;
|
|
1365
|
-
},
|
|
1366
|
-
includeRequest: false,
|
|
1367
|
-
includeResult: false,
|
|
1368
|
-
includeResultOnError: false,
|
|
1369
|
-
pretty: false
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
return {
|
|
1373
|
-
enabled: value.enabled ?? true,
|
|
1374
|
-
logger: value.logger ?? ((line) => console.log(line)),
|
|
1375
|
-
includeRequest: value.includeRequest ?? true,
|
|
1376
|
-
includeResult: value.includeResult ?? true,
|
|
1377
|
-
includeResultOnError: value.includeResultOnError ?? true,
|
|
1378
|
-
pretty: value.pretty ?? false
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1381
|
-
function formatToolExecutionRequestDebugLine(execution, debug) {
|
|
1382
|
-
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1383
|
-
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1384
|
-
const payload = formatDebugPayload(execution.arguments, debug.pretty);
|
|
1385
|
-
return `[tool:mcp:request] ${scope} ${toolRef}#${execution.callId} arguments=${payload}`;
|
|
1386
|
-
}
|
|
1387
|
-
function formatToolExecutionResultDebugLine(execution, debug) {
|
|
1388
|
-
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1389
|
-
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1390
|
-
if (execution.error) {
|
|
1391
|
-
const payload2 = formatDebugPayload({ error: execution.error }, debug.pretty);
|
|
1392
|
-
return `[tool:mcp:result:error] ${scope} ${toolRef}#${execution.callId} output=${payload2}`;
|
|
1393
|
-
}
|
|
1394
|
-
const payload = formatDebugPayload(execution.output, debug.pretty);
|
|
1395
|
-
return `[tool:mcp:result:ok] ${scope} ${toolRef}#${execution.callId} output=${payload}`;
|
|
1396
|
-
}
|
|
1397
|
-
function formatDebugPayload(value, pretty) {
|
|
1398
|
-
if (value === undefined) {
|
|
1399
|
-
return "undefined";
|
|
1400
|
-
}
|
|
1401
|
-
try {
|
|
1402
|
-
const serialized = JSON.stringify(value, null, pretty ? 2 : 0);
|
|
1403
|
-
return serialized ?? "undefined";
|
|
1404
|
-
} catch {
|
|
1405
|
-
return String(value);
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
1416
|
function countNameCollisions(names) {
|
|
1409
1417
|
const out = new Map;
|
|
1410
1418
|
for (const name of names) {
|
|
@@ -1601,103 +1609,89 @@ function createOpenAICompatibleAdapter(options) {
|
|
|
1601
1609
|
if (usesMCP) {
|
|
1602
1610
|
return streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks);
|
|
1603
1611
|
}
|
|
1604
|
-
|
|
1605
|
-
method: "POST",
|
|
1606
|
-
headers: buildHeaders(options),
|
|
1607
|
-
body: JSON.stringify(cleanUndefined({
|
|
1608
|
-
...options.defaultBody,
|
|
1609
|
-
...request.body,
|
|
1610
|
-
model: options.model,
|
|
1611
|
-
messages: buildMessages(request),
|
|
1612
|
-
temperature: request.temperature,
|
|
1613
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1614
|
-
max_tokens: request.maxTokens,
|
|
1615
|
-
stream: true
|
|
1616
|
-
})),
|
|
1617
|
-
signal: request.signal
|
|
1618
|
-
});
|
|
1619
|
-
if (!response.ok) {
|
|
1620
|
-
const message = await response.text();
|
|
1621
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1622
|
-
}
|
|
1623
|
-
callbacks.onStart?.();
|
|
1624
|
-
let text = "";
|
|
1625
|
-
let reasoning = "";
|
|
1626
|
-
let usage;
|
|
1627
|
-
let finishReason;
|
|
1628
|
-
await consumeSSE(response, (data) => {
|
|
1629
|
-
if (data === "[DONE]") {
|
|
1630
|
-
return;
|
|
1631
|
-
}
|
|
1632
|
-
const json = safeJSONParse(data);
|
|
1633
|
-
if (!isRecord2(json)) {
|
|
1634
|
-
return;
|
|
1635
|
-
}
|
|
1636
|
-
const delta = pickAssistantDelta(json);
|
|
1637
|
-
const reasoningDelta = pickAssistantReasoningDelta(json);
|
|
1638
|
-
const chunkUsage = pickUsage(json);
|
|
1639
|
-
const chunkFinishReason = pickFinishReason(json);
|
|
1640
|
-
usage = preferLatestUsage(usage, chunkUsage);
|
|
1641
|
-
if (chunkFinishReason) {
|
|
1642
|
-
finishReason = chunkFinishReason;
|
|
1643
|
-
}
|
|
1644
|
-
if (delta) {
|
|
1645
|
-
text += delta;
|
|
1646
|
-
callbacks.onToken?.(delta);
|
|
1647
|
-
}
|
|
1648
|
-
if (reasoningDelta) {
|
|
1649
|
-
reasoning += reasoningDelta;
|
|
1650
|
-
}
|
|
1651
|
-
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
1652
|
-
const chunk = {
|
|
1653
|
-
textDelta: delta,
|
|
1654
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
1655
|
-
raw: json,
|
|
1656
|
-
usage: chunkUsage,
|
|
1657
|
-
finishReason: chunkFinishReason
|
|
1658
|
-
};
|
|
1659
|
-
callbacks.onChunk?.(chunk);
|
|
1660
|
-
}
|
|
1661
|
-
});
|
|
1662
|
-
const out = {
|
|
1663
|
-
text,
|
|
1664
|
-
reasoning: reasoning.length > 0 ? reasoning : undefined,
|
|
1665
|
-
usage,
|
|
1666
|
-
finishReason
|
|
1667
|
-
};
|
|
1668
|
-
callbacks.onComplete?.(out);
|
|
1669
|
-
return out;
|
|
1612
|
+
return streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks);
|
|
1670
1613
|
},
|
|
1671
1614
|
async embed(request) {
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1615
|
+
return embedOpenAI(options, fetcher, embeddingPath, request);
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
async function streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks) {
|
|
1620
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1621
|
+
messages: buildMessages(request),
|
|
1622
|
+
stream: true
|
|
1623
|
+
}));
|
|
1624
|
+
if (!response.ok) {
|
|
1625
|
+
const message = await response.text();
|
|
1626
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1627
|
+
}
|
|
1628
|
+
callbacks.onStart?.();
|
|
1629
|
+
let text = "";
|
|
1630
|
+
let reasoning = "";
|
|
1631
|
+
let usage;
|
|
1632
|
+
let finishReason;
|
|
1633
|
+
await consumeSSE(response, (data) => {
|
|
1634
|
+
if (data === "[DONE]") {
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
const json = safeJSONParse(data);
|
|
1638
|
+
if (!isRecord2(json)) {
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
const delta = pickAssistantDelta(json);
|
|
1642
|
+
const reasoningDelta = pickAssistantReasoningDelta(json);
|
|
1643
|
+
const chunkUsage = pickUsage(json);
|
|
1644
|
+
const chunkFinishReason = pickFinishReason(json);
|
|
1645
|
+
usage = preferLatestUsage(usage, chunkUsage);
|
|
1646
|
+
if (chunkFinishReason) {
|
|
1647
|
+
finishReason = chunkFinishReason;
|
|
1648
|
+
}
|
|
1649
|
+
if (delta) {
|
|
1650
|
+
text += delta;
|
|
1651
|
+
callbacks.onToken?.(delta);
|
|
1700
1652
|
}
|
|
1653
|
+
if (reasoningDelta) {
|
|
1654
|
+
reasoning += reasoningDelta;
|
|
1655
|
+
}
|
|
1656
|
+
emitOpenAIStreamChunk(callbacks, undefined, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
1657
|
+
});
|
|
1658
|
+
const out = {
|
|
1659
|
+
text,
|
|
1660
|
+
reasoning: reasoning.length > 0 ? reasoning : undefined,
|
|
1661
|
+
usage,
|
|
1662
|
+
finishReason
|
|
1663
|
+
};
|
|
1664
|
+
callbacks.onComplete?.(out);
|
|
1665
|
+
return out;
|
|
1666
|
+
}
|
|
1667
|
+
async function embedOpenAI(options, fetcher, path, request) {
|
|
1668
|
+
const body = cleanUndefined({
|
|
1669
|
+
...options.defaultBody,
|
|
1670
|
+
...request.body,
|
|
1671
|
+
model: request.model ?? options.model,
|
|
1672
|
+
input: request.input,
|
|
1673
|
+
dimensions: request.dimensions,
|
|
1674
|
+
encoding_format: "float"
|
|
1675
|
+
});
|
|
1676
|
+
const response = await fetcher(buildURL(options.baseURL, path), {
|
|
1677
|
+
method: "POST",
|
|
1678
|
+
headers: buildHeaders(options),
|
|
1679
|
+
body: JSON.stringify(body)
|
|
1680
|
+
});
|
|
1681
|
+
if (!response.ok) {
|
|
1682
|
+
const message = await response.text();
|
|
1683
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1684
|
+
}
|
|
1685
|
+
const json = await response.json();
|
|
1686
|
+
const data = json.data;
|
|
1687
|
+
if (!Array.isArray(data)) {
|
|
1688
|
+
throw new Error("Unexpected embedding response: missing data array");
|
|
1689
|
+
}
|
|
1690
|
+
return {
|
|
1691
|
+
embeddings: data.map((d) => isRecord2(d) && Array.isArray(d.embedding) ? d.embedding : []),
|
|
1692
|
+
model: pickString(json.model) ?? body.model,
|
|
1693
|
+
usage: pickUsage(json),
|
|
1694
|
+
raw: json
|
|
1701
1695
|
};
|
|
1702
1696
|
}
|
|
1703
1697
|
async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath, request) {
|
|
@@ -1714,22 +1708,80 @@ async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath,
|
|
|
1714
1708
|
}
|
|
1715
1709
|
return completeWithChatCompletionsPassThrough(options, fetcher, chatPath, request);
|
|
1716
1710
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1711
|
+
function buildChatCompletionsBody(options, request, overrides) {
|
|
1712
|
+
return buildOpenAIRequestBody(options, request, "max_tokens", overrides);
|
|
1713
|
+
}
|
|
1714
|
+
function buildResponsesBody(options, request, overrides) {
|
|
1715
|
+
return buildOpenAIRequestBody(options, request, "max_output_tokens", overrides);
|
|
1716
|
+
}
|
|
1717
|
+
function buildOpenAIRequestBody(options, request, maxTokenKey, overrides) {
|
|
1718
|
+
return cleanUndefined({
|
|
1719
|
+
...options.defaultBody,
|
|
1720
|
+
...request.body,
|
|
1721
|
+
model: options.model,
|
|
1722
|
+
temperature: request.temperature,
|
|
1723
|
+
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1724
|
+
[maxTokenKey]: request.maxTokens,
|
|
1725
|
+
...overrides
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
function sendOpenAIRequest(options, fetcher, path, request, body) {
|
|
1729
|
+
return fetcher(buildURL(options.baseURL, path), {
|
|
1719
1730
|
method: "POST",
|
|
1720
1731
|
headers: buildHeaders(options),
|
|
1721
|
-
body: JSON.stringify(
|
|
1722
|
-
...options.defaultBody,
|
|
1723
|
-
...request.body,
|
|
1724
|
-
model: options.model,
|
|
1725
|
-
messages: buildMessages(request),
|
|
1726
|
-
temperature: request.temperature,
|
|
1727
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1728
|
-
max_tokens: request.maxTokens,
|
|
1729
|
-
stream: false
|
|
1730
|
-
})),
|
|
1732
|
+
body: JSON.stringify(body),
|
|
1731
1733
|
signal: request.signal
|
|
1732
1734
|
});
|
|
1735
|
+
}
|
|
1736
|
+
async function sendOpenAIJsonRequest(options, fetcher, path, request, body) {
|
|
1737
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, body);
|
|
1738
|
+
if (!response.ok) {
|
|
1739
|
+
const message = await response.text();
|
|
1740
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1741
|
+
}
|
|
1742
|
+
return await response.json();
|
|
1743
|
+
}
|
|
1744
|
+
function createResponsesMCPState(request) {
|
|
1745
|
+
return {
|
|
1746
|
+
input: buildResponsesInput(request),
|
|
1747
|
+
previousResponseId: pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined),
|
|
1748
|
+
aggregatedUsage: undefined,
|
|
1749
|
+
finishReason: undefined,
|
|
1750
|
+
lastPayload: undefined,
|
|
1751
|
+
executedToolCalls: [],
|
|
1752
|
+
toolExecutions: [],
|
|
1753
|
+
reasoningBlocks: []
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
function buildResponsesMCPResult(state, text, raw) {
|
|
1757
|
+
return {
|
|
1758
|
+
text,
|
|
1759
|
+
reasoning: joinReasoningBlocks(state.reasoningBlocks) || undefined,
|
|
1760
|
+
reasoningBlocks: state.reasoningBlocks.length > 0 ? state.reasoningBlocks : undefined,
|
|
1761
|
+
raw,
|
|
1762
|
+
usage: state.aggregatedUsage,
|
|
1763
|
+
finishReason: state.finishReason,
|
|
1764
|
+
toolCalls: state.executedToolCalls.length > 0 ? state.executedToolCalls : undefined,
|
|
1765
|
+
toolExecutions: state.toolExecutions.length > 0 ? state.toolExecutions : undefined
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
function emitOpenAIStreamChunk(callbacks, round, raw, delta, reasoningDelta, usage, finishReason) {
|
|
1769
|
+
if (delta || reasoningDelta || usage || finishReason) {
|
|
1770
|
+
callbacks.onChunk?.({
|
|
1771
|
+
textDelta: delta,
|
|
1772
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
1773
|
+
...round !== undefined ? { turnIndex: round } : {},
|
|
1774
|
+
raw,
|
|
1775
|
+
usage,
|
|
1776
|
+
finishReason
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
async function completeWithChatCompletionsPassThrough(options, fetcher, path, request) {
|
|
1781
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1782
|
+
messages: buildMessages(request),
|
|
1783
|
+
stream: false
|
|
1784
|
+
}));
|
|
1733
1785
|
if (!response.ok) {
|
|
1734
1786
|
const message = await response.text();
|
|
1735
1787
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -1777,44 +1829,32 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1777
1829
|
let lastPayload;
|
|
1778
1830
|
const toolCalls = [];
|
|
1779
1831
|
const toolExecutions = [];
|
|
1832
|
+
const reasoningBlocks = [];
|
|
1780
1833
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1781
1834
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1782
1835
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
1783
|
-
const
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
model: options.model,
|
|
1790
|
-
messages,
|
|
1791
|
-
temperature: request.temperature,
|
|
1792
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1793
|
-
max_tokens: request.maxTokens,
|
|
1794
|
-
tools: transportTools,
|
|
1795
|
-
tool_choice: request.toolChoice,
|
|
1796
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
1797
|
-
})),
|
|
1798
|
-
signal: request.signal
|
|
1799
|
-
});
|
|
1800
|
-
if (!response.ok) {
|
|
1801
|
-
const message = await response.text();
|
|
1802
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1803
|
-
}
|
|
1804
|
-
const payload = await response.json();
|
|
1836
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1837
|
+
messages,
|
|
1838
|
+
tools: transportTools,
|
|
1839
|
+
tool_choice: request.toolChoice,
|
|
1840
|
+
parallel_tool_calls: request.parallelToolCalls
|
|
1841
|
+
}));
|
|
1805
1842
|
lastPayload = payload;
|
|
1806
1843
|
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1807
1844
|
finishReason = pickFinishReason(payload);
|
|
1808
1845
|
const assistantMessage = pickAssistantMessage(payload);
|
|
1809
1846
|
const calledTools = pickChatToolCalls(payload);
|
|
1847
|
+
const roundReasoning = pickAssistantReasoning(payload);
|
|
1848
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
1810
1849
|
if (!assistantMessage) {
|
|
1811
1850
|
throw new Error("No assistant message in OpenAI-compatible response.");
|
|
1812
1851
|
}
|
|
1813
1852
|
if (calledTools.length === 0) {
|
|
1814
|
-
const reasoning =
|
|
1853
|
+
const reasoning = joinReasoningBlocks(reasoningBlocks) || undefined;
|
|
1815
1854
|
return {
|
|
1816
1855
|
text: pickAssistantText(payload),
|
|
1817
|
-
reasoning
|
|
1856
|
+
reasoning,
|
|
1857
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1818
1858
|
raw: payload,
|
|
1819
1859
|
usage: aggregatedUsage,
|
|
1820
1860
|
finishReason,
|
|
@@ -1842,10 +1882,8 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1842
1882
|
}
|
|
1843
1883
|
return {
|
|
1844
1884
|
text: pickAssistantText(lastPayload ?? {}),
|
|
1845
|
-
reasoning: (
|
|
1846
|
-
|
|
1847
|
-
return value.length > 0 ? value : undefined;
|
|
1848
|
-
})(),
|
|
1885
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1886
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1849
1887
|
raw: lastPayload,
|
|
1850
1888
|
usage: aggregatedUsage,
|
|
1851
1889
|
finishReason,
|
|
@@ -1855,26 +1893,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1855
1893
|
}
|
|
1856
1894
|
async function completeWithResponsesAPIPassThrough(options, fetcher, path, request) {
|
|
1857
1895
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
1858
|
-
const
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
...options.defaultBody,
|
|
1863
|
-
...request.body,
|
|
1864
|
-
model: options.model,
|
|
1865
|
-
input: buildResponsesInput(request),
|
|
1866
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
1867
|
-
temperature: request.temperature,
|
|
1868
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1869
|
-
max_output_tokens: request.maxTokens
|
|
1870
|
-
})),
|
|
1871
|
-
signal: request.signal
|
|
1872
|
-
});
|
|
1873
|
-
if (!response.ok) {
|
|
1874
|
-
const message = await response.text();
|
|
1875
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1876
|
-
}
|
|
1877
|
-
const payload = await response.json();
|
|
1896
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
1897
|
+
input: buildResponsesInput(request),
|
|
1898
|
+
previous_response_id: pickString(body?.previous_response_id)
|
|
1899
|
+
}));
|
|
1878
1900
|
const toolCalls = pickResponsesToolCalls(payload);
|
|
1879
1901
|
return {
|
|
1880
1902
|
text: pickResponsesText(payload) || pickAssistantText(payload),
|
|
@@ -1886,54 +1908,26 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
|
|
|
1886
1908
|
}
|
|
1887
1909
|
async function completeWithResponsesAPIWithMCP(options, fetcher, path, request) {
|
|
1888
1910
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
1889
|
-
|
|
1890
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
1891
|
-
let aggregatedUsage;
|
|
1892
|
-
let finishReason;
|
|
1893
|
-
let lastPayload;
|
|
1894
|
-
const executedToolCalls = [];
|
|
1895
|
-
const toolExecutions = [];
|
|
1911
|
+
const state = createResponsesMCPState(request);
|
|
1896
1912
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1897
1913
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1898
1914
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
1899
|
-
const
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
max_output_tokens: request.maxTokens,
|
|
1911
|
-
tools: transportTools,
|
|
1912
|
-
tool_choice: request.toolChoice,
|
|
1913
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
1914
|
-
})),
|
|
1915
|
-
signal: request.signal
|
|
1916
|
-
});
|
|
1917
|
-
if (!response.ok) {
|
|
1918
|
-
const message = await response.text();
|
|
1919
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1920
|
-
}
|
|
1921
|
-
const payload = await response.json();
|
|
1922
|
-
lastPayload = payload;
|
|
1923
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1924
|
-
finishReason = pickResponsesFinishReason(payload) ?? finishReason;
|
|
1915
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
1916
|
+
input: state.input,
|
|
1917
|
+
previous_response_id: state.previousResponseId,
|
|
1918
|
+
tools: transportTools,
|
|
1919
|
+
tool_choice: request.toolChoice,
|
|
1920
|
+
parallel_tool_calls: request.parallelToolCalls
|
|
1921
|
+
}));
|
|
1922
|
+
state.lastPayload = payload;
|
|
1923
|
+
state.aggregatedUsage = mergeUsage(state.aggregatedUsage, pickUsage(payload));
|
|
1924
|
+
state.finishReason = pickResponsesFinishReason(payload) ?? state.finishReason;
|
|
1925
|
+
pushReasoningBlock(state.reasoningBlocks, round, pickResponsesReasoning(payload));
|
|
1925
1926
|
const providerToolCalls = pickResponsesToolCalls(payload);
|
|
1926
1927
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
1927
1928
|
if (functionCalls.length === 0) {
|
|
1928
1929
|
const text = pickResponsesText(payload) || pickAssistantText(payload);
|
|
1929
|
-
return
|
|
1930
|
-
text,
|
|
1931
|
-
raw: payload,
|
|
1932
|
-
usage: aggregatedUsage,
|
|
1933
|
-
finishReason,
|
|
1934
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
1935
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
1936
|
-
};
|
|
1930
|
+
return buildResponsesMCPResult(state, text, payload);
|
|
1937
1931
|
}
|
|
1938
1932
|
if (round > maxToolRounds) {
|
|
1939
1933
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
@@ -1944,23 +1938,16 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1944
1938
|
provider: "openai-compatible",
|
|
1945
1939
|
model: options.model
|
|
1946
1940
|
});
|
|
1947
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
1948
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
1949
|
-
input = outputs.map((entry) => ({
|
|
1941
|
+
state.executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
1942
|
+
state.toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
1943
|
+
state.input = outputs.map((entry) => ({
|
|
1950
1944
|
type: "function_call_output",
|
|
1951
1945
|
call_id: entry.call.id,
|
|
1952
1946
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
1953
1947
|
}));
|
|
1954
|
-
previousResponseId = pickString(payload.id);
|
|
1948
|
+
state.previousResponseId = pickString(payload.id);
|
|
1955
1949
|
}
|
|
1956
|
-
return {
|
|
1957
|
-
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
1958
|
-
raw: lastPayload,
|
|
1959
|
-
usage: aggregatedUsage,
|
|
1960
|
-
finishReason,
|
|
1961
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
1962
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
1963
|
-
};
|
|
1950
|
+
return buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
1964
1951
|
}
|
|
1965
1952
|
async function streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks) {
|
|
1966
1953
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
@@ -1970,30 +1957,19 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
1970
1957
|
let lastPayload;
|
|
1971
1958
|
const executedToolCalls = [];
|
|
1972
1959
|
const toolExecutions = [];
|
|
1960
|
+
const reasoningBlocks = [];
|
|
1973
1961
|
callbacks.onStart?.();
|
|
1974
1962
|
let lastRoundText = "";
|
|
1975
|
-
let lastRoundReasoning = "";
|
|
1976
1963
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1977
1964
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1978
1965
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
1979
|
-
const response = await
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
messages,
|
|
1987
|
-
temperature: request.temperature,
|
|
1988
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1989
|
-
max_tokens: request.maxTokens,
|
|
1990
|
-
tools: transportTools,
|
|
1991
|
-
tool_choice: request.toolChoice,
|
|
1992
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
1993
|
-
stream: true
|
|
1994
|
-
})),
|
|
1995
|
-
signal: request.signal
|
|
1996
|
-
});
|
|
1966
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1967
|
+
messages,
|
|
1968
|
+
tools: transportTools,
|
|
1969
|
+
tool_choice: request.toolChoice,
|
|
1970
|
+
parallel_tool_calls: request.parallelToolCalls,
|
|
1971
|
+
stream: true
|
|
1972
|
+
}));
|
|
1997
1973
|
if (!response.ok) {
|
|
1998
1974
|
const message = await response.text();
|
|
1999
1975
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2030,38 +2006,48 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2030
2006
|
roundReasoning += reasoningDelta;
|
|
2031
2007
|
reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
|
|
2032
2008
|
}
|
|
2033
|
-
|
|
2034
|
-
const chunk = {
|
|
2035
|
-
textDelta: delta,
|
|
2036
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
2037
|
-
raw: json,
|
|
2038
|
-
usage: chunkUsage,
|
|
2039
|
-
finishReason: chunkFinishReason
|
|
2040
|
-
};
|
|
2041
|
-
callbacks.onChunk?.(chunk);
|
|
2042
|
-
}
|
|
2009
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2043
2010
|
});
|
|
2044
2011
|
aggregatedUsage = mergeUsage(aggregatedUsage, roundUsage);
|
|
2045
2012
|
if (roundFinishReason) {
|
|
2046
2013
|
finishReason = roundFinishReason;
|
|
2047
2014
|
}
|
|
2048
2015
|
const calledTools = buildOpenAIStreamToolCalls(streamedToolCalls);
|
|
2016
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2017
|
+
request.onTurnTransition?.({
|
|
2018
|
+
turnIndex: round,
|
|
2019
|
+
kind: "reasoningComplete",
|
|
2020
|
+
reasoningText: roundReasoning
|
|
2021
|
+
});
|
|
2049
2022
|
if (calledTools.length === 0) {
|
|
2050
2023
|
const out2 = {
|
|
2051
2024
|
text: roundText,
|
|
2052
|
-
reasoning:
|
|
2025
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2026
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2053
2027
|
raw: lastPayload,
|
|
2054
2028
|
usage: aggregatedUsage,
|
|
2055
2029
|
finishReason,
|
|
2056
2030
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2057
2031
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2058
2032
|
};
|
|
2033
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2059
2034
|
callbacks.onComplete?.(out2);
|
|
2060
2035
|
return out2;
|
|
2061
2036
|
}
|
|
2062
2037
|
if (round > maxToolRounds) {
|
|
2063
2038
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2064
2039
|
}
|
|
2040
|
+
request.onTurnTransition?.({
|
|
2041
|
+
turnIndex: round,
|
|
2042
|
+
kind: "toolCallsEmit",
|
|
2043
|
+
toolCalls: calledTools
|
|
2044
|
+
});
|
|
2045
|
+
callbacks.onChunk?.({
|
|
2046
|
+
textDelta: "",
|
|
2047
|
+
turnIndex: round,
|
|
2048
|
+
toolCalls: calledTools,
|
|
2049
|
+
finishReason: roundFinishReason
|
|
2050
|
+
});
|
|
2065
2051
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
2066
2052
|
round,
|
|
2067
2053
|
request,
|
|
@@ -2070,8 +2056,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2070
2056
|
});
|
|
2071
2057
|
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2072
2058
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2059
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2073
2060
|
lastRoundText = roundText;
|
|
2074
|
-
lastRoundReasoning = roundReasoning;
|
|
2075
2061
|
const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
|
|
2076
2062
|
reasoning: roundReasoning,
|
|
2077
2063
|
reasoningFieldName
|
|
@@ -2085,34 +2071,25 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2085
2071
|
}
|
|
2086
2072
|
const out = {
|
|
2087
2073
|
text: lastRoundText,
|
|
2088
|
-
reasoning:
|
|
2074
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2075
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2089
2076
|
raw: lastPayload,
|
|
2090
2077
|
usage: aggregatedUsage,
|
|
2091
2078
|
finishReason,
|
|
2092
2079
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2093
2080
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2094
2081
|
};
|
|
2082
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2095
2083
|
callbacks.onComplete?.(out);
|
|
2096
2084
|
return out;
|
|
2097
2085
|
}
|
|
2098
2086
|
async function streamWithResponsesAPIPassThrough(options, fetcher, path, request, callbacks) {
|
|
2099
2087
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
2100
|
-
const response = await
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
...request.body,
|
|
2106
|
-
model: options.model,
|
|
2107
|
-
input: buildResponsesInput(request),
|
|
2108
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
2109
|
-
temperature: request.temperature,
|
|
2110
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2111
|
-
max_output_tokens: request.maxTokens,
|
|
2112
|
-
stream: true
|
|
2113
|
-
})),
|
|
2114
|
-
signal: request.signal
|
|
2115
|
-
});
|
|
2088
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2089
|
+
input: buildResponsesInput(request),
|
|
2090
|
+
previous_response_id: pickString(body?.previous_response_id),
|
|
2091
|
+
stream: true
|
|
2092
|
+
}));
|
|
2116
2093
|
if (!response.ok) {
|
|
2117
2094
|
const message = await response.text();
|
|
2118
2095
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2145,15 +2122,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2145
2122
|
text += delta;
|
|
2146
2123
|
callbacks.onToken?.(delta);
|
|
2147
2124
|
}
|
|
2148
|
-
|
|
2149
|
-
const chunk = {
|
|
2150
|
-
textDelta: delta,
|
|
2151
|
-
raw: json,
|
|
2152
|
-
usage: chunkUsage,
|
|
2153
|
-
finishReason: chunkFinishReason
|
|
2154
|
-
};
|
|
2155
|
-
callbacks.onChunk?.(chunk);
|
|
2156
|
-
}
|
|
2125
|
+
emitOpenAIStreamChunk(callbacks, undefined, json, delta, "", chunkUsage, chunkFinishReason);
|
|
2157
2126
|
});
|
|
2158
2127
|
const finalPayload = lastPayload ?? {};
|
|
2159
2128
|
const out = {
|
|
@@ -2167,41 +2136,25 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2167
2136
|
}
|
|
2168
2137
|
async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, callbacks) {
|
|
2169
2138
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
2170
|
-
|
|
2171
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
2172
|
-
let aggregatedUsage;
|
|
2173
|
-
let finishReason;
|
|
2174
|
-
let lastPayload;
|
|
2175
|
-
const executedToolCalls = [];
|
|
2176
|
-
const toolExecutions = [];
|
|
2139
|
+
const state = createResponsesMCPState(request);
|
|
2177
2140
|
callbacks.onStart?.();
|
|
2178
2141
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2179
2142
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2180
2143
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
2181
|
-
const response = await
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
previous_response_id: previousResponseId,
|
|
2190
|
-
temperature: request.temperature,
|
|
2191
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2192
|
-
max_output_tokens: request.maxTokens,
|
|
2193
|
-
tools: transportTools,
|
|
2194
|
-
tool_choice: request.toolChoice,
|
|
2195
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
2196
|
-
stream: true
|
|
2197
|
-
})),
|
|
2198
|
-
signal: request.signal
|
|
2199
|
-
});
|
|
2144
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2145
|
+
input: state.input,
|
|
2146
|
+
previous_response_id: state.previousResponseId,
|
|
2147
|
+
tools: transportTools,
|
|
2148
|
+
tool_choice: request.toolChoice,
|
|
2149
|
+
parallel_tool_calls: request.parallelToolCalls,
|
|
2150
|
+
stream: true
|
|
2151
|
+
}));
|
|
2200
2152
|
if (!response.ok) {
|
|
2201
2153
|
const message = await response.text();
|
|
2202
2154
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2203
2155
|
}
|
|
2204
2156
|
let roundText = "";
|
|
2157
|
+
let roundReasoning = "";
|
|
2205
2158
|
let roundUsage;
|
|
2206
2159
|
let roundFinishReason;
|
|
2207
2160
|
let roundPayload;
|
|
@@ -2217,9 +2170,10 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2217
2170
|
const payload = pickResponsesStreamPayload(json);
|
|
2218
2171
|
if (payload) {
|
|
2219
2172
|
roundPayload = payload;
|
|
2220
|
-
lastPayload = payload;
|
|
2173
|
+
state.lastPayload = payload;
|
|
2221
2174
|
}
|
|
2222
2175
|
const delta = pickResponsesStreamTextDelta(json);
|
|
2176
|
+
const reasoningDelta = pickResponsesStreamReasoningDelta(json);
|
|
2223
2177
|
const chunkUsage = pickResponsesStreamUsage(json);
|
|
2224
2178
|
const chunkFinishReason = pickResponsesStreamFinishReason(json);
|
|
2225
2179
|
collectResponsesStreamToolCalls(json, streamedToolCalls);
|
|
@@ -2231,66 +2185,70 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2231
2185
|
roundText += delta;
|
|
2232
2186
|
callbacks.onToken?.(delta);
|
|
2233
2187
|
}
|
|
2234
|
-
if (
|
|
2235
|
-
|
|
2236
|
-
textDelta: delta,
|
|
2237
|
-
raw: json,
|
|
2238
|
-
usage: chunkUsage,
|
|
2239
|
-
finishReason: chunkFinishReason
|
|
2240
|
-
};
|
|
2241
|
-
callbacks.onChunk?.(chunk);
|
|
2188
|
+
if (reasoningDelta) {
|
|
2189
|
+
roundReasoning += reasoningDelta;
|
|
2242
2190
|
}
|
|
2191
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2243
2192
|
});
|
|
2244
2193
|
const resolvedRoundUsage = preferLatestUsage(roundUsage, roundPayload ? pickUsage(roundPayload) : undefined);
|
|
2245
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, resolvedRoundUsage);
|
|
2194
|
+
state.aggregatedUsage = mergeUsage(state.aggregatedUsage, resolvedRoundUsage);
|
|
2246
2195
|
if (roundFinishReason) {
|
|
2247
|
-
finishReason = roundFinishReason;
|
|
2196
|
+
state.finishReason = roundFinishReason;
|
|
2248
2197
|
} else if (roundPayload) {
|
|
2249
|
-
finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
|
|
2198
|
+
state.finishReason = pickResponsesFinishReason(roundPayload) ?? state.finishReason;
|
|
2250
2199
|
}
|
|
2251
2200
|
const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
|
|
2201
|
+
if (roundPayload && roundReasoning.length === 0) {
|
|
2202
|
+
roundReasoning = pickResponsesReasoning(roundPayload);
|
|
2203
|
+
}
|
|
2252
2204
|
const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
|
|
2253
2205
|
const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
|
|
2254
2206
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2207
|
+
pushReasoningBlock(state.reasoningBlocks, round, roundReasoning);
|
|
2208
|
+
request.onTurnTransition?.({
|
|
2209
|
+
turnIndex: round,
|
|
2210
|
+
kind: "reasoningComplete",
|
|
2211
|
+
reasoningText: roundReasoning
|
|
2212
|
+
});
|
|
2255
2213
|
if (functionCalls.length === 0) {
|
|
2256
2214
|
const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
|
|
2257
|
-
const out2 =
|
|
2258
|
-
|
|
2259
|
-
raw: roundPayload ?? lastPayload,
|
|
2260
|
-
usage: aggregatedUsage,
|
|
2261
|
-
finishReason,
|
|
2262
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2263
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2264
|
-
};
|
|
2215
|
+
const out2 = buildResponsesMCPResult(state, finalText, roundPayload ?? state.lastPayload);
|
|
2216
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2265
2217
|
callbacks.onComplete?.(out2);
|
|
2266
2218
|
return out2;
|
|
2267
2219
|
}
|
|
2268
2220
|
if (round > maxToolRounds) {
|
|
2269
2221
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2270
2222
|
}
|
|
2223
|
+
request.onTurnTransition?.({
|
|
2224
|
+
turnIndex: round,
|
|
2225
|
+
kind: "toolCallsEmit",
|
|
2226
|
+
toolCalls: functionCalls
|
|
2227
|
+
});
|
|
2228
|
+
callbacks.onChunk?.({
|
|
2229
|
+
textDelta: "",
|
|
2230
|
+
turnIndex: round,
|
|
2231
|
+
toolCalls: functionCalls,
|
|
2232
|
+
finishReason: roundFinishReason
|
|
2233
|
+
});
|
|
2271
2234
|
const outputs = await executeMCPToolCalls(functionCalls, mcpToolset, {
|
|
2272
2235
|
round,
|
|
2273
2236
|
request,
|
|
2274
2237
|
provider: "openai-compatible",
|
|
2275
2238
|
model: options.model
|
|
2276
2239
|
});
|
|
2277
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2278
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2279
|
-
|
|
2240
|
+
state.executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2241
|
+
state.toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2242
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2243
|
+
state.input = outputs.map((entry) => ({
|
|
2280
2244
|
type: "function_call_output",
|
|
2281
2245
|
call_id: entry.call.id,
|
|
2282
2246
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
2283
2247
|
}));
|
|
2284
|
-
previousResponseId = pickString(roundPayload?.id);
|
|
2248
|
+
state.previousResponseId = pickString(roundPayload?.id);
|
|
2285
2249
|
}
|
|
2286
|
-
const out = {
|
|
2287
|
-
|
|
2288
|
-
raw: lastPayload,
|
|
2289
|
-
usage: aggregatedUsage,
|
|
2290
|
-
finishReason,
|
|
2291
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2292
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2293
|
-
};
|
|
2250
|
+
const out = buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
2251
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2294
2252
|
callbacks.onComplete?.(out);
|
|
2295
2253
|
return out;
|
|
2296
2254
|
}
|
|
@@ -2442,7 +2400,7 @@ function pickAssistantDelta(payload) {
|
|
|
2442
2400
|
if (!isRecord2(delta)) {
|
|
2443
2401
|
return "";
|
|
2444
2402
|
}
|
|
2445
|
-
return
|
|
2403
|
+
return pickTextLike(delta.content);
|
|
2446
2404
|
}
|
|
2447
2405
|
function pickAssistantReasoning(payload) {
|
|
2448
2406
|
const message = pickAssistantMessage(payload);
|
|
@@ -2575,6 +2533,20 @@ function pickResponsesStreamTextDelta(payload) {
|
|
|
2575
2533
|
}
|
|
2576
2534
|
return "";
|
|
2577
2535
|
}
|
|
2536
|
+
function pickResponsesStreamReasoningDelta(payload) {
|
|
2537
|
+
const eventType = pickString(payload.type) ?? "";
|
|
2538
|
+
if (!eventType.includes("reasoning") && !eventType.includes("thinking")) {
|
|
2539
|
+
return "";
|
|
2540
|
+
}
|
|
2541
|
+
const direct = pickString(payload.delta);
|
|
2542
|
+
if (direct) {
|
|
2543
|
+
return direct;
|
|
2544
|
+
}
|
|
2545
|
+
if (isRecord2(payload.delta)) {
|
|
2546
|
+
return pickReasoningText(payload.delta) || pickString(payload.delta.text) || pickString(payload.delta.summary_text) || "";
|
|
2547
|
+
}
|
|
2548
|
+
return "";
|
|
2549
|
+
}
|
|
2578
2550
|
function pickResponsesStreamUsage(payload) {
|
|
2579
2551
|
const direct = pickUsage(payload);
|
|
2580
2552
|
if (direct) {
|
|
@@ -2709,10 +2681,34 @@ function pickResponsesText(payload) {
|
|
|
2709
2681
|
}).join("");
|
|
2710
2682
|
}).join("");
|
|
2711
2683
|
}
|
|
2684
|
+
function pickResponsesReasoning(payload) {
|
|
2685
|
+
const direct = pickReasoningText(payload);
|
|
2686
|
+
if (direct) {
|
|
2687
|
+
return direct;
|
|
2688
|
+
}
|
|
2689
|
+
const output = payload.output;
|
|
2690
|
+
if (!Array.isArray(output)) {
|
|
2691
|
+
return "";
|
|
2692
|
+
}
|
|
2693
|
+
return output.map((item) => {
|
|
2694
|
+
if (!isRecord2(item)) {
|
|
2695
|
+
return "";
|
|
2696
|
+
}
|
|
2697
|
+
const itemReasoning = pickReasoningText(item);
|
|
2698
|
+
if (itemReasoning) {
|
|
2699
|
+
return itemReasoning;
|
|
2700
|
+
}
|
|
2701
|
+
const itemType = pickString(item.type) ?? "";
|
|
2702
|
+
if ((itemType.includes("reasoning") || itemType.includes("thinking")) && Array.isArray(item.content)) {
|
|
2703
|
+
return item.content.map((part) => isRecord2(part) ? pickTextLike(part) : "").join("");
|
|
2704
|
+
}
|
|
2705
|
+
return "";
|
|
2706
|
+
}).join("");
|
|
2707
|
+
}
|
|
2712
2708
|
function pickAssistantText(payload) {
|
|
2713
2709
|
const message = pickAssistantMessage(payload);
|
|
2714
2710
|
if (message) {
|
|
2715
|
-
const text =
|
|
2711
|
+
const text = pickTextLike(message.content);
|
|
2716
2712
|
if (text.length > 0) {
|
|
2717
2713
|
return text;
|
|
2718
2714
|
}
|
|
@@ -2729,8 +2725,17 @@ function pickAssistantText(payload) {
|
|
|
2729
2725
|
function pickReasoningText(value) {
|
|
2730
2726
|
return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
|
|
2731
2727
|
}
|
|
2732
|
-
function
|
|
2733
|
-
|
|
2728
|
+
function pushReasoningBlock(blocks, turnIndex, text) {
|
|
2729
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
2730
|
+
if (!clean) {
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
blocks.push({ turnIndex, text: clean });
|
|
2734
|
+
}
|
|
2735
|
+
function joinReasoningBlocks(blocks) {
|
|
2736
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
2737
|
+
|
|
2738
|
+
`);
|
|
2734
2739
|
}
|
|
2735
2740
|
function pickTextLike(value) {
|
|
2736
2741
|
if (typeof value === "string") {
|
|
@@ -2805,83 +2810,81 @@ function createAnthropicCompatibleAdapter(options) {
|
|
|
2805
2810
|
if (hasMCPClients(request.mcpClients)) {
|
|
2806
2811
|
return streamWithMCPToolLoop(options, fetcher, path, request, callbacks);
|
|
2807
2812
|
}
|
|
2808
|
-
|
|
2809
|
-
const response = await fetcher(buildURL(options.baseURL, path), {
|
|
2810
|
-
method: "POST",
|
|
2811
|
-
headers: buildHeaders2(options),
|
|
2812
|
-
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
2813
|
-
...options.defaultBody,
|
|
2814
|
-
...request.body,
|
|
2815
|
-
model: options.model,
|
|
2816
|
-
system: input.systemPrompt,
|
|
2817
|
-
messages: input.messages,
|
|
2818
|
-
temperature: request.temperature,
|
|
2819
|
-
stream: true
|
|
2820
|
-
})),
|
|
2821
|
-
signal: request.signal
|
|
2822
|
-
});
|
|
2823
|
-
if (!response.ok) {
|
|
2824
|
-
const message = await response.text();
|
|
2825
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2826
|
-
}
|
|
2827
|
-
callbacks.onStart?.();
|
|
2828
|
-
let text = "";
|
|
2829
|
-
let usage;
|
|
2830
|
-
let finishReason;
|
|
2831
|
-
await consumeSSE(response, (data) => {
|
|
2832
|
-
if (data === "[DONE]") {
|
|
2833
|
-
return;
|
|
2834
|
-
}
|
|
2835
|
-
const json = safeJSONParse(data);
|
|
2836
|
-
if (!isRecord2(json)) {
|
|
2837
|
-
return;
|
|
2838
|
-
}
|
|
2839
|
-
const delta = pickAnthropicDelta(json);
|
|
2840
|
-
const chunkUsage = pickUsage2(json);
|
|
2841
|
-
const chunkFinishReason = pickFinishReason2(json);
|
|
2842
|
-
usage = preferLatestUsage(usage, chunkUsage);
|
|
2843
|
-
if (chunkFinishReason) {
|
|
2844
|
-
finishReason = chunkFinishReason;
|
|
2845
|
-
}
|
|
2846
|
-
if (delta) {
|
|
2847
|
-
text += delta;
|
|
2848
|
-
callbacks.onToken?.(delta);
|
|
2849
|
-
}
|
|
2850
|
-
if (delta || chunkUsage || chunkFinishReason) {
|
|
2851
|
-
const chunk = {
|
|
2852
|
-
textDelta: delta,
|
|
2853
|
-
raw: json,
|
|
2854
|
-
usage: chunkUsage,
|
|
2855
|
-
finishReason: chunkFinishReason
|
|
2856
|
-
};
|
|
2857
|
-
callbacks.onChunk?.(chunk);
|
|
2858
|
-
}
|
|
2859
|
-
});
|
|
2860
|
-
const out = { text, usage, finishReason };
|
|
2861
|
-
callbacks.onComplete?.(out);
|
|
2862
|
-
return out;
|
|
2813
|
+
return streamPassThrough(options, fetcher, path, request, callbacks);
|
|
2863
2814
|
},
|
|
2864
2815
|
async embed() {
|
|
2865
2816
|
throw new Error("Anthropic does not provide a native embedding API. " + "Use the openai-compatible provider with Voyage AI (https://api.voyageai.com) — " + "Anthropic's recommended embedding solution, which uses the same request format.");
|
|
2866
2817
|
}
|
|
2867
2818
|
};
|
|
2868
2819
|
}
|
|
2869
|
-
async function
|
|
2820
|
+
async function streamPassThrough(options, fetcher, path, request, callbacks) {
|
|
2870
2821
|
const input = resolveAnthropicInput(request);
|
|
2871
|
-
const response = await
|
|
2822
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
2823
|
+
system: input.systemPrompt,
|
|
2824
|
+
messages: input.messages,
|
|
2825
|
+
stream: true
|
|
2826
|
+
});
|
|
2827
|
+
if (!response.ok) {
|
|
2828
|
+
const message = await response.text();
|
|
2829
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2830
|
+
}
|
|
2831
|
+
callbacks.onStart?.();
|
|
2832
|
+
let text = "";
|
|
2833
|
+
let usage;
|
|
2834
|
+
let finishReason;
|
|
2835
|
+
await consumeSSE(response, (data) => {
|
|
2836
|
+
if (data === "[DONE]") {
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
const json = safeJSONParse(data);
|
|
2840
|
+
if (!isRecord2(json)) {
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
const delta = pickAnthropicDelta(json);
|
|
2844
|
+
const chunkUsage = pickUsage2(json);
|
|
2845
|
+
const chunkFinishReason = pickFinishReason2(json);
|
|
2846
|
+
usage = preferLatestUsage(usage, chunkUsage);
|
|
2847
|
+
if (chunkFinishReason) {
|
|
2848
|
+
finishReason = chunkFinishReason;
|
|
2849
|
+
}
|
|
2850
|
+
if (delta) {
|
|
2851
|
+
text += delta;
|
|
2852
|
+
callbacks.onToken?.(delta);
|
|
2853
|
+
}
|
|
2854
|
+
if (delta || chunkUsage || chunkFinishReason) {
|
|
2855
|
+
callbacks.onChunk?.({
|
|
2856
|
+
textDelta: delta,
|
|
2857
|
+
raw: json,
|
|
2858
|
+
usage: chunkUsage,
|
|
2859
|
+
finishReason: chunkFinishReason
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2863
|
+
const out = { text, usage, finishReason };
|
|
2864
|
+
callbacks.onComplete?.(out);
|
|
2865
|
+
return out;
|
|
2866
|
+
}
|
|
2867
|
+
function sendAnthropicMessage(options, fetcher, path, request, body) {
|
|
2868
|
+
return fetcher(buildURL(options.baseURL, path), {
|
|
2872
2869
|
method: "POST",
|
|
2873
2870
|
headers: buildHeaders2(options),
|
|
2874
2871
|
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
2875
2872
|
...options.defaultBody,
|
|
2876
2873
|
...request.body,
|
|
2877
2874
|
model: options.model,
|
|
2878
|
-
system: input.systemPrompt,
|
|
2879
|
-
messages: input.messages,
|
|
2880
2875
|
temperature: request.temperature,
|
|
2881
|
-
|
|
2876
|
+
...body
|
|
2882
2877
|
})),
|
|
2883
2878
|
signal: request.signal
|
|
2884
2879
|
});
|
|
2880
|
+
}
|
|
2881
|
+
async function completePassThrough(options, fetcher, path, request) {
|
|
2882
|
+
const input = resolveAnthropicInput(request);
|
|
2883
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
2884
|
+
system: input.systemPrompt,
|
|
2885
|
+
messages: input.messages,
|
|
2886
|
+
stream: false
|
|
2887
|
+
});
|
|
2885
2888
|
if (!response.ok) {
|
|
2886
2889
|
const message = await response.text();
|
|
2887
2890
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2909,24 +2912,16 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2909
2912
|
let lastPayload;
|
|
2910
2913
|
const toolCalls = [];
|
|
2911
2914
|
const toolExecutions = [];
|
|
2915
|
+
const reasoningBlocks = [];
|
|
2912
2916
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2913
2917
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2914
2918
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
2915
|
-
const response = await
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
model: options.model,
|
|
2922
|
-
system: input.systemPrompt,
|
|
2923
|
-
messages,
|
|
2924
|
-
temperature: request.temperature,
|
|
2925
|
-
tools,
|
|
2926
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
2927
|
-
stream: false
|
|
2928
|
-
})),
|
|
2929
|
-
signal: request.signal
|
|
2919
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
2920
|
+
system: input.systemPrompt,
|
|
2921
|
+
messages,
|
|
2922
|
+
tools,
|
|
2923
|
+
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
2924
|
+
stream: false
|
|
2930
2925
|
});
|
|
2931
2926
|
if (!response.ok) {
|
|
2932
2927
|
const message = await response.text();
|
|
@@ -2938,9 +2933,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2938
2933
|
finishReason = pickFinishReason2(payload);
|
|
2939
2934
|
const content = Array.isArray(payload.content) ? payload.content : [];
|
|
2940
2935
|
const calledTools = pickAnthropicToolCalls(payload).filter((call) => call.type === "function");
|
|
2936
|
+
pushReasoningBlock2(reasoningBlocks, round, extractAnthropicReasoning(payload));
|
|
2941
2937
|
if (calledTools.length === 0) {
|
|
2942
2938
|
return {
|
|
2943
2939
|
text: extractAnthropicText(payload),
|
|
2940
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
2941
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2944
2942
|
raw: payload,
|
|
2945
2943
|
usage: aggregatedUsage,
|
|
2946
2944
|
finishReason,
|
|
@@ -2976,6 +2974,8 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2976
2974
|
}
|
|
2977
2975
|
return {
|
|
2978
2976
|
text: extractAnthropicText(lastPayload ?? {}),
|
|
2977
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
2978
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2979
2979
|
raw: lastPayload,
|
|
2980
2980
|
usage: aggregatedUsage,
|
|
2981
2981
|
finishReason,
|
|
@@ -2992,31 +2992,24 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
2992
2992
|
let lastPayload;
|
|
2993
2993
|
const toolCalls = [];
|
|
2994
2994
|
const toolExecutions = [];
|
|
2995
|
+
const reasoningBlocks = [];
|
|
2995
2996
|
callbacks.onStart?.();
|
|
2996
2997
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2997
2998
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2998
2999
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
2999
|
-
const response = await
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
model: options.model,
|
|
3006
|
-
system: input.systemPrompt,
|
|
3007
|
-
messages,
|
|
3008
|
-
temperature: request.temperature,
|
|
3009
|
-
tools,
|
|
3010
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3011
|
-
stream: true
|
|
3012
|
-
})),
|
|
3013
|
-
signal: request.signal
|
|
3000
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
3001
|
+
system: input.systemPrompt,
|
|
3002
|
+
messages,
|
|
3003
|
+
tools,
|
|
3004
|
+
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3005
|
+
stream: true
|
|
3014
3006
|
});
|
|
3015
3007
|
if (!response.ok) {
|
|
3016
3008
|
const message = await response.text();
|
|
3017
3009
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
3018
3010
|
}
|
|
3019
3011
|
let roundText = "";
|
|
3012
|
+
let roundReasoning = "";
|
|
3020
3013
|
let roundUsage;
|
|
3021
3014
|
let roundFinishReason;
|
|
3022
3015
|
const streamedToolCalls = new Map;
|
|
@@ -3030,6 +3023,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3030
3023
|
}
|
|
3031
3024
|
lastPayload = json;
|
|
3032
3025
|
const delta = pickAnthropicDelta(json);
|
|
3026
|
+
const reasoningDelta = pickAnthropicReasoningDelta(json);
|
|
3033
3027
|
const chunkUsage = pickUsage2(json);
|
|
3034
3028
|
const chunkFinishReason = pickFinishReason2(json);
|
|
3035
3029
|
collectAnthropicStreamToolCalls(json, streamedToolCalls);
|
|
@@ -3041,9 +3035,14 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3041
3035
|
roundText += delta;
|
|
3042
3036
|
callbacks.onToken?.(delta);
|
|
3043
3037
|
}
|
|
3044
|
-
if (
|
|
3038
|
+
if (reasoningDelta) {
|
|
3039
|
+
roundReasoning += reasoningDelta;
|
|
3040
|
+
}
|
|
3041
|
+
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
3045
3042
|
const chunk = {
|
|
3046
3043
|
textDelta: delta,
|
|
3044
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
3045
|
+
turnIndex: round,
|
|
3047
3046
|
raw: json,
|
|
3048
3047
|
usage: chunkUsage,
|
|
3049
3048
|
finishReason: chunkFinishReason
|
|
@@ -3056,21 +3055,41 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3056
3055
|
finishReason = roundFinishReason;
|
|
3057
3056
|
}
|
|
3058
3057
|
const calledTools = buildAnthropicStreamToolCalls(streamedToolCalls);
|
|
3058
|
+
pushReasoningBlock2(reasoningBlocks, round, roundReasoning);
|
|
3059
|
+
request.onTurnTransition?.({
|
|
3060
|
+
turnIndex: round,
|
|
3061
|
+
kind: "reasoningComplete",
|
|
3062
|
+
reasoningText: roundReasoning
|
|
3063
|
+
});
|
|
3059
3064
|
if (calledTools.length === 0) {
|
|
3060
3065
|
const out2 = {
|
|
3061
3066
|
text: roundText,
|
|
3067
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3068
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3062
3069
|
raw: lastPayload,
|
|
3063
3070
|
usage: aggregatedUsage,
|
|
3064
3071
|
finishReason,
|
|
3065
3072
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3066
3073
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3067
3074
|
};
|
|
3075
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
3068
3076
|
callbacks.onComplete?.(out2);
|
|
3069
3077
|
return out2;
|
|
3070
3078
|
}
|
|
3071
3079
|
if (round > maxToolRounds) {
|
|
3072
3080
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
3073
3081
|
}
|
|
3082
|
+
request.onTurnTransition?.({
|
|
3083
|
+
turnIndex: round,
|
|
3084
|
+
kind: "toolCallsEmit",
|
|
3085
|
+
toolCalls: calledTools
|
|
3086
|
+
});
|
|
3087
|
+
callbacks.onChunk?.({
|
|
3088
|
+
textDelta: "",
|
|
3089
|
+
turnIndex: round,
|
|
3090
|
+
toolCalls: calledTools,
|
|
3091
|
+
finishReason: roundFinishReason
|
|
3092
|
+
});
|
|
3074
3093
|
const toolResultContent = [];
|
|
3075
3094
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
3076
3095
|
round,
|
|
@@ -3080,6 +3099,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3080
3099
|
});
|
|
3081
3100
|
toolCalls.push(...outputs.map((entry) => entry.call));
|
|
3082
3101
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
3102
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
3083
3103
|
for (const entry of outputs) {
|
|
3084
3104
|
toolResultContent.push({
|
|
3085
3105
|
type: "tool_result",
|
|
@@ -3096,12 +3116,15 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3096
3116
|
}
|
|
3097
3117
|
const out = {
|
|
3098
3118
|
text: "",
|
|
3119
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3120
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3099
3121
|
raw: lastPayload,
|
|
3100
3122
|
usage: aggregatedUsage,
|
|
3101
3123
|
finishReason,
|
|
3102
3124
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3103
3125
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3104
3126
|
};
|
|
3127
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
3105
3128
|
callbacks.onComplete?.(out);
|
|
3106
3129
|
return out;
|
|
3107
3130
|
}
|
|
@@ -3222,10 +3245,26 @@ function extractAnthropicText(payload) {
|
|
|
3222
3245
|
return typeof text === "string" ? text : "";
|
|
3223
3246
|
}).join("");
|
|
3224
3247
|
}
|
|
3225
|
-
function
|
|
3248
|
+
function extractAnthropicReasoning(payload) {
|
|
3226
3249
|
const content = payload.content;
|
|
3227
3250
|
if (!Array.isArray(content)) {
|
|
3228
|
-
return
|
|
3251
|
+
return "";
|
|
3252
|
+
}
|
|
3253
|
+
return content.map((part) => {
|
|
3254
|
+
if (!isRecord2(part)) {
|
|
3255
|
+
return "";
|
|
3256
|
+
}
|
|
3257
|
+
const type = pickString(part.type) ?? "";
|
|
3258
|
+
if (type !== "thinking" && type !== "reasoning") {
|
|
3259
|
+
return "";
|
|
3260
|
+
}
|
|
3261
|
+
return pickString(part.thinking) ?? pickString(part.text) ?? pickString(part.reasoning) ?? "";
|
|
3262
|
+
}).join("");
|
|
3263
|
+
}
|
|
3264
|
+
function pickAnthropicToolCalls(payload) {
|
|
3265
|
+
const content = payload.content;
|
|
3266
|
+
if (!Array.isArray(content)) {
|
|
3267
|
+
return [];
|
|
3229
3268
|
}
|
|
3230
3269
|
const calls = [];
|
|
3231
3270
|
for (const part of content) {
|
|
@@ -3252,6 +3291,35 @@ function pickAnthropicDelta(payload) {
|
|
|
3252
3291
|
}
|
|
3253
3292
|
return "";
|
|
3254
3293
|
}
|
|
3294
|
+
function pickAnthropicReasoningDelta(payload) {
|
|
3295
|
+
const deltaObject = payload.delta;
|
|
3296
|
+
if (isRecord2(deltaObject)) {
|
|
3297
|
+
const type = pickString(deltaObject.type) ?? "";
|
|
3298
|
+
if (type === "thinking_delta" || type === "reasoning_delta") {
|
|
3299
|
+
return pickString(deltaObject.thinking) ?? pickString(deltaObject.text) ?? "";
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
const contentBlock = payload.content_block;
|
|
3303
|
+
if (isRecord2(contentBlock)) {
|
|
3304
|
+
const type = pickString(contentBlock.type) ?? "";
|
|
3305
|
+
if (type === "thinking" || type === "reasoning") {
|
|
3306
|
+
return pickString(contentBlock.thinking) ?? pickString(contentBlock.text) ?? "";
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
return "";
|
|
3310
|
+
}
|
|
3311
|
+
function pushReasoningBlock2(blocks, turnIndex, text) {
|
|
3312
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
3313
|
+
if (!clean) {
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
blocks.push({ turnIndex, text: clean });
|
|
3317
|
+
}
|
|
3318
|
+
function joinReasoningBlocks2(blocks) {
|
|
3319
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
3320
|
+
|
|
3321
|
+
`);
|
|
3322
|
+
}
|
|
3255
3323
|
function collectAnthropicStreamToolCalls(payload, state) {
|
|
3256
3324
|
const eventType = pickString(payload.type);
|
|
3257
3325
|
if (!eventType) {
|
|
@@ -3478,39 +3546,10 @@ function buildProviderOptions(config) {
|
|
|
3478
3546
|
return {
|
|
3479
3547
|
model: config.model,
|
|
3480
3548
|
...transport,
|
|
3481
|
-
...config.options
|
|
3549
|
+
...config.options
|
|
3482
3550
|
};
|
|
3483
3551
|
}
|
|
3484
3552
|
|
|
3485
|
-
// src/utils/debug-colors.ts
|
|
3486
|
-
var ANSI = {
|
|
3487
|
-
reset: "\x1B[0m",
|
|
3488
|
-
bold: "\x1B[1m",
|
|
3489
|
-
cyan: "\x1B[36m",
|
|
3490
|
-
yellow: "\x1B[33m",
|
|
3491
|
-
green: "\x1B[32m",
|
|
3492
|
-
red: "\x1B[31m",
|
|
3493
|
-
dim: "\x1B[2m"
|
|
3494
|
-
};
|
|
3495
|
-
function color(config, text, tone) {
|
|
3496
|
-
if (!config.colors) {
|
|
3497
|
-
return text;
|
|
3498
|
-
}
|
|
3499
|
-
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
3500
|
-
}
|
|
3501
|
-
function dim(config, text) {
|
|
3502
|
-
if (!config.colors) {
|
|
3503
|
-
return text;
|
|
3504
|
-
}
|
|
3505
|
-
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
3506
|
-
}
|
|
3507
|
-
function title(config, text) {
|
|
3508
|
-
if (!config.colors) {
|
|
3509
|
-
return text;
|
|
3510
|
-
}
|
|
3511
|
-
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3512
|
-
}
|
|
3513
|
-
|
|
3514
3553
|
// src/outdent.ts
|
|
3515
3554
|
var DEFAULT_OPTIONS = {
|
|
3516
3555
|
trimLeadingNewline: true,
|
|
@@ -3648,131 +3687,213 @@ function createOutdent(options = {}) {
|
|
|
3648
3687
|
return outdent;
|
|
3649
3688
|
}
|
|
3650
3689
|
|
|
3651
|
-
// src/generate-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3690
|
+
// src/generate-tool-timeout.ts
|
|
3691
|
+
function withToolTimeout(client, toolTimeoutMs) {
|
|
3692
|
+
return {
|
|
3693
|
+
id: client.id,
|
|
3694
|
+
listTools: client.listTools.bind(client),
|
|
3695
|
+
close: client.close?.bind(client),
|
|
3696
|
+
async callTool(params) {
|
|
3697
|
+
let timeoutId;
|
|
3698
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3699
|
+
timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
|
|
3700
|
+
});
|
|
3701
|
+
try {
|
|
3702
|
+
return await Promise.race([client.callTool(params), timeoutPromise]);
|
|
3703
|
+
} finally {
|
|
3704
|
+
clearTimeout(timeoutId);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3709
|
+
function applyToolTimeout(clients, toolTimeoutMs) {
|
|
3710
|
+
return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
|
|
3711
|
+
}
|
|
3712
|
+
// src/generate-output.ts
|
|
3658
3713
|
var RE_THINK_TAGS = /<\/?think\s*>/gi;
|
|
3659
|
-
function
|
|
3660
|
-
const
|
|
3661
|
-
|
|
3714
|
+
function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
|
|
3715
|
+
const sanitized = sanitizeThink(text);
|
|
3716
|
+
const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
|
|
3717
|
+
const reasoning = joinReasoningSegments([
|
|
3718
|
+
dedicatedReasoning,
|
|
3719
|
+
...sanitized.thinkBlocks.map((block) => block.content)
|
|
3720
|
+
]);
|
|
3721
|
+
return {
|
|
3722
|
+
text: visibleText,
|
|
3723
|
+
reasoning,
|
|
3724
|
+
reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
|
|
3725
|
+
thinkBlocks: sanitized.thinkBlocks,
|
|
3726
|
+
parseSource: composeParseSource(visibleText, reasoning)
|
|
3727
|
+
};
|
|
3662
3728
|
}
|
|
3663
|
-
function
|
|
3664
|
-
if (
|
|
3665
|
-
return
|
|
3666
|
-
prompt: value
|
|
3667
|
-
};
|
|
3668
|
-
}
|
|
3669
|
-
if (isPromptResolver(value)) {
|
|
3670
|
-
return normalizePromptPayload(value.resolvePrompt(_context));
|
|
3729
|
+
function normalizeReasoningBlocks(blocks) {
|
|
3730
|
+
if (!Array.isArray(blocks)) {
|
|
3731
|
+
return;
|
|
3671
3732
|
}
|
|
3672
|
-
|
|
3733
|
+
const normalized = blocks.map((block) => ({
|
|
3734
|
+
turnIndex: block.turnIndex,
|
|
3735
|
+
text: block.text.replace(RE_THINK_TAGS, "").trim()
|
|
3736
|
+
})).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
|
|
3737
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
3673
3738
|
}
|
|
3674
|
-
function
|
|
3675
|
-
const
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
|
|
3739
|
+
function appendReasoningBlock(blocks, transition) {
|
|
3740
|
+
const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
|
|
3741
|
+
if (!text) {
|
|
3742
|
+
return blocks;
|
|
3679
3743
|
}
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
|
|
3683
|
-
messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
|
|
3684
|
-
};
|
|
3744
|
+
const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
|
|
3745
|
+
return normalizeReasoningBlocks(next);
|
|
3685
3746
|
}
|
|
3686
|
-
function
|
|
3687
|
-
if (
|
|
3688
|
-
return
|
|
3747
|
+
function composeParseSource(text, reasoning) {
|
|
3748
|
+
if (typeof reasoning !== "string" || reasoning.length === 0) {
|
|
3749
|
+
return text;
|
|
3689
3750
|
}
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
|
|
3696
|
-
}))
|
|
3697
|
-
};
|
|
3751
|
+
const sanitized = reasoning.replace(RE_THINK_TAGS, "");
|
|
3752
|
+
if (sanitized.length === 0) {
|
|
3753
|
+
return text;
|
|
3754
|
+
}
|
|
3755
|
+
return `<think>${sanitized}</think>${text}`;
|
|
3698
3756
|
}
|
|
3699
|
-
function
|
|
3700
|
-
|
|
3701
|
-
|
|
3757
|
+
function aggregateUsage(attempts) {
|
|
3758
|
+
let usage;
|
|
3759
|
+
for (const attempt of attempts) {
|
|
3760
|
+
usage = mergeUsage2(usage, attempt.usage);
|
|
3702
3761
|
}
|
|
3703
|
-
return
|
|
3762
|
+
return usage;
|
|
3704
3763
|
}
|
|
3705
|
-
function
|
|
3706
|
-
|
|
3707
|
-
if (prompts.length === 0) {
|
|
3764
|
+
function mergeUsage2(base, next) {
|
|
3765
|
+
if (!base && !next) {
|
|
3708
3766
|
return;
|
|
3709
3767
|
}
|
|
3710
|
-
return
|
|
3768
|
+
return {
|
|
3769
|
+
inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
|
|
3770
|
+
outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
|
|
3771
|
+
totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
|
|
3772
|
+
cost: (base?.cost ?? 0) + (next?.cost ?? 0)
|
|
3773
|
+
};
|
|
3774
|
+
}
|
|
3775
|
+
function joinReasoningSegments(parts) {
|
|
3776
|
+
return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
|
|
3711
3777
|
|
|
3712
3778
|
`);
|
|
3713
3779
|
}
|
|
3714
|
-
function
|
|
3715
|
-
if (
|
|
3716
|
-
return
|
|
3717
|
-
enabled: option
|
|
3718
|
-
};
|
|
3780
|
+
function stripThinkBlocks(text, thinkBlocks) {
|
|
3781
|
+
if (thinkBlocks.length === 0) {
|
|
3782
|
+
return text;
|
|
3719
3783
|
}
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3784
|
+
let output = "";
|
|
3785
|
+
let cursor = 0;
|
|
3786
|
+
for (const block of thinkBlocks) {
|
|
3787
|
+
output += text.slice(cursor, block.start);
|
|
3788
|
+
cursor = block.end;
|
|
3724
3789
|
}
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
onData: option.onData,
|
|
3728
|
-
to: option.to
|
|
3729
|
-
};
|
|
3790
|
+
output += text.slice(cursor);
|
|
3791
|
+
return output;
|
|
3730
3792
|
}
|
|
3731
|
-
function
|
|
3732
|
-
|
|
3733
|
-
return
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
verbose: false,
|
|
3737
|
-
logger: (line) => console.log(line)
|
|
3738
|
-
};
|
|
3793
|
+
function toStreamDataFingerprint(value) {
|
|
3794
|
+
try {
|
|
3795
|
+
return JSON.stringify(value);
|
|
3796
|
+
} catch {
|
|
3797
|
+
return "__unserializable__";
|
|
3739
3798
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
// src/utils/debug-colors.ts
|
|
3802
|
+
var ANSI = {
|
|
3803
|
+
reset: "\x1B[0m",
|
|
3804
|
+
bold: "\x1B[1m",
|
|
3805
|
+
cyan: "\x1B[36m",
|
|
3806
|
+
yellow: "\x1B[33m",
|
|
3807
|
+
green: "\x1B[32m",
|
|
3808
|
+
red: "\x1B[31m",
|
|
3809
|
+
dim: "\x1B[2m"
|
|
3810
|
+
};
|
|
3811
|
+
function color(config, text, tone) {
|
|
3812
|
+
if (!config.colors) {
|
|
3813
|
+
return text;
|
|
3747
3814
|
}
|
|
3748
|
-
return {
|
|
3749
|
-
enabled: option.enabled ?? true,
|
|
3750
|
-
colors: option.colors ?? true,
|
|
3751
|
-
verbose: option.verbose ?? false,
|
|
3752
|
-
logger: option.logger ?? ((line) => console.log(line))
|
|
3753
|
-
};
|
|
3815
|
+
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
3754
3816
|
}
|
|
3755
|
-
function
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
async callTool(params) {
|
|
3761
|
-
let timeoutId;
|
|
3762
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
3763
|
-
timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
|
|
3764
|
-
});
|
|
3765
|
-
try {
|
|
3766
|
-
return await Promise.race([client.callTool(params), timeoutPromise]);
|
|
3767
|
-
} finally {
|
|
3768
|
-
clearTimeout(timeoutId);
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
};
|
|
3817
|
+
function dim(config, text) {
|
|
3818
|
+
if (!config.colors) {
|
|
3819
|
+
return text;
|
|
3820
|
+
}
|
|
3821
|
+
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
3772
3822
|
}
|
|
3773
|
-
function
|
|
3774
|
-
|
|
3823
|
+
function title(config, text) {
|
|
3824
|
+
if (!config.colors) {
|
|
3825
|
+
return text;
|
|
3826
|
+
}
|
|
3827
|
+
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3828
|
+
}
|
|
3829
|
+
|
|
3830
|
+
// src/generate-debug.ts
|
|
3831
|
+
function emitDebugRequest(config, input) {
|
|
3832
|
+
const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
|
|
3833
|
+
const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
|
|
3834
|
+
const lines = [
|
|
3835
|
+
color(config, title(config, [
|
|
3836
|
+
`[${input.label}][request]`,
|
|
3837
|
+
`attempt=${input.attempt}`,
|
|
3838
|
+
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
3839
|
+
`selfHealAttempt=${input.selfHealAttempt}`
|
|
3840
|
+
].join(" ")), "cyan"),
|
|
3841
|
+
dim(config, [
|
|
3842
|
+
`provider=${input.provider ?? "unknown"}`,
|
|
3843
|
+
`model=${input.model ?? "unknown"}`,
|
|
3844
|
+
`stream=${input.stream}`
|
|
3845
|
+
].join(" ")),
|
|
3846
|
+
color(config, "prompt:", "yellow"),
|
|
3847
|
+
input.requestPayload.prompt ?? "(none)",
|
|
3848
|
+
color(config, "messages:", "yellow"),
|
|
3849
|
+
requestMessages,
|
|
3850
|
+
color(config, "systemPrompt:", "yellow"),
|
|
3851
|
+
input.requestPayload.systemPrompt ?? "(none)",
|
|
3852
|
+
color(config, "request.body:", "yellow"),
|
|
3853
|
+
requestBody
|
|
3854
|
+
];
|
|
3855
|
+
emitDebug(config, lines.join(`
|
|
3856
|
+
`));
|
|
3857
|
+
}
|
|
3858
|
+
function emitDebugResponse(config, input) {
|
|
3859
|
+
const text = input.text.length > 0 ? input.text : "(none)";
|
|
3860
|
+
const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
|
|
3861
|
+
const metadata = [
|
|
3862
|
+
`via=${input.via}`,
|
|
3863
|
+
`textChars=${input.text.length}`,
|
|
3864
|
+
`reasoningChars=${input.reasoning.length}`
|
|
3865
|
+
];
|
|
3866
|
+
if (config.verbose) {
|
|
3867
|
+
metadata.push(`parseSourceChars=${input.parseSource.length}`);
|
|
3868
|
+
}
|
|
3869
|
+
metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
|
|
3870
|
+
const lines = [
|
|
3871
|
+
color(config, title(config, [
|
|
3872
|
+
`[${input.label}][response]`,
|
|
3873
|
+
`attempt=${input.attempt}`,
|
|
3874
|
+
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
3875
|
+
`selfHealAttempt=${input.selfHealAttempt}`
|
|
3876
|
+
].join(" ")), "green"),
|
|
3877
|
+
dim(config, metadata.join(" ")),
|
|
3878
|
+
color(config, "text:", "yellow"),
|
|
3879
|
+
text,
|
|
3880
|
+
color(config, "reasoning:", "yellow"),
|
|
3881
|
+
reasoning
|
|
3882
|
+
];
|
|
3883
|
+
if (config.verbose) {
|
|
3884
|
+
lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
|
|
3885
|
+
}
|
|
3886
|
+
emitDebug(config, lines.join(`
|
|
3887
|
+
`));
|
|
3775
3888
|
}
|
|
3889
|
+
function emitDebug(config, message) {
|
|
3890
|
+
if (!config.enabled) {
|
|
3891
|
+
return;
|
|
3892
|
+
}
|
|
3893
|
+
config.logger(message);
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3896
|
+
// src/generate-model-call.ts
|
|
3776
3897
|
async function callModel(adapter, options) {
|
|
3777
3898
|
const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
|
|
3778
3899
|
const requestPayload = {
|
|
@@ -3792,6 +3913,7 @@ async function callModel(adapter, options) {
|
|
|
3792
3913
|
transformToolCallParams: options.request?.transformToolCallParams,
|
|
3793
3914
|
unknownToolError: options.request?.unknownToolError,
|
|
3794
3915
|
toolDebug: options.request?.toolDebug,
|
|
3916
|
+
onTurnTransition: options.stream.onTurnTransition,
|
|
3795
3917
|
body: options.request?.body,
|
|
3796
3918
|
signal: requestSignal
|
|
3797
3919
|
};
|
|
@@ -3819,13 +3941,21 @@ async function callModel(adapter, options) {
|
|
|
3819
3941
|
let latestFinishReason;
|
|
3820
3942
|
let streamedProviderText = "";
|
|
3821
3943
|
let streamedDedicatedReasoning = "";
|
|
3944
|
+
let currentTurnIndex;
|
|
3945
|
+
let currentToolCalls;
|
|
3946
|
+
let streamedReasoningBlocks;
|
|
3822
3947
|
let lastSnapshotFingerprint;
|
|
3823
3948
|
let previousSnapshotText = "";
|
|
3824
3949
|
let previousSnapshotReasoning = "";
|
|
3825
3950
|
const emitStreamingData = (done, usage2, finishReason2) => {
|
|
3826
|
-
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
|
|
3951
|
+
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3827
3952
|
const snapshot = options.buildSnapshot(normalized2);
|
|
3828
|
-
const fingerprint = toStreamDataFingerprint(
|
|
3953
|
+
const fingerprint = toStreamDataFingerprint({
|
|
3954
|
+
snapshot,
|
|
3955
|
+
done,
|
|
3956
|
+
turnIndex: currentTurnIndex,
|
|
3957
|
+
toolCalls: currentToolCalls
|
|
3958
|
+
});
|
|
3829
3959
|
if (!done && fingerprint === lastSnapshotFingerprint) {
|
|
3830
3960
|
return;
|
|
3831
3961
|
}
|
|
@@ -3841,7 +3971,9 @@ async function callModel(adapter, options) {
|
|
|
3841
3971
|
snapshot,
|
|
3842
3972
|
done,
|
|
3843
3973
|
usage: usage2,
|
|
3844
|
-
finishReason: finishReason2
|
|
3974
|
+
finishReason: finishReason2,
|
|
3975
|
+
turnIndex: currentTurnIndex,
|
|
3976
|
+
toolCalls: currentToolCalls
|
|
3845
3977
|
});
|
|
3846
3978
|
if (options.stream.to === "stdout" && delta.text) {
|
|
3847
3979
|
process.stdout.write(delta.text);
|
|
@@ -3876,8 +4008,21 @@ async function callModel(adapter, options) {
|
|
|
3876
4008
|
streamedDedicatedReasoning += delta;
|
|
3877
4009
|
emitStreamingData(false);
|
|
3878
4010
|
};
|
|
3879
|
-
const
|
|
4011
|
+
const streamRequestPayload = {
|
|
4012
|
+
...requestPayload,
|
|
4013
|
+
onTurnTransition: (transition) => {
|
|
4014
|
+
if (transition.kind === "reasoningComplete") {
|
|
4015
|
+
streamedReasoningBlocks = appendReasoningBlock(streamedReasoningBlocks, transition);
|
|
4016
|
+
}
|
|
4017
|
+
options.stream.onTurnTransition?.(transition);
|
|
4018
|
+
}
|
|
4019
|
+
};
|
|
4020
|
+
const response2 = await adapter.stream(streamRequestPayload, {
|
|
3880
4021
|
onChunk: (chunk) => {
|
|
4022
|
+
if (chunk.turnIndex !== undefined) {
|
|
4023
|
+
currentTurnIndex = chunk.turnIndex;
|
|
4024
|
+
}
|
|
4025
|
+
currentToolCalls = chunk.toolCalls;
|
|
3881
4026
|
if (chunk.textDelta) {
|
|
3882
4027
|
handleTextDelta(chunk.textDelta);
|
|
3883
4028
|
}
|
|
@@ -3890,11 +4035,15 @@ async function callModel(adapter, options) {
|
|
|
3890
4035
|
if (chunk.finishReason) {
|
|
3891
4036
|
latestFinishReason = chunk.finishReason;
|
|
3892
4037
|
}
|
|
4038
|
+
if (!chunk.textDelta && !chunk.reasoningDelta && (chunk.turnIndex !== undefined || chunk.toolCalls)) {
|
|
4039
|
+
emitStreamingData(false, chunk.usage, chunk.finishReason);
|
|
4040
|
+
}
|
|
3893
4041
|
}
|
|
3894
4042
|
});
|
|
3895
4043
|
streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
|
|
3896
4044
|
streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
|
|
3897
|
-
|
|
4045
|
+
streamedReasoningBlocks = response2.reasoningBlocks ?? streamedReasoningBlocks;
|
|
4046
|
+
const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3898
4047
|
const usage = preferLatestUsage(latestUsage, response2.usage);
|
|
3899
4048
|
const finishReason = response2.finishReason ?? latestFinishReason;
|
|
3900
4049
|
emitStreamingData(true, usage, finishReason);
|
|
@@ -3926,11 +4075,12 @@ async function callModel(adapter, options) {
|
|
|
3926
4075
|
parseSource: finalNormalized.parseSource,
|
|
3927
4076
|
via: "stream",
|
|
3928
4077
|
usage,
|
|
3929
|
-
finishReason
|
|
4078
|
+
finishReason,
|
|
4079
|
+
reasoningBlocks: finalNormalized.reasoningBlocks
|
|
3930
4080
|
};
|
|
3931
4081
|
}
|
|
3932
4082
|
const response = await adapter.complete(requestPayload);
|
|
3933
|
-
const normalized = normalizeModelOutput(response.text, response.reasoning);
|
|
4083
|
+
const normalized = normalizeModelOutput(response.text, response.reasoning, response.reasoningBlocks);
|
|
3934
4084
|
options.observe?.(options.buildEvent({
|
|
3935
4085
|
stage: "llm.response",
|
|
3936
4086
|
message: "Completion response received.",
|
|
@@ -3959,51 +4109,119 @@ async function callModel(adapter, options) {
|
|
|
3959
4109
|
parseSource: normalized.parseSource,
|
|
3960
4110
|
via: "complete",
|
|
3961
4111
|
usage: response.usage,
|
|
3962
|
-
finishReason: response.finishReason
|
|
4112
|
+
finishReason: response.finishReason,
|
|
4113
|
+
reasoningBlocks: normalized.reasoningBlocks
|
|
3963
4114
|
};
|
|
3964
4115
|
}
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
4116
|
+
|
|
4117
|
+
// src/generate-shared.ts
|
|
4118
|
+
var sharedOutdent = createOutdent({
|
|
4119
|
+
trimLeadingNewline: true,
|
|
4120
|
+
trimTrailingNewline: true,
|
|
4121
|
+
newline: `
|
|
4122
|
+
`
|
|
4123
|
+
});
|
|
4124
|
+
function resolvePrompt(prompt, context) {
|
|
4125
|
+
const resolved = typeof prompt === "function" ? prompt(context) : prompt;
|
|
4126
|
+
return normalizePromptValue(resolved, context);
|
|
4127
|
+
}
|
|
4128
|
+
function normalizePromptValue(value, _context) {
|
|
4129
|
+
if (typeof value === "string") {
|
|
4130
|
+
return {
|
|
4131
|
+
prompt: value
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
4134
|
+
if (isPromptResolver(value)) {
|
|
4135
|
+
return normalizePromptPayload(value.resolvePrompt(_context));
|
|
4136
|
+
}
|
|
4137
|
+
return normalizePromptPayload(value);
|
|
4138
|
+
}
|
|
4139
|
+
function normalizePromptPayload(value) {
|
|
4140
|
+
const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
|
|
4141
|
+
const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
|
|
4142
|
+
if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
|
|
4143
|
+
throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
|
|
4144
|
+
}
|
|
3972
4145
|
return {
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
parseSource: composeParseSource(visibleText, reasoning)
|
|
4146
|
+
prompt,
|
|
4147
|
+
systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
|
|
4148
|
+
messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
|
|
3977
4149
|
};
|
|
3978
4150
|
}
|
|
3979
|
-
function
|
|
3980
|
-
if (
|
|
3981
|
-
return
|
|
4151
|
+
function applyPromptOutdent(payload, enabled) {
|
|
4152
|
+
if (!enabled) {
|
|
4153
|
+
return payload;
|
|
3982
4154
|
}
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
4155
|
+
return {
|
|
4156
|
+
prompt: typeof payload.prompt === "string" ? sharedOutdent.string(payload.prompt) : undefined,
|
|
4157
|
+
systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
|
|
4158
|
+
messages: payload.messages?.map((message) => ({
|
|
4159
|
+
...message,
|
|
4160
|
+
content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
|
|
4161
|
+
}))
|
|
4162
|
+
};
|
|
4163
|
+
}
|
|
4164
|
+
function applyOutdentToOptionalPrompt(value, enabled) {
|
|
4165
|
+
if (!enabled || typeof value !== "string") {
|
|
4166
|
+
return value;
|
|
3986
4167
|
}
|
|
3987
|
-
return
|
|
4168
|
+
return sharedOutdent.string(value);
|
|
3988
4169
|
}
|
|
3989
|
-
function
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
4170
|
+
function mergeSystemPrompts(primary, secondary) {
|
|
4171
|
+
const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
4172
|
+
if (prompts.length === 0) {
|
|
4173
|
+
return;
|
|
4174
|
+
}
|
|
4175
|
+
return prompts.join(`
|
|
4176
|
+
|
|
4177
|
+
`);
|
|
4178
|
+
}
|
|
4179
|
+
function normalizeStreamConfig(option) {
|
|
4180
|
+
if (typeof option === "boolean") {
|
|
4181
|
+
return {
|
|
4182
|
+
enabled: option
|
|
4183
|
+
};
|
|
4184
|
+
}
|
|
4185
|
+
if (!option) {
|
|
4186
|
+
return {
|
|
4187
|
+
enabled: false
|
|
4188
|
+
};
|
|
3993
4189
|
}
|
|
3994
|
-
return
|
|
4190
|
+
return {
|
|
4191
|
+
enabled: option.enabled ?? true,
|
|
4192
|
+
onData: option.onData,
|
|
4193
|
+
onTurnTransition: option.onTurnTransition,
|
|
4194
|
+
to: option.to
|
|
4195
|
+
};
|
|
3995
4196
|
}
|
|
3996
|
-
function
|
|
3997
|
-
if (
|
|
3998
|
-
return
|
|
4197
|
+
function normalizeDebugConfig(option) {
|
|
4198
|
+
if (typeof option === "boolean") {
|
|
4199
|
+
return {
|
|
4200
|
+
enabled: option,
|
|
4201
|
+
colors: true,
|
|
4202
|
+
verbose: false,
|
|
4203
|
+
logger: defaultDebugLogger
|
|
4204
|
+
};
|
|
4205
|
+
}
|
|
4206
|
+
if (!option) {
|
|
4207
|
+
return {
|
|
4208
|
+
enabled: false,
|
|
4209
|
+
colors: true,
|
|
4210
|
+
verbose: false,
|
|
4211
|
+
logger: defaultDebugLogger
|
|
4212
|
+
};
|
|
3999
4213
|
}
|
|
4000
4214
|
return {
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4215
|
+
enabled: option.enabled ?? true,
|
|
4216
|
+
colors: option.colors ?? true,
|
|
4217
|
+
verbose: option.verbose ?? false,
|
|
4218
|
+
logger: option.logger ?? defaultDebugLogger
|
|
4005
4219
|
};
|
|
4006
4220
|
}
|
|
4221
|
+
function defaultDebugLogger(line) {
|
|
4222
|
+
const { log } = globalThis.console;
|
|
4223
|
+
log(line);
|
|
4224
|
+
}
|
|
4007
4225
|
function isPromptResolver(value) {
|
|
4008
4226
|
return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
|
|
4009
4227
|
}
|
|
@@ -4017,95 +4235,6 @@ function isLLMMessage(value) {
|
|
|
4017
4235
|
}
|
|
4018
4236
|
return "content" in candidate;
|
|
4019
4237
|
}
|
|
4020
|
-
function joinReasoningSegments(parts) {
|
|
4021
|
-
return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
|
|
4022
|
-
|
|
4023
|
-
`);
|
|
4024
|
-
}
|
|
4025
|
-
function stripThinkBlocks(text, thinkBlocks) {
|
|
4026
|
-
if (thinkBlocks.length === 0) {
|
|
4027
|
-
return text;
|
|
4028
|
-
}
|
|
4029
|
-
let output = "";
|
|
4030
|
-
let cursor = 0;
|
|
4031
|
-
for (const block of thinkBlocks) {
|
|
4032
|
-
output += text.slice(cursor, block.start);
|
|
4033
|
-
cursor = block.end;
|
|
4034
|
-
}
|
|
4035
|
-
output += text.slice(cursor);
|
|
4036
|
-
return output;
|
|
4037
|
-
}
|
|
4038
|
-
function toStreamDataFingerprint(value) {
|
|
4039
|
-
try {
|
|
4040
|
-
return JSON.stringify(value);
|
|
4041
|
-
} catch {
|
|
4042
|
-
return "__unserializable__";
|
|
4043
|
-
}
|
|
4044
|
-
}
|
|
4045
|
-
function emitDebugRequest(config, input) {
|
|
4046
|
-
const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
|
|
4047
|
-
const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
|
|
4048
|
-
const lines = [
|
|
4049
|
-
color(config, title(config, [
|
|
4050
|
-
`[${input.label}][request]`,
|
|
4051
|
-
`attempt=${input.attempt}`,
|
|
4052
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4053
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4054
|
-
].join(" ")), "cyan"),
|
|
4055
|
-
dim(config, [
|
|
4056
|
-
`provider=${input.provider ?? "unknown"}`,
|
|
4057
|
-
`model=${input.model ?? "unknown"}`,
|
|
4058
|
-
`stream=${input.stream}`
|
|
4059
|
-
].join(" ")),
|
|
4060
|
-
color(config, "prompt:", "yellow"),
|
|
4061
|
-
input.requestPayload.prompt ?? "(none)",
|
|
4062
|
-
color(config, "messages:", "yellow"),
|
|
4063
|
-
requestMessages,
|
|
4064
|
-
color(config, "systemPrompt:", "yellow"),
|
|
4065
|
-
input.requestPayload.systemPrompt ?? "(none)",
|
|
4066
|
-
color(config, "request.body:", "yellow"),
|
|
4067
|
-
requestBody
|
|
4068
|
-
];
|
|
4069
|
-
emitDebug(config, lines.join(`
|
|
4070
|
-
`));
|
|
4071
|
-
}
|
|
4072
|
-
function emitDebugResponse(config, input) {
|
|
4073
|
-
const text = input.text.length > 0 ? input.text : "(none)";
|
|
4074
|
-
const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
|
|
4075
|
-
const metadata = [
|
|
4076
|
-
`via=${input.via}`,
|
|
4077
|
-
`textChars=${input.text.length}`,
|
|
4078
|
-
`reasoningChars=${input.reasoning.length}`
|
|
4079
|
-
];
|
|
4080
|
-
if (config.verbose) {
|
|
4081
|
-
metadata.push(`parseSourceChars=${input.parseSource.length}`);
|
|
4082
|
-
}
|
|
4083
|
-
metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
|
|
4084
|
-
const lines = [
|
|
4085
|
-
color(config, title(config, [
|
|
4086
|
-
`[${input.label}][response]`,
|
|
4087
|
-
`attempt=${input.attempt}`,
|
|
4088
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4089
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4090
|
-
].join(" ")), "green"),
|
|
4091
|
-
dim(config, metadata.join(" ")),
|
|
4092
|
-
color(config, "text:", "yellow"),
|
|
4093
|
-
text,
|
|
4094
|
-
color(config, "reasoning:", "yellow"),
|
|
4095
|
-
reasoning
|
|
4096
|
-
];
|
|
4097
|
-
if (config.verbose) {
|
|
4098
|
-
lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
|
|
4099
|
-
}
|
|
4100
|
-
emitDebug(config, lines.join(`
|
|
4101
|
-
`));
|
|
4102
|
-
}
|
|
4103
|
-
function emitDebug(config, message) {
|
|
4104
|
-
if (!config.enabled) {
|
|
4105
|
-
return;
|
|
4106
|
-
}
|
|
4107
|
-
config.logger(message);
|
|
4108
|
-
}
|
|
4109
4238
|
|
|
4110
4239
|
// src/generate.ts
|
|
4111
4240
|
async function generate(adapter, promptOrOptions, callOptions) {
|
|
@@ -4135,7 +4264,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4135
4264
|
}),
|
|
4136
4265
|
buildSnapshot: (model) => ({
|
|
4137
4266
|
text: model.text,
|
|
4138
|
-
reasoning: model.reasoning
|
|
4267
|
+
reasoning: model.reasoning,
|
|
4268
|
+
...model.reasoningBlocks ? { reasoningBlocks: model.reasoningBlocks } : {}
|
|
4139
4269
|
}),
|
|
4140
4270
|
debug: debugConfig,
|
|
4141
4271
|
debugLabel: "generate",
|
|
@@ -4150,7 +4280,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4150
4280
|
text: response.text,
|
|
4151
4281
|
reasoning: response.reasoning,
|
|
4152
4282
|
usage: response.usage,
|
|
4153
|
-
finishReason: response.finishReason
|
|
4283
|
+
finishReason: response.finishReason,
|
|
4284
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {}
|
|
4154
4285
|
};
|
|
4155
4286
|
const attempts = [attempt];
|
|
4156
4287
|
normalized.observe?.({
|
|
@@ -4167,7 +4298,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4167
4298
|
reasoning: attempt.reasoning,
|
|
4168
4299
|
attempts,
|
|
4169
4300
|
usage: aggregateUsage(attempts),
|
|
4170
|
-
finishReason: attempt.finishReason
|
|
4301
|
+
finishReason: attempt.finishReason,
|
|
4302
|
+
...attempt.reasoningBlocks ? { reasoningBlocks: attempt.reasoningBlocks } : {}
|
|
4171
4303
|
};
|
|
4172
4304
|
}
|
|
4173
4305
|
function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
@@ -4178,7 +4310,7 @@ function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
|
4178
4310
|
throw new Error("Missing prompt in generate(adapter, prompt, options?) call.");
|
|
4179
4311
|
}
|
|
4180
4312
|
return {
|
|
4181
|
-
...callOptions
|
|
4313
|
+
...callOptions,
|
|
4182
4314
|
prompt: promptOrOptions
|
|
4183
4315
|
};
|
|
4184
4316
|
}
|
|
@@ -4204,9 +4336,6 @@ function prepareGeneratePromptPayload(payload, systemPrompt) {
|
|
|
4204
4336
|
};
|
|
4205
4337
|
}
|
|
4206
4338
|
|
|
4207
|
-
// src/structured.ts
|
|
4208
|
-
import { jsonrepair as jsonrepair3 } from "jsonrepair";
|
|
4209
|
-
|
|
4210
4339
|
// src/parse.ts
|
|
4211
4340
|
import { jsonrepair as jsonrepair2 } from "jsonrepair";
|
|
4212
4341
|
function parseLLMOutput(output, schema, options = {}) {
|
|
@@ -4506,6 +4635,80 @@ function formatZodIssues(issues) {
|
|
|
4506
4635
|
`);
|
|
4507
4636
|
}
|
|
4508
4637
|
|
|
4638
|
+
// src/structured-streaming.ts
|
|
4639
|
+
import { jsonrepair as jsonrepair3 } from "jsonrepair";
|
|
4640
|
+
function parseStreamingStructuredData(parseSource) {
|
|
4641
|
+
const sanitized = sanitizeThink(parseSource);
|
|
4642
|
+
const start = findFirstJsonRootStart(sanitized.visibleText);
|
|
4643
|
+
if (start < 0) {
|
|
4644
|
+
return null;
|
|
4645
|
+
}
|
|
4646
|
+
const candidate = sanitized.visibleText.slice(start).trim();
|
|
4647
|
+
if (!candidate) {
|
|
4648
|
+
return null;
|
|
4649
|
+
}
|
|
4650
|
+
try {
|
|
4651
|
+
const repaired = jsonrepair3(candidate);
|
|
4652
|
+
const parsed = JSON.parse(repaired);
|
|
4653
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
4654
|
+
return null;
|
|
4655
|
+
}
|
|
4656
|
+
return parsed;
|
|
4657
|
+
} catch {
|
|
4658
|
+
return null;
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
function findFirstJsonRootStart(input) {
|
|
4662
|
+
const unquotedRootStart = findFirstUnquotedJsonRootStart(input);
|
|
4663
|
+
if (unquotedRootStart >= 0) {
|
|
4664
|
+
return unquotedRootStart;
|
|
4665
|
+
}
|
|
4666
|
+
return findFirstRawJsonRootStart(input);
|
|
4667
|
+
}
|
|
4668
|
+
function findFirstUnquotedJsonRootStart(input) {
|
|
4669
|
+
let inString = false;
|
|
4670
|
+
let escaped = false;
|
|
4671
|
+
for (let index = 0;index < input.length; index += 1) {
|
|
4672
|
+
const char = input[index];
|
|
4673
|
+
if (!char) {
|
|
4674
|
+
continue;
|
|
4675
|
+
}
|
|
4676
|
+
if (inString) {
|
|
4677
|
+
if (escaped) {
|
|
4678
|
+
escaped = false;
|
|
4679
|
+
continue;
|
|
4680
|
+
}
|
|
4681
|
+
if (char === "\\") {
|
|
4682
|
+
escaped = true;
|
|
4683
|
+
continue;
|
|
4684
|
+
}
|
|
4685
|
+
if (char === '"') {
|
|
4686
|
+
inString = false;
|
|
4687
|
+
}
|
|
4688
|
+
continue;
|
|
4689
|
+
}
|
|
4690
|
+
if (char === '"') {
|
|
4691
|
+
inString = true;
|
|
4692
|
+
continue;
|
|
4693
|
+
}
|
|
4694
|
+
if (char === "{" || char === "[") {
|
|
4695
|
+
return index;
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
return -1;
|
|
4699
|
+
}
|
|
4700
|
+
function findFirstRawJsonRootStart(input) {
|
|
4701
|
+
const objectStart = input.indexOf("{");
|
|
4702
|
+
const arrayStart = input.indexOf("[");
|
|
4703
|
+
if (objectStart < 0) {
|
|
4704
|
+
return arrayStart;
|
|
4705
|
+
}
|
|
4706
|
+
if (arrayStart < 0) {
|
|
4707
|
+
return objectStart;
|
|
4708
|
+
}
|
|
4709
|
+
return Math.min(objectStart, arrayStart);
|
|
4710
|
+
}
|
|
4711
|
+
|
|
4509
4712
|
// src/structured.ts
|
|
4510
4713
|
class StructuredParseError extends Error {
|
|
4511
4714
|
name = "StructuredParseError";
|
|
@@ -4743,7 +4946,7 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
|
|
|
4743
4946
|
throw new Error("Missing prompt in structured(adapter, schema, prompt, options?) call.");
|
|
4744
4947
|
}
|
|
4745
4948
|
return {
|
|
4746
|
-
...callOptions
|
|
4949
|
+
...callOptions,
|
|
4747
4950
|
schema: schemaOrOptions,
|
|
4748
4951
|
prompt: promptInput
|
|
4749
4952
|
};
|
|
@@ -4987,6 +5190,7 @@ async function executeAttempt(adapter, input) {
|
|
|
4987
5190
|
success: parsed.success,
|
|
4988
5191
|
usage: response.usage,
|
|
4989
5192
|
finishReason: response.finishReason,
|
|
5193
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {},
|
|
4990
5194
|
parsed
|
|
4991
5195
|
};
|
|
4992
5196
|
return {
|
|
@@ -5007,72 +5211,12 @@ async function callModel2(adapter, options) {
|
|
|
5007
5211
|
buildSnapshot: (normalized) => ({
|
|
5008
5212
|
text: normalized.text,
|
|
5009
5213
|
reasoning: normalized.reasoning,
|
|
5214
|
+
...normalized.reasoningBlocks ? { reasoningBlocks: normalized.reasoningBlocks } : {},
|
|
5010
5215
|
data: parseStreamingStructuredData(normalized.parseSource) ?? null
|
|
5011
5216
|
}),
|
|
5012
5217
|
debugLabel: "structured"
|
|
5013
5218
|
});
|
|
5014
5219
|
}
|
|
5015
|
-
function parseStreamingStructuredData(parseSource) {
|
|
5016
|
-
const sanitized = sanitizeThink(parseSource);
|
|
5017
|
-
const start = findFirstJsonRootStart(sanitized.visibleText);
|
|
5018
|
-
if (start < 0) {
|
|
5019
|
-
return null;
|
|
5020
|
-
}
|
|
5021
|
-
const candidate = sanitized.visibleText.slice(start).trim();
|
|
5022
|
-
if (!candidate) {
|
|
5023
|
-
return null;
|
|
5024
|
-
}
|
|
5025
|
-
try {
|
|
5026
|
-
const repaired = jsonrepair3(candidate);
|
|
5027
|
-
const parsed = JSON.parse(repaired);
|
|
5028
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
5029
|
-
return null;
|
|
5030
|
-
}
|
|
5031
|
-
return parsed;
|
|
5032
|
-
} catch {
|
|
5033
|
-
return null;
|
|
5034
|
-
}
|
|
5035
|
-
}
|
|
5036
|
-
function findFirstJsonRootStart(input) {
|
|
5037
|
-
let inString = false;
|
|
5038
|
-
let escaped = false;
|
|
5039
|
-
for (let index = 0;index < input.length; index += 1) {
|
|
5040
|
-
const char = input[index];
|
|
5041
|
-
if (!char) {
|
|
5042
|
-
continue;
|
|
5043
|
-
}
|
|
5044
|
-
if (inString) {
|
|
5045
|
-
if (escaped) {
|
|
5046
|
-
escaped = false;
|
|
5047
|
-
continue;
|
|
5048
|
-
}
|
|
5049
|
-
if (char === "\\") {
|
|
5050
|
-
escaped = true;
|
|
5051
|
-
continue;
|
|
5052
|
-
}
|
|
5053
|
-
if (char === '"') {
|
|
5054
|
-
inString = false;
|
|
5055
|
-
}
|
|
5056
|
-
continue;
|
|
5057
|
-
}
|
|
5058
|
-
if (char === '"') {
|
|
5059
|
-
inString = true;
|
|
5060
|
-
continue;
|
|
5061
|
-
}
|
|
5062
|
-
if (char === "{" || char === "[") {
|
|
5063
|
-
return index;
|
|
5064
|
-
}
|
|
5065
|
-
}
|
|
5066
|
-
const objectStart = input.indexOf("{");
|
|
5067
|
-
const arrayStart = input.indexOf("[");
|
|
5068
|
-
if (objectStart < 0) {
|
|
5069
|
-
return arrayStart;
|
|
5070
|
-
}
|
|
5071
|
-
if (arrayStart < 0) {
|
|
5072
|
-
return objectStart;
|
|
5073
|
-
}
|
|
5074
|
-
return Math.min(objectStart, arrayStart);
|
|
5075
|
-
}
|
|
5076
5220
|
function parseWithObserve(output, schema, parseOptions, context) {
|
|
5077
5221
|
const userParseTrace = parseOptions.onTrace;
|
|
5078
5222
|
return parseLLMOutput(output, schema, {
|
|
@@ -5107,7 +5251,8 @@ function buildSuccessResult(data, attempts) {
|
|
|
5107
5251
|
json: final?.json ?? null,
|
|
5108
5252
|
attempts,
|
|
5109
5253
|
usage: aggregateUsage(attempts),
|
|
5110
|
-
finishReason: final?.finishReason
|
|
5254
|
+
finishReason: final?.finishReason,
|
|
5255
|
+
...final?.reasoningBlocks ? { reasoningBlocks: final.reasoningBlocks } : {}
|
|
5111
5256
|
};
|
|
5112
5257
|
}
|
|
5113
5258
|
function toStructuredError(attempt) {
|
|
@@ -5174,12 +5319,12 @@ function mergeStructuredOptions(defaults, overrides) {
|
|
|
5174
5319
|
...defaults,
|
|
5175
5320
|
...overrides,
|
|
5176
5321
|
parse: {
|
|
5177
|
-
...defaults?.parse
|
|
5178
|
-
...overrides?.parse
|
|
5322
|
+
...defaults?.parse,
|
|
5323
|
+
...overrides?.parse
|
|
5179
5324
|
},
|
|
5180
5325
|
request: {
|
|
5181
|
-
...defaults?.request
|
|
5182
|
-
...overrides?.request
|
|
5326
|
+
...defaults?.request,
|
|
5327
|
+
...overrides?.request
|
|
5183
5328
|
},
|
|
5184
5329
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5185
5330
|
selfHeal: mergeObjectLike(defaults?.selfHeal, overrides?.selfHeal),
|
|
@@ -5195,8 +5340,8 @@ function mergeGenerateOptions(defaults, overrides) {
|
|
|
5195
5340
|
outdent: overrides?.outdent ?? defaults?.outdent,
|
|
5196
5341
|
systemPrompt: overrides?.systemPrompt ?? defaults?.systemPrompt,
|
|
5197
5342
|
request: {
|
|
5198
|
-
...defaults?.request
|
|
5199
|
-
...overrides?.request
|
|
5343
|
+
...defaults?.request,
|
|
5344
|
+
...overrides?.request
|
|
5200
5345
|
},
|
|
5201
5346
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5202
5347
|
debug: mergeObjectLike(defaults?.debug, overrides?.debug),
|
|
@@ -5574,11 +5719,11 @@ function inferSchemaExample(schema) {
|
|
|
5574
5719
|
}
|
|
5575
5720
|
function getObjectShape(schema) {
|
|
5576
5721
|
const unwrapped = unwrap2(schema).schema;
|
|
5577
|
-
const typeName = unwrapped.
|
|
5722
|
+
const typeName = unwrapped.def?.type;
|
|
5578
5723
|
if (typeName !== "object") {
|
|
5579
5724
|
return null;
|
|
5580
5725
|
}
|
|
5581
|
-
const rawShape = unwrapped.
|
|
5726
|
+
const rawShape = unwrapped.def?.shape;
|
|
5582
5727
|
if (typeof rawShape === "function") {
|
|
5583
5728
|
return rawShape();
|
|
5584
5729
|
}
|
|
@@ -5586,11 +5731,11 @@ function getObjectShape(schema) {
|
|
|
5586
5731
|
}
|
|
5587
5732
|
function readDefaultValue(schema) {
|
|
5588
5733
|
let current = schema;
|
|
5589
|
-
while (current?.
|
|
5590
|
-
const typeName = current.
|
|
5734
|
+
while (current?.def?.type) {
|
|
5735
|
+
const typeName = current.def.type;
|
|
5591
5736
|
if (typeName === "default") {
|
|
5592
5737
|
try {
|
|
5593
|
-
const raw = current.
|
|
5738
|
+
const raw = current.def.defaultValue;
|
|
5594
5739
|
if (typeof raw === "function") {
|
|
5595
5740
|
return raw();
|
|
5596
5741
|
}
|
|
@@ -5600,11 +5745,11 @@ function readDefaultValue(schema) {
|
|
|
5600
5745
|
}
|
|
5601
5746
|
}
|
|
5602
5747
|
if (typeName === "optional" || typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5603
|
-
current = current.
|
|
5748
|
+
current = current.def.innerType ?? current;
|
|
5604
5749
|
continue;
|
|
5605
5750
|
}
|
|
5606
5751
|
if (typeName === "pipe") {
|
|
5607
|
-
current = current.
|
|
5752
|
+
current = current.def.in ?? current;
|
|
5608
5753
|
continue;
|
|
5609
5754
|
}
|
|
5610
5755
|
return;
|
|
@@ -5613,22 +5758,22 @@ function readDefaultValue(schema) {
|
|
|
5613
5758
|
}
|
|
5614
5759
|
function readSchemaDescription2(schema) {
|
|
5615
5760
|
let current = schema;
|
|
5616
|
-
while (current?.
|
|
5761
|
+
while (current?.def?.type) {
|
|
5617
5762
|
const desc = current.description;
|
|
5618
5763
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
5619
5764
|
return desc.trim();
|
|
5620
5765
|
}
|
|
5621
|
-
const typeName = current.
|
|
5766
|
+
const typeName = current.def.type;
|
|
5622
5767
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
5623
|
-
current = current.
|
|
5768
|
+
current = current.def.innerType ?? current;
|
|
5624
5769
|
continue;
|
|
5625
5770
|
}
|
|
5626
5771
|
if (typeName === "catch" || typeName === "readonly") {
|
|
5627
|
-
current = current.
|
|
5772
|
+
current = current.def.innerType ?? current;
|
|
5628
5773
|
continue;
|
|
5629
5774
|
}
|
|
5630
5775
|
if (typeName === "pipe") {
|
|
5631
|
-
current = current.
|
|
5776
|
+
current = current.def.in ?? current;
|
|
5632
5777
|
continue;
|
|
5633
5778
|
}
|
|
5634
5779
|
break;
|
|
@@ -5638,19 +5783,19 @@ function readSchemaDescription2(schema) {
|
|
|
5638
5783
|
function unwrap2(schema) {
|
|
5639
5784
|
let current = schema;
|
|
5640
5785
|
let optional = false;
|
|
5641
|
-
while (current?.
|
|
5642
|
-
const typeName = current.
|
|
5786
|
+
while (current?.def?.type) {
|
|
5787
|
+
const typeName = current.def.type;
|
|
5643
5788
|
if (typeName === "optional" || typeName === "default") {
|
|
5644
5789
|
optional = true;
|
|
5645
|
-
current = current.
|
|
5790
|
+
current = current.def.innerType ?? current;
|
|
5646
5791
|
continue;
|
|
5647
5792
|
}
|
|
5648
5793
|
if (typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5649
|
-
current = current.
|
|
5794
|
+
current = current.def.innerType ?? current;
|
|
5650
5795
|
continue;
|
|
5651
5796
|
}
|
|
5652
5797
|
if (typeName === "pipe") {
|
|
5653
|
-
current = current.
|
|
5798
|
+
current = current.def.in ?? current;
|
|
5654
5799
|
continue;
|
|
5655
5800
|
}
|
|
5656
5801
|
break;
|