extrait 0.7.0 → 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/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 +6 -10
- package/dist/generate-tool-timeout.d.ts +3 -0
- package/dist/index.cjs +1010 -1119
- package/dist/index.js +1010 -1119
- package/dist/providers/mcp-runtime-debug.d.ts +3 -0
- package/dist/providers/mcp-runtime.d.ts +1 -1
- package/dist/structured-streaming.d.ts +1 -0
- 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 -468
- package/package.json +3 -3
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;
|
|
@@ -1129,17 +1131,107 @@ 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
|
-
|
|
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) {
|
|
1143
1235
|
let cursor;
|
|
1144
1236
|
do {
|
|
1145
1237
|
const page = await client.listTools(cursor ? { cursor } : undefined);
|
|
@@ -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}`);
|
|
@@ -1781,28 +1833,12 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1781
1833
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1782
1834
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1783
1835
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
1784
|
-
const
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
model: options.model,
|
|
1791
|
-
messages,
|
|
1792
|
-
temperature: request.temperature,
|
|
1793
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1794
|
-
max_tokens: request.maxTokens,
|
|
1795
|
-
tools: transportTools,
|
|
1796
|
-
tool_choice: request.toolChoice,
|
|
1797
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
1798
|
-
})),
|
|
1799
|
-
signal: request.signal
|
|
1800
|
-
});
|
|
1801
|
-
if (!response.ok) {
|
|
1802
|
-
const message = await response.text();
|
|
1803
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1804
|
-
}
|
|
1805
|
-
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
|
+
}));
|
|
1806
1842
|
lastPayload = payload;
|
|
1807
1843
|
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1808
1844
|
finishReason = pickFinishReason(payload);
|
|
@@ -1857,26 +1893,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1857
1893
|
}
|
|
1858
1894
|
async function completeWithResponsesAPIPassThrough(options, fetcher, path, request) {
|
|
1859
1895
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
1860
|
-
const
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
...options.defaultBody,
|
|
1865
|
-
...request.body,
|
|
1866
|
-
model: options.model,
|
|
1867
|
-
input: buildResponsesInput(request),
|
|
1868
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
1869
|
-
temperature: request.temperature,
|
|
1870
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1871
|
-
max_output_tokens: request.maxTokens
|
|
1872
|
-
})),
|
|
1873
|
-
signal: request.signal
|
|
1874
|
-
});
|
|
1875
|
-
if (!response.ok) {
|
|
1876
|
-
const message = await response.text();
|
|
1877
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1878
|
-
}
|
|
1879
|
-
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
|
+
}));
|
|
1880
1900
|
const toolCalls = pickResponsesToolCalls(payload);
|
|
1881
1901
|
return {
|
|
1882
1902
|
text: pickResponsesText(payload) || pickAssistantText(payload),
|
|
@@ -1888,58 +1908,26 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
|
|
|
1888
1908
|
}
|
|
1889
1909
|
async function completeWithResponsesAPIWithMCP(options, fetcher, path, request) {
|
|
1890
1910
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
1891
|
-
|
|
1892
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
1893
|
-
let aggregatedUsage;
|
|
1894
|
-
let finishReason;
|
|
1895
|
-
let lastPayload;
|
|
1896
|
-
const executedToolCalls = [];
|
|
1897
|
-
const toolExecutions = [];
|
|
1898
|
-
const reasoningBlocks = [];
|
|
1911
|
+
const state = createResponsesMCPState(request);
|
|
1899
1912
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1900
1913
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1901
1914
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
1902
|
-
const
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
max_output_tokens: request.maxTokens,
|
|
1914
|
-
tools: transportTools,
|
|
1915
|
-
tool_choice: request.toolChoice,
|
|
1916
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
1917
|
-
})),
|
|
1918
|
-
signal: request.signal
|
|
1919
|
-
});
|
|
1920
|
-
if (!response.ok) {
|
|
1921
|
-
const message = await response.text();
|
|
1922
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1923
|
-
}
|
|
1924
|
-
const payload = await response.json();
|
|
1925
|
-
lastPayload = payload;
|
|
1926
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1927
|
-
finishReason = pickResponsesFinishReason(payload) ?? finishReason;
|
|
1928
|
-
pushReasoningBlock(reasoningBlocks, round, pickResponsesReasoning(payload));
|
|
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));
|
|
1929
1926
|
const providerToolCalls = pickResponsesToolCalls(payload);
|
|
1930
1927
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
1931
1928
|
if (functionCalls.length === 0) {
|
|
1932
1929
|
const text = pickResponsesText(payload) || pickAssistantText(payload);
|
|
1933
|
-
return
|
|
1934
|
-
text,
|
|
1935
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1936
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1937
|
-
raw: payload,
|
|
1938
|
-
usage: aggregatedUsage,
|
|
1939
|
-
finishReason,
|
|
1940
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
1941
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
1942
|
-
};
|
|
1930
|
+
return buildResponsesMCPResult(state, text, payload);
|
|
1943
1931
|
}
|
|
1944
1932
|
if (round > maxToolRounds) {
|
|
1945
1933
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
@@ -1950,25 +1938,16 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1950
1938
|
provider: "openai-compatible",
|
|
1951
1939
|
model: options.model
|
|
1952
1940
|
});
|
|
1953
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
1954
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
1955
|
-
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) => ({
|
|
1956
1944
|
type: "function_call_output",
|
|
1957
1945
|
call_id: entry.call.id,
|
|
1958
1946
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
1959
1947
|
}));
|
|
1960
|
-
previousResponseId = pickString(payload.id);
|
|
1948
|
+
state.previousResponseId = pickString(payload.id);
|
|
1961
1949
|
}
|
|
1962
|
-
return {
|
|
1963
|
-
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
1964
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1965
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1966
|
-
raw: lastPayload,
|
|
1967
|
-
usage: aggregatedUsage,
|
|
1968
|
-
finishReason,
|
|
1969
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
1970
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
1971
|
-
};
|
|
1950
|
+
return buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
1972
1951
|
}
|
|
1973
1952
|
async function streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks) {
|
|
1974
1953
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
@@ -1984,24 +1963,13 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
1984
1963
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1985
1964
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1986
1965
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
1987
|
-
const response = await
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
messages,
|
|
1995
|
-
temperature: request.temperature,
|
|
1996
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1997
|
-
max_tokens: request.maxTokens,
|
|
1998
|
-
tools: transportTools,
|
|
1999
|
-
tool_choice: request.toolChoice,
|
|
2000
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
2001
|
-
stream: true
|
|
2002
|
-
})),
|
|
2003
|
-
signal: request.signal
|
|
2004
|
-
});
|
|
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
|
+
}));
|
|
2005
1973
|
if (!response.ok) {
|
|
2006
1974
|
const message = await response.text();
|
|
2007
1975
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2038,17 +2006,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2038
2006
|
roundReasoning += reasoningDelta;
|
|
2039
2007
|
reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
|
|
2040
2008
|
}
|
|
2041
|
-
|
|
2042
|
-
const chunk = {
|
|
2043
|
-
textDelta: delta,
|
|
2044
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
2045
|
-
turnIndex: round,
|
|
2046
|
-
raw: json,
|
|
2047
|
-
usage: chunkUsage,
|
|
2048
|
-
finishReason: chunkFinishReason
|
|
2049
|
-
};
|
|
2050
|
-
callbacks.onChunk?.(chunk);
|
|
2051
|
-
}
|
|
2009
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2052
2010
|
});
|
|
2053
2011
|
aggregatedUsage = mergeUsage(aggregatedUsage, roundUsage);
|
|
2054
2012
|
if (roundFinishReason) {
|
|
@@ -2127,22 +2085,11 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2127
2085
|
}
|
|
2128
2086
|
async function streamWithResponsesAPIPassThrough(options, fetcher, path, request, callbacks) {
|
|
2129
2087
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
2130
|
-
const response = await
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
...request.body,
|
|
2136
|
-
model: options.model,
|
|
2137
|
-
input: buildResponsesInput(request),
|
|
2138
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
2139
|
-
temperature: request.temperature,
|
|
2140
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2141
|
-
max_output_tokens: request.maxTokens,
|
|
2142
|
-
stream: true
|
|
2143
|
-
})),
|
|
2144
|
-
signal: request.signal
|
|
2145
|
-
});
|
|
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
|
+
}));
|
|
2146
2093
|
if (!response.ok) {
|
|
2147
2094
|
const message = await response.text();
|
|
2148
2095
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2175,15 +2122,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2175
2122
|
text += delta;
|
|
2176
2123
|
callbacks.onToken?.(delta);
|
|
2177
2124
|
}
|
|
2178
|
-
|
|
2179
|
-
const chunk = {
|
|
2180
|
-
textDelta: delta,
|
|
2181
|
-
raw: json,
|
|
2182
|
-
usage: chunkUsage,
|
|
2183
|
-
finishReason: chunkFinishReason
|
|
2184
|
-
};
|
|
2185
|
-
callbacks.onChunk?.(chunk);
|
|
2186
|
-
}
|
|
2125
|
+
emitOpenAIStreamChunk(callbacks, undefined, json, delta, "", chunkUsage, chunkFinishReason);
|
|
2187
2126
|
});
|
|
2188
2127
|
const finalPayload = lastPayload ?? {};
|
|
2189
2128
|
const out = {
|
|
@@ -2197,37 +2136,19 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2197
2136
|
}
|
|
2198
2137
|
async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, callbacks) {
|
|
2199
2138
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
2200
|
-
|
|
2201
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
2202
|
-
let aggregatedUsage;
|
|
2203
|
-
let finishReason;
|
|
2204
|
-
let lastPayload;
|
|
2205
|
-
const executedToolCalls = [];
|
|
2206
|
-
const toolExecutions = [];
|
|
2207
|
-
const reasoningBlocks = [];
|
|
2139
|
+
const state = createResponsesMCPState(request);
|
|
2208
2140
|
callbacks.onStart?.();
|
|
2209
2141
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2210
2142
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2211
2143
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
2212
|
-
const response = await
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
previous_response_id: previousResponseId,
|
|
2221
|
-
temperature: request.temperature,
|
|
2222
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2223
|
-
max_output_tokens: request.maxTokens,
|
|
2224
|
-
tools: transportTools,
|
|
2225
|
-
tool_choice: request.toolChoice,
|
|
2226
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
2227
|
-
stream: true
|
|
2228
|
-
})),
|
|
2229
|
-
signal: request.signal
|
|
2230
|
-
});
|
|
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
|
+
}));
|
|
2231
2152
|
if (!response.ok) {
|
|
2232
2153
|
const message = await response.text();
|
|
2233
2154
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2249,7 +2170,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2249
2170
|
const payload = pickResponsesStreamPayload(json);
|
|
2250
2171
|
if (payload) {
|
|
2251
2172
|
roundPayload = payload;
|
|
2252
|
-
lastPayload = payload;
|
|
2173
|
+
state.lastPayload = payload;
|
|
2253
2174
|
}
|
|
2254
2175
|
const delta = pickResponsesStreamTextDelta(json);
|
|
2255
2176
|
const reasoningDelta = pickResponsesStreamReasoningDelta(json);
|
|
@@ -2267,24 +2188,14 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2267
2188
|
if (reasoningDelta) {
|
|
2268
2189
|
roundReasoning += reasoningDelta;
|
|
2269
2190
|
}
|
|
2270
|
-
|
|
2271
|
-
const chunk = {
|
|
2272
|
-
textDelta: delta,
|
|
2273
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
2274
|
-
turnIndex: round,
|
|
2275
|
-
raw: json,
|
|
2276
|
-
usage: chunkUsage,
|
|
2277
|
-
finishReason: chunkFinishReason
|
|
2278
|
-
};
|
|
2279
|
-
callbacks.onChunk?.(chunk);
|
|
2280
|
-
}
|
|
2191
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2281
2192
|
});
|
|
2282
2193
|
const resolvedRoundUsage = preferLatestUsage(roundUsage, roundPayload ? pickUsage(roundPayload) : undefined);
|
|
2283
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, resolvedRoundUsage);
|
|
2194
|
+
state.aggregatedUsage = mergeUsage(state.aggregatedUsage, resolvedRoundUsage);
|
|
2284
2195
|
if (roundFinishReason) {
|
|
2285
|
-
finishReason = roundFinishReason;
|
|
2196
|
+
state.finishReason = roundFinishReason;
|
|
2286
2197
|
} else if (roundPayload) {
|
|
2287
|
-
finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
|
|
2198
|
+
state.finishReason = pickResponsesFinishReason(roundPayload) ?? state.finishReason;
|
|
2288
2199
|
}
|
|
2289
2200
|
const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
|
|
2290
2201
|
if (roundPayload && roundReasoning.length === 0) {
|
|
@@ -2293,7 +2204,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2293
2204
|
const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
|
|
2294
2205
|
const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
|
|
2295
2206
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2296
|
-
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2207
|
+
pushReasoningBlock(state.reasoningBlocks, round, roundReasoning);
|
|
2297
2208
|
request.onTurnTransition?.({
|
|
2298
2209
|
turnIndex: round,
|
|
2299
2210
|
kind: "reasoningComplete",
|
|
@@ -2301,16 +2212,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2301
2212
|
});
|
|
2302
2213
|
if (functionCalls.length === 0) {
|
|
2303
2214
|
const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
|
|
2304
|
-
const out2 =
|
|
2305
|
-
text: finalText,
|
|
2306
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2307
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2308
|
-
raw: roundPayload ?? lastPayload,
|
|
2309
|
-
usage: aggregatedUsage,
|
|
2310
|
-
finishReason,
|
|
2311
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2312
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2313
|
-
};
|
|
2215
|
+
const out2 = buildResponsesMCPResult(state, finalText, roundPayload ?? state.lastPayload);
|
|
2314
2216
|
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2315
2217
|
callbacks.onComplete?.(out2);
|
|
2316
2218
|
return out2;
|
|
@@ -2335,26 +2237,17 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2335
2237
|
provider: "openai-compatible",
|
|
2336
2238
|
model: options.model
|
|
2337
2239
|
});
|
|
2338
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2339
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2240
|
+
state.executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2241
|
+
state.toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2340
2242
|
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2341
|
-
input = outputs.map((entry) => ({
|
|
2243
|
+
state.input = outputs.map((entry) => ({
|
|
2342
2244
|
type: "function_call_output",
|
|
2343
2245
|
call_id: entry.call.id,
|
|
2344
2246
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
2345
2247
|
}));
|
|
2346
|
-
previousResponseId = pickString(roundPayload?.id);
|
|
2248
|
+
state.previousResponseId = pickString(roundPayload?.id);
|
|
2347
2249
|
}
|
|
2348
|
-
const out = {
|
|
2349
|
-
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
2350
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2351
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2352
|
-
raw: lastPayload,
|
|
2353
|
-
usage: aggregatedUsage,
|
|
2354
|
-
finishReason,
|
|
2355
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2356
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2357
|
-
};
|
|
2250
|
+
const out = buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
2358
2251
|
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2359
2252
|
callbacks.onComplete?.(out);
|
|
2360
2253
|
return out;
|
|
@@ -2507,7 +2400,7 @@ function pickAssistantDelta(payload) {
|
|
|
2507
2400
|
if (!isRecord2(delta)) {
|
|
2508
2401
|
return "";
|
|
2509
2402
|
}
|
|
2510
|
-
return
|
|
2403
|
+
return pickTextLike(delta.content);
|
|
2511
2404
|
}
|
|
2512
2405
|
function pickAssistantReasoning(payload) {
|
|
2513
2406
|
const message = pickAssistantMessage(payload);
|
|
@@ -2815,7 +2708,7 @@ function pickResponsesReasoning(payload) {
|
|
|
2815
2708
|
function pickAssistantText(payload) {
|
|
2816
2709
|
const message = pickAssistantMessage(payload);
|
|
2817
2710
|
if (message) {
|
|
2818
|
-
const text =
|
|
2711
|
+
const text = pickTextLike(message.content);
|
|
2819
2712
|
if (text.length > 0) {
|
|
2820
2713
|
return text;
|
|
2821
2714
|
}
|
|
@@ -2844,9 +2737,6 @@ function joinReasoningBlocks(blocks) {
|
|
|
2844
2737
|
|
|
2845
2738
|
`);
|
|
2846
2739
|
}
|
|
2847
|
-
function pickTextFromOpenAIContent(value) {
|
|
2848
|
-
return pickTextLike(value);
|
|
2849
|
-
}
|
|
2850
2740
|
function pickTextLike(value) {
|
|
2851
2741
|
if (typeof value === "string") {
|
|
2852
2742
|
return value;
|
|
@@ -2920,83 +2810,81 @@ function createAnthropicCompatibleAdapter(options) {
|
|
|
2920
2810
|
if (hasMCPClients(request.mcpClients)) {
|
|
2921
2811
|
return streamWithMCPToolLoop(options, fetcher, path, request, callbacks);
|
|
2922
2812
|
}
|
|
2923
|
-
|
|
2924
|
-
const response = await fetcher(buildURL(options.baseURL, path), {
|
|
2925
|
-
method: "POST",
|
|
2926
|
-
headers: buildHeaders2(options),
|
|
2927
|
-
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
2928
|
-
...options.defaultBody,
|
|
2929
|
-
...request.body,
|
|
2930
|
-
model: options.model,
|
|
2931
|
-
system: input.systemPrompt,
|
|
2932
|
-
messages: input.messages,
|
|
2933
|
-
temperature: request.temperature,
|
|
2934
|
-
stream: true
|
|
2935
|
-
})),
|
|
2936
|
-
signal: request.signal
|
|
2937
|
-
});
|
|
2938
|
-
if (!response.ok) {
|
|
2939
|
-
const message = await response.text();
|
|
2940
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2941
|
-
}
|
|
2942
|
-
callbacks.onStart?.();
|
|
2943
|
-
let text = "";
|
|
2944
|
-
let usage;
|
|
2945
|
-
let finishReason;
|
|
2946
|
-
await consumeSSE(response, (data) => {
|
|
2947
|
-
if (data === "[DONE]") {
|
|
2948
|
-
return;
|
|
2949
|
-
}
|
|
2950
|
-
const json = safeJSONParse(data);
|
|
2951
|
-
if (!isRecord2(json)) {
|
|
2952
|
-
return;
|
|
2953
|
-
}
|
|
2954
|
-
const delta = pickAnthropicDelta(json);
|
|
2955
|
-
const chunkUsage = pickUsage2(json);
|
|
2956
|
-
const chunkFinishReason = pickFinishReason2(json);
|
|
2957
|
-
usage = preferLatestUsage(usage, chunkUsage);
|
|
2958
|
-
if (chunkFinishReason) {
|
|
2959
|
-
finishReason = chunkFinishReason;
|
|
2960
|
-
}
|
|
2961
|
-
if (delta) {
|
|
2962
|
-
text += delta;
|
|
2963
|
-
callbacks.onToken?.(delta);
|
|
2964
|
-
}
|
|
2965
|
-
if (delta || chunkUsage || chunkFinishReason) {
|
|
2966
|
-
const chunk = {
|
|
2967
|
-
textDelta: delta,
|
|
2968
|
-
raw: json,
|
|
2969
|
-
usage: chunkUsage,
|
|
2970
|
-
finishReason: chunkFinishReason
|
|
2971
|
-
};
|
|
2972
|
-
callbacks.onChunk?.(chunk);
|
|
2973
|
-
}
|
|
2974
|
-
});
|
|
2975
|
-
const out = { text, usage, finishReason };
|
|
2976
|
-
callbacks.onComplete?.(out);
|
|
2977
|
-
return out;
|
|
2813
|
+
return streamPassThrough(options, fetcher, path, request, callbacks);
|
|
2978
2814
|
},
|
|
2979
2815
|
async embed() {
|
|
2980
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.");
|
|
2981
2817
|
}
|
|
2982
2818
|
};
|
|
2983
2819
|
}
|
|
2984
|
-
async function
|
|
2820
|
+
async function streamPassThrough(options, fetcher, path, request, callbacks) {
|
|
2985
2821
|
const input = resolveAnthropicInput(request);
|
|
2986
|
-
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), {
|
|
2987
2869
|
method: "POST",
|
|
2988
2870
|
headers: buildHeaders2(options),
|
|
2989
2871
|
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
2990
2872
|
...options.defaultBody,
|
|
2991
2873
|
...request.body,
|
|
2992
2874
|
model: options.model,
|
|
2993
|
-
system: input.systemPrompt,
|
|
2994
|
-
messages: input.messages,
|
|
2995
2875
|
temperature: request.temperature,
|
|
2996
|
-
|
|
2876
|
+
...body
|
|
2997
2877
|
})),
|
|
2998
2878
|
signal: request.signal
|
|
2999
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
|
+
});
|
|
3000
2888
|
if (!response.ok) {
|
|
3001
2889
|
const message = await response.text();
|
|
3002
2890
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -3028,21 +2916,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
3028
2916
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
3029
2917
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
3030
2918
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
3031
|
-
const response = await
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
model: options.model,
|
|
3038
|
-
system: input.systemPrompt,
|
|
3039
|
-
messages,
|
|
3040
|
-
temperature: request.temperature,
|
|
3041
|
-
tools,
|
|
3042
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3043
|
-
stream: false
|
|
3044
|
-
})),
|
|
3045
|
-
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
|
|
3046
2925
|
});
|
|
3047
2926
|
if (!response.ok) {
|
|
3048
2927
|
const message = await response.text();
|
|
@@ -3118,21 +2997,12 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3118
2997
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
3119
2998
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
3120
2999
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
3121
|
-
const response = await
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
model: options.model,
|
|
3128
|
-
system: input.systemPrompt,
|
|
3129
|
-
messages,
|
|
3130
|
-
temperature: request.temperature,
|
|
3131
|
-
tools,
|
|
3132
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3133
|
-
stream: true
|
|
3134
|
-
})),
|
|
3135
|
-
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
|
|
3136
3006
|
});
|
|
3137
3007
|
if (!response.ok) {
|
|
3138
3008
|
const message = await response.text();
|
|
@@ -3676,39 +3546,10 @@ function buildProviderOptions(config) {
|
|
|
3676
3546
|
return {
|
|
3677
3547
|
model: config.model,
|
|
3678
3548
|
...transport,
|
|
3679
|
-
...config.options
|
|
3549
|
+
...config.options
|
|
3680
3550
|
};
|
|
3681
3551
|
}
|
|
3682
3552
|
|
|
3683
|
-
// src/utils/debug-colors.ts
|
|
3684
|
-
var ANSI = {
|
|
3685
|
-
reset: "\x1B[0m",
|
|
3686
|
-
bold: "\x1B[1m",
|
|
3687
|
-
cyan: "\x1B[36m",
|
|
3688
|
-
yellow: "\x1B[33m",
|
|
3689
|
-
green: "\x1B[32m",
|
|
3690
|
-
red: "\x1B[31m",
|
|
3691
|
-
dim: "\x1B[2m"
|
|
3692
|
-
};
|
|
3693
|
-
function color(config, text, tone) {
|
|
3694
|
-
if (!config.colors) {
|
|
3695
|
-
return text;
|
|
3696
|
-
}
|
|
3697
|
-
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
3698
|
-
}
|
|
3699
|
-
function dim(config, text) {
|
|
3700
|
-
if (!config.colors) {
|
|
3701
|
-
return text;
|
|
3702
|
-
}
|
|
3703
|
-
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
3704
|
-
}
|
|
3705
|
-
function title(config, text) {
|
|
3706
|
-
if (!config.colors) {
|
|
3707
|
-
return text;
|
|
3708
|
-
}
|
|
3709
|
-
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3710
|
-
}
|
|
3711
|
-
|
|
3712
3553
|
// src/outdent.ts
|
|
3713
3554
|
var DEFAULT_OPTIONS = {
|
|
3714
3555
|
trimLeadingNewline: true,
|
|
@@ -3846,132 +3687,213 @@ function createOutdent(options = {}) {
|
|
|
3846
3687
|
return outdent;
|
|
3847
3688
|
}
|
|
3848
3689
|
|
|
3849
|
-
// src/generate-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
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
|
|
3856
3713
|
var RE_THINK_TAGS = /<\/?think\s*>/gi;
|
|
3857
|
-
function
|
|
3858
|
-
const
|
|
3859
|
-
|
|
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
|
+
};
|
|
3860
3728
|
}
|
|
3861
|
-
function
|
|
3862
|
-
if (
|
|
3863
|
-
return
|
|
3864
|
-
prompt: value
|
|
3865
|
-
};
|
|
3866
|
-
}
|
|
3867
|
-
if (isPromptResolver(value)) {
|
|
3868
|
-
return normalizePromptPayload(value.resolvePrompt(_context));
|
|
3729
|
+
function normalizeReasoningBlocks(blocks) {
|
|
3730
|
+
if (!Array.isArray(blocks)) {
|
|
3731
|
+
return;
|
|
3869
3732
|
}
|
|
3870
|
-
|
|
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;
|
|
3871
3738
|
}
|
|
3872
|
-
function
|
|
3873
|
-
const
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
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;
|
|
3877
3743
|
}
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
|
|
3881
|
-
messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
|
|
3882
|
-
};
|
|
3744
|
+
const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
|
|
3745
|
+
return normalizeReasoningBlocks(next);
|
|
3883
3746
|
}
|
|
3884
|
-
function
|
|
3885
|
-
if (
|
|
3886
|
-
return
|
|
3747
|
+
function composeParseSource(text, reasoning) {
|
|
3748
|
+
if (typeof reasoning !== "string" || reasoning.length === 0) {
|
|
3749
|
+
return text;
|
|
3887
3750
|
}
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
|
|
3894
|
-
}))
|
|
3895
|
-
};
|
|
3751
|
+
const sanitized = reasoning.replace(RE_THINK_TAGS, "");
|
|
3752
|
+
if (sanitized.length === 0) {
|
|
3753
|
+
return text;
|
|
3754
|
+
}
|
|
3755
|
+
return `<think>${sanitized}</think>${text}`;
|
|
3896
3756
|
}
|
|
3897
|
-
function
|
|
3898
|
-
|
|
3899
|
-
|
|
3757
|
+
function aggregateUsage(attempts) {
|
|
3758
|
+
let usage;
|
|
3759
|
+
for (const attempt of attempts) {
|
|
3760
|
+
usage = mergeUsage2(usage, attempt.usage);
|
|
3900
3761
|
}
|
|
3901
|
-
return
|
|
3762
|
+
return usage;
|
|
3902
3763
|
}
|
|
3903
|
-
function
|
|
3904
|
-
|
|
3905
|
-
if (prompts.length === 0) {
|
|
3764
|
+
function mergeUsage2(base, next) {
|
|
3765
|
+
if (!base && !next) {
|
|
3906
3766
|
return;
|
|
3907
3767
|
}
|
|
3908
|
-
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(`
|
|
3909
3777
|
|
|
3910
3778
|
`);
|
|
3911
3779
|
}
|
|
3912
|
-
function
|
|
3913
|
-
if (
|
|
3914
|
-
return
|
|
3915
|
-
enabled: option
|
|
3916
|
-
};
|
|
3780
|
+
function stripThinkBlocks(text, thinkBlocks) {
|
|
3781
|
+
if (thinkBlocks.length === 0) {
|
|
3782
|
+
return text;
|
|
3917
3783
|
}
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3784
|
+
let output = "";
|
|
3785
|
+
let cursor = 0;
|
|
3786
|
+
for (const block of thinkBlocks) {
|
|
3787
|
+
output += text.slice(cursor, block.start);
|
|
3788
|
+
cursor = block.end;
|
|
3922
3789
|
}
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
onData: option.onData,
|
|
3926
|
-
onTurnTransition: option.onTurnTransition,
|
|
3927
|
-
to: option.to
|
|
3928
|
-
};
|
|
3790
|
+
output += text.slice(cursor);
|
|
3791
|
+
return output;
|
|
3929
3792
|
}
|
|
3930
|
-
function
|
|
3931
|
-
|
|
3932
|
-
return
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
verbose: false,
|
|
3936
|
-
logger: (line) => console.log(line)
|
|
3937
|
-
};
|
|
3793
|
+
function toStreamDataFingerprint(value) {
|
|
3794
|
+
try {
|
|
3795
|
+
return JSON.stringify(value);
|
|
3796
|
+
} catch {
|
|
3797
|
+
return "__unserializable__";
|
|
3938
3798
|
}
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
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;
|
|
3946
3814
|
}
|
|
3947
|
-
return {
|
|
3948
|
-
enabled: option.enabled ?? true,
|
|
3949
|
-
colors: option.colors ?? true,
|
|
3950
|
-
verbose: option.verbose ?? false,
|
|
3951
|
-
logger: option.logger ?? ((line) => console.log(line))
|
|
3952
|
-
};
|
|
3815
|
+
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
3953
3816
|
}
|
|
3954
|
-
function
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
async callTool(params) {
|
|
3960
|
-
let timeoutId;
|
|
3961
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
3962
|
-
timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
|
|
3963
|
-
});
|
|
3964
|
-
try {
|
|
3965
|
-
return await Promise.race([client.callTool(params), timeoutPromise]);
|
|
3966
|
-
} finally {
|
|
3967
|
-
clearTimeout(timeoutId);
|
|
3968
|
-
}
|
|
3969
|
-
}
|
|
3970
|
-
};
|
|
3817
|
+
function dim(config, text) {
|
|
3818
|
+
if (!config.colors) {
|
|
3819
|
+
return text;
|
|
3820
|
+
}
|
|
3821
|
+
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
3971
3822
|
}
|
|
3972
|
-
function
|
|
3973
|
-
|
|
3823
|
+
function title(config, text) {
|
|
3824
|
+
if (!config.colors) {
|
|
3825
|
+
return text;
|
|
3826
|
+
}
|
|
3827
|
+
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3974
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
|
+
`));
|
|
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
|
|
3975
3897
|
async function callModel(adapter, options) {
|
|
3976
3898
|
const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
|
|
3977
3899
|
const requestPayload = {
|
|
@@ -4191,67 +4113,115 @@ async function callModel(adapter, options) {
|
|
|
4191
4113
|
reasoningBlocks: normalized.reasoningBlocks
|
|
4192
4114
|
};
|
|
4193
4115
|
}
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
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
|
+
}
|
|
4201
4145
|
return {
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
thinkBlocks: sanitized.thinkBlocks,
|
|
4206
|
-
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
|
|
4207
4149
|
};
|
|
4208
4150
|
}
|
|
4209
|
-
function
|
|
4210
|
-
if (!
|
|
4211
|
-
return;
|
|
4151
|
+
function applyPromptOutdent(payload, enabled) {
|
|
4152
|
+
if (!enabled) {
|
|
4153
|
+
return payload;
|
|
4212
4154
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
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
|
+
};
|
|
4218
4163
|
}
|
|
4219
|
-
function
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
return blocks;
|
|
4164
|
+
function applyOutdentToOptionalPrompt(value, enabled) {
|
|
4165
|
+
if (!enabled || typeof value !== "string") {
|
|
4166
|
+
return value;
|
|
4223
4167
|
}
|
|
4224
|
-
|
|
4225
|
-
return normalizeReasoningBlocks(next);
|
|
4168
|
+
return sharedOutdent.string(value);
|
|
4226
4169
|
}
|
|
4227
|
-
function
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
const sanitized = reasoning.replace(RE_THINK_TAGS, "");
|
|
4232
|
-
if (sanitized.length === 0) {
|
|
4233
|
-
return text;
|
|
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;
|
|
4234
4174
|
}
|
|
4235
|
-
return
|
|
4175
|
+
return prompts.join(`
|
|
4176
|
+
|
|
4177
|
+
`);
|
|
4236
4178
|
}
|
|
4237
|
-
function
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
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
|
+
};
|
|
4241
4189
|
}
|
|
4242
|
-
return
|
|
4190
|
+
return {
|
|
4191
|
+
enabled: option.enabled ?? true,
|
|
4192
|
+
onData: option.onData,
|
|
4193
|
+
onTurnTransition: option.onTurnTransition,
|
|
4194
|
+
to: option.to
|
|
4195
|
+
};
|
|
4243
4196
|
}
|
|
4244
|
-
function
|
|
4245
|
-
if (
|
|
4246
|
-
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
|
+
};
|
|
4247
4213
|
}
|
|
4248
4214
|
return {
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4215
|
+
enabled: option.enabled ?? true,
|
|
4216
|
+
colors: option.colors ?? true,
|
|
4217
|
+
verbose: option.verbose ?? false,
|
|
4218
|
+
logger: option.logger ?? defaultDebugLogger
|
|
4253
4219
|
};
|
|
4254
4220
|
}
|
|
4221
|
+
function defaultDebugLogger(line) {
|
|
4222
|
+
const { log } = globalThis.console;
|
|
4223
|
+
log(line);
|
|
4224
|
+
}
|
|
4255
4225
|
function isPromptResolver(value) {
|
|
4256
4226
|
return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
|
|
4257
4227
|
}
|
|
@@ -4265,95 +4235,6 @@ function isLLMMessage(value) {
|
|
|
4265
4235
|
}
|
|
4266
4236
|
return "content" in candidate;
|
|
4267
4237
|
}
|
|
4268
|
-
function joinReasoningSegments(parts) {
|
|
4269
|
-
return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
|
|
4270
|
-
|
|
4271
|
-
`);
|
|
4272
|
-
}
|
|
4273
|
-
function stripThinkBlocks(text, thinkBlocks) {
|
|
4274
|
-
if (thinkBlocks.length === 0) {
|
|
4275
|
-
return text;
|
|
4276
|
-
}
|
|
4277
|
-
let output = "";
|
|
4278
|
-
let cursor = 0;
|
|
4279
|
-
for (const block of thinkBlocks) {
|
|
4280
|
-
output += text.slice(cursor, block.start);
|
|
4281
|
-
cursor = block.end;
|
|
4282
|
-
}
|
|
4283
|
-
output += text.slice(cursor);
|
|
4284
|
-
return output;
|
|
4285
|
-
}
|
|
4286
|
-
function toStreamDataFingerprint(value) {
|
|
4287
|
-
try {
|
|
4288
|
-
return JSON.stringify(value);
|
|
4289
|
-
} catch {
|
|
4290
|
-
return "__unserializable__";
|
|
4291
|
-
}
|
|
4292
|
-
}
|
|
4293
|
-
function emitDebugRequest(config, input) {
|
|
4294
|
-
const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
|
|
4295
|
-
const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
|
|
4296
|
-
const lines = [
|
|
4297
|
-
color(config, title(config, [
|
|
4298
|
-
`[${input.label}][request]`,
|
|
4299
|
-
`attempt=${input.attempt}`,
|
|
4300
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4301
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4302
|
-
].join(" ")), "cyan"),
|
|
4303
|
-
dim(config, [
|
|
4304
|
-
`provider=${input.provider ?? "unknown"}`,
|
|
4305
|
-
`model=${input.model ?? "unknown"}`,
|
|
4306
|
-
`stream=${input.stream}`
|
|
4307
|
-
].join(" ")),
|
|
4308
|
-
color(config, "prompt:", "yellow"),
|
|
4309
|
-
input.requestPayload.prompt ?? "(none)",
|
|
4310
|
-
color(config, "messages:", "yellow"),
|
|
4311
|
-
requestMessages,
|
|
4312
|
-
color(config, "systemPrompt:", "yellow"),
|
|
4313
|
-
input.requestPayload.systemPrompt ?? "(none)",
|
|
4314
|
-
color(config, "request.body:", "yellow"),
|
|
4315
|
-
requestBody
|
|
4316
|
-
];
|
|
4317
|
-
emitDebug(config, lines.join(`
|
|
4318
|
-
`));
|
|
4319
|
-
}
|
|
4320
|
-
function emitDebugResponse(config, input) {
|
|
4321
|
-
const text = input.text.length > 0 ? input.text : "(none)";
|
|
4322
|
-
const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
|
|
4323
|
-
const metadata = [
|
|
4324
|
-
`via=${input.via}`,
|
|
4325
|
-
`textChars=${input.text.length}`,
|
|
4326
|
-
`reasoningChars=${input.reasoning.length}`
|
|
4327
|
-
];
|
|
4328
|
-
if (config.verbose) {
|
|
4329
|
-
metadata.push(`parseSourceChars=${input.parseSource.length}`);
|
|
4330
|
-
}
|
|
4331
|
-
metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
|
|
4332
|
-
const lines = [
|
|
4333
|
-
color(config, title(config, [
|
|
4334
|
-
`[${input.label}][response]`,
|
|
4335
|
-
`attempt=${input.attempt}`,
|
|
4336
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4337
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4338
|
-
].join(" ")), "green"),
|
|
4339
|
-
dim(config, metadata.join(" ")),
|
|
4340
|
-
color(config, "text:", "yellow"),
|
|
4341
|
-
text,
|
|
4342
|
-
color(config, "reasoning:", "yellow"),
|
|
4343
|
-
reasoning
|
|
4344
|
-
];
|
|
4345
|
-
if (config.verbose) {
|
|
4346
|
-
lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
|
|
4347
|
-
}
|
|
4348
|
-
emitDebug(config, lines.join(`
|
|
4349
|
-
`));
|
|
4350
|
-
}
|
|
4351
|
-
function emitDebug(config, message) {
|
|
4352
|
-
if (!config.enabled) {
|
|
4353
|
-
return;
|
|
4354
|
-
}
|
|
4355
|
-
config.logger(message);
|
|
4356
|
-
}
|
|
4357
4238
|
|
|
4358
4239
|
// src/generate.ts
|
|
4359
4240
|
async function generate(adapter, promptOrOptions, callOptions) {
|
|
@@ -4429,7 +4310,7 @@ function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
|
4429
4310
|
throw new Error("Missing prompt in generate(adapter, prompt, options?) call.");
|
|
4430
4311
|
}
|
|
4431
4312
|
return {
|
|
4432
|
-
...callOptions
|
|
4313
|
+
...callOptions,
|
|
4433
4314
|
prompt: promptOrOptions
|
|
4434
4315
|
};
|
|
4435
4316
|
}
|
|
@@ -4455,9 +4336,6 @@ function prepareGeneratePromptPayload(payload, systemPrompt) {
|
|
|
4455
4336
|
};
|
|
4456
4337
|
}
|
|
4457
4338
|
|
|
4458
|
-
// src/structured.ts
|
|
4459
|
-
import { jsonrepair as jsonrepair3 } from "jsonrepair";
|
|
4460
|
-
|
|
4461
4339
|
// src/parse.ts
|
|
4462
4340
|
import { jsonrepair as jsonrepair2 } from "jsonrepair";
|
|
4463
4341
|
function parseLLMOutput(output, schema, options = {}) {
|
|
@@ -4757,6 +4635,80 @@ function formatZodIssues(issues) {
|
|
|
4757
4635
|
`);
|
|
4758
4636
|
}
|
|
4759
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
|
+
|
|
4760
4712
|
// src/structured.ts
|
|
4761
4713
|
class StructuredParseError extends Error {
|
|
4762
4714
|
name = "StructuredParseError";
|
|
@@ -4994,7 +4946,7 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
|
|
|
4994
4946
|
throw new Error("Missing prompt in structured(adapter, schema, prompt, options?) call.");
|
|
4995
4947
|
}
|
|
4996
4948
|
return {
|
|
4997
|
-
...callOptions
|
|
4949
|
+
...callOptions,
|
|
4998
4950
|
schema: schemaOrOptions,
|
|
4999
4951
|
prompt: promptInput
|
|
5000
4952
|
};
|
|
@@ -5265,67 +5217,6 @@ async function callModel2(adapter, options) {
|
|
|
5265
5217
|
debugLabel: "structured"
|
|
5266
5218
|
});
|
|
5267
5219
|
}
|
|
5268
|
-
function parseStreamingStructuredData(parseSource) {
|
|
5269
|
-
const sanitized = sanitizeThink(parseSource);
|
|
5270
|
-
const start = findFirstJsonRootStart(sanitized.visibleText);
|
|
5271
|
-
if (start < 0) {
|
|
5272
|
-
return null;
|
|
5273
|
-
}
|
|
5274
|
-
const candidate = sanitized.visibleText.slice(start).trim();
|
|
5275
|
-
if (!candidate) {
|
|
5276
|
-
return null;
|
|
5277
|
-
}
|
|
5278
|
-
try {
|
|
5279
|
-
const repaired = jsonrepair3(candidate);
|
|
5280
|
-
const parsed = JSON.parse(repaired);
|
|
5281
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
5282
|
-
return null;
|
|
5283
|
-
}
|
|
5284
|
-
return parsed;
|
|
5285
|
-
} catch {
|
|
5286
|
-
return null;
|
|
5287
|
-
}
|
|
5288
|
-
}
|
|
5289
|
-
function findFirstJsonRootStart(input) {
|
|
5290
|
-
let inString = false;
|
|
5291
|
-
let escaped = false;
|
|
5292
|
-
for (let index = 0;index < input.length; index += 1) {
|
|
5293
|
-
const char = input[index];
|
|
5294
|
-
if (!char) {
|
|
5295
|
-
continue;
|
|
5296
|
-
}
|
|
5297
|
-
if (inString) {
|
|
5298
|
-
if (escaped) {
|
|
5299
|
-
escaped = false;
|
|
5300
|
-
continue;
|
|
5301
|
-
}
|
|
5302
|
-
if (char === "\\") {
|
|
5303
|
-
escaped = true;
|
|
5304
|
-
continue;
|
|
5305
|
-
}
|
|
5306
|
-
if (char === '"') {
|
|
5307
|
-
inString = false;
|
|
5308
|
-
}
|
|
5309
|
-
continue;
|
|
5310
|
-
}
|
|
5311
|
-
if (char === '"') {
|
|
5312
|
-
inString = true;
|
|
5313
|
-
continue;
|
|
5314
|
-
}
|
|
5315
|
-
if (char === "{" || char === "[") {
|
|
5316
|
-
return index;
|
|
5317
|
-
}
|
|
5318
|
-
}
|
|
5319
|
-
const objectStart = input.indexOf("{");
|
|
5320
|
-
const arrayStart = input.indexOf("[");
|
|
5321
|
-
if (objectStart < 0) {
|
|
5322
|
-
return arrayStart;
|
|
5323
|
-
}
|
|
5324
|
-
if (arrayStart < 0) {
|
|
5325
|
-
return objectStart;
|
|
5326
|
-
}
|
|
5327
|
-
return Math.min(objectStart, arrayStart);
|
|
5328
|
-
}
|
|
5329
5220
|
function parseWithObserve(output, schema, parseOptions, context) {
|
|
5330
5221
|
const userParseTrace = parseOptions.onTrace;
|
|
5331
5222
|
return parseLLMOutput(output, schema, {
|
|
@@ -5428,12 +5319,12 @@ function mergeStructuredOptions(defaults, overrides) {
|
|
|
5428
5319
|
...defaults,
|
|
5429
5320
|
...overrides,
|
|
5430
5321
|
parse: {
|
|
5431
|
-
...defaults?.parse
|
|
5432
|
-
...overrides?.parse
|
|
5322
|
+
...defaults?.parse,
|
|
5323
|
+
...overrides?.parse
|
|
5433
5324
|
},
|
|
5434
5325
|
request: {
|
|
5435
|
-
...defaults?.request
|
|
5436
|
-
...overrides?.request
|
|
5326
|
+
...defaults?.request,
|
|
5327
|
+
...overrides?.request
|
|
5437
5328
|
},
|
|
5438
5329
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5439
5330
|
selfHeal: mergeObjectLike(defaults?.selfHeal, overrides?.selfHeal),
|
|
@@ -5449,8 +5340,8 @@ function mergeGenerateOptions(defaults, overrides) {
|
|
|
5449
5340
|
outdent: overrides?.outdent ?? defaults?.outdent,
|
|
5450
5341
|
systemPrompt: overrides?.systemPrompt ?? defaults?.systemPrompt,
|
|
5451
5342
|
request: {
|
|
5452
|
-
...defaults?.request
|
|
5453
|
-
...overrides?.request
|
|
5343
|
+
...defaults?.request,
|
|
5344
|
+
...overrides?.request
|
|
5454
5345
|
},
|
|
5455
5346
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5456
5347
|
debug: mergeObjectLike(defaults?.debug, overrides?.debug),
|