@yeaft/webchat-agent 0.1.1105 → 1.0.2
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/package.json +1 -1
- package/yeaft/debug-trace.js +179 -131
- package/yeaft/llm/anthropic.js +8 -8
- package/yeaft/llm/router.js +16 -2
- package/yeaft/models.js +15 -2
- package/yeaft/web-bridge.js +11 -5
package/package.json
CHANGED
package/yeaft/debug-trace.js
CHANGED
|
@@ -413,41 +413,24 @@ export class DebugTrace {
|
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
/**
|
|
416
|
-
* Fetch
|
|
417
|
-
* record per LLM loop (ordered oldest → newest) with the structured
|
|
418
|
-
* fields the panel expects. JSON columns are parsed; truncated /
|
|
419
|
-
* malformed payloads degrade to null instead of failing the call.
|
|
416
|
+
* Fetch debug history for the YeaftDebugPanel.
|
|
420
417
|
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
418
|
+
* Default mode returns recent loop details for backward compatibility.
|
|
419
|
+
* `indexOnly` returns all matching request summaries without loop payloads
|
|
420
|
+
* so the panel can list every past request cheaply. `limit` is intentionally
|
|
421
|
+
* ignored in index-only mode; the returned `limit` only reports the requested
|
|
422
|
+
* retention window used by older/detail paths. `detailTurnId` returns the
|
|
423
|
+
* full loop/tool payload for one request on demand.
|
|
424
|
+
*
|
|
425
|
+
* @param {{ limit?: number, dreamLimit?: number, sessionId?: string|null, threadId?: string|null, indexOnly?: boolean, detailTurnId?: string|null }} [opts]
|
|
426
|
+
* @returns {{ loops: object[], turns: object[], dreamEvents: object[], hasMore?: boolean, indexOnly?: boolean, detailTurnId?: string|null }}
|
|
423
427
|
*/
|
|
424
|
-
fetchRecentDebugHistory({ limit = 100, dreamLimit = 5, sessionId = null, threadId = null } = {}) {
|
|
428
|
+
fetchRecentDebugHistory({ limit = 100, dreamLimit = 5, sessionId = null, threadId = null, indexOnly = false, detailTurnId = null } = {}) {
|
|
425
429
|
const lim = Math.max(1, Math.min(500, Number(limit) || 100));
|
|
426
430
|
const dreamLim = Number.isFinite(Number(dreamLimit))
|
|
427
431
|
? Math.max(0, Math.min(50, Number(dreamLimit)))
|
|
428
432
|
: 5;
|
|
429
|
-
const
|
|
430
|
-
const args = [];
|
|
431
|
-
if (sessionId) { where.push('group_id = ?'); args.push(sessionId); }
|
|
432
|
-
if (threadId) { where.push('thread_id = ?'); args.push(threadId); }
|
|
433
|
-
const sql = `
|
|
434
|
-
SELECT * FROM trace_turns
|
|
435
|
-
${where.length ? `WHERE ${where.join(' AND ')}` : ''}
|
|
436
|
-
ORDER BY started_at DESC
|
|
437
|
-
LIMIT ?
|
|
438
|
-
`;
|
|
439
|
-
// Fetch one extra row so the UI can tell whether older history exists
|
|
440
|
-
// without issuing a second COUNT(*) against the hot debug trace table.
|
|
441
|
-
args.push(lim + 1);
|
|
442
|
-
const fetchedRows = this.#db.prepare(sql).all(...args);
|
|
443
|
-
const hasMore = fetchedRows.length > lim;
|
|
444
|
-
const rows = hasMore ? fetchedRows.slice(0, lim) : fetchedRows;
|
|
445
|
-
const turnIds = rows.map(r => r.id);
|
|
446
|
-
const tools = turnIds.length > 0
|
|
447
|
-
? this.#db.prepare(
|
|
448
|
-
`SELECT * FROM trace_tools WHERE turn_id IN (${turnIds.map(() => '?').join(',')}) ORDER BY created_at`
|
|
449
|
-
).all(...turnIds)
|
|
450
|
-
: [];
|
|
433
|
+
const requestedDetailTurnId = typeof detailTurnId === 'string' && detailTurnId ? detailTurnId : null;
|
|
451
434
|
const parseJsonSafe = (s) => {
|
|
452
435
|
if (s == null) return null;
|
|
453
436
|
try { return JSON.parse(s); }
|
|
@@ -472,104 +455,12 @@ export class DebugTrace {
|
|
|
472
455
|
: totalInputTokens + outputTokens,
|
|
473
456
|
};
|
|
474
457
|
};
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const key = `${traceId}#${r.turn_number || 0}`;
|
|
482
|
-
if (seenLoopKeys.has(key)) duplicateLoopTraceIds.add(traceId);
|
|
483
|
-
else seenLoopKeys.add(key);
|
|
484
|
-
}
|
|
485
|
-
// Legacy rows used one Engine-instance trace_id for many user requests.
|
|
486
|
-
// That creates duplicate Loop 1/2/... rows under the same trace. Split
|
|
487
|
-
// only those corrupted traces by SQLite row id; healthy rows keep trace_id
|
|
488
|
-
// so multi-loop requests still hydrate as one turn. Keep this as one
|
|
489
|
-
// function so loop hydration and tool attachment use the same identity.
|
|
490
|
-
const turnKeyForRow = (r) => {
|
|
491
|
-
const baseTurnId = r.trace_id || r.id;
|
|
492
|
-
return duplicateLoopTraceIds.has(baseTurnId) ? (r.id || baseTurnId) : baseTurnId;
|
|
493
|
-
};
|
|
494
|
-
const loopInstanceIdForRow = (r) => {
|
|
495
|
-
const baseTurnId = r.trace_id || r.id;
|
|
496
|
-
return duplicateLoopTraceIds.has(baseTurnId) ? (r.id || `${baseTurnId}#${r.turn_number || 0}`) : null;
|
|
497
|
-
};
|
|
498
|
-
const turnsById = new Map();
|
|
499
|
-
const loops = rows.map((r) => {
|
|
500
|
-
const parsedMessages = parseJsonSafe(r.messages_json) || [];
|
|
501
|
-
const parsedUsage = parseJsonSafe(r.usage_json);
|
|
502
|
-
const hydratedTurnId = turnKeyForRow(r);
|
|
503
|
-
const loopInstanceId = loopInstanceIdForRow(r);
|
|
504
|
-
const loop = {
|
|
505
|
-
turnId: hydratedTurnId,
|
|
506
|
-
...(loopInstanceId ? { loopInstanceId } : {}),
|
|
507
|
-
loopNumber: r.turn_number || 0,
|
|
508
|
-
model: r.model || null,
|
|
509
|
-
systemPrompt: r.system_prompt || '',
|
|
510
|
-
messages: parsedMessages,
|
|
511
|
-
response: r.response_text || '',
|
|
512
|
-
toolCalls: parseJsonSafe(r.tool_calls_json) || [],
|
|
513
|
-
usage: normalizeUsage(parsedUsage, r),
|
|
514
|
-
latencyMs: r.latency_ms || 0,
|
|
515
|
-
ttfbMs: r.ttfb_ms || null,
|
|
516
|
-
stopReason: r.stop_reason || null,
|
|
517
|
-
rawRequest: r.raw_request || null,
|
|
518
|
-
rawResponse: r.raw_response || null,
|
|
519
|
-
sessionId: r.group_id || null,
|
|
520
|
-
vpId: r.vp_id || null,
|
|
521
|
-
threadId: r.thread_id || null,
|
|
522
|
-
};
|
|
523
|
-
if (!turnsById.has(hydratedTurnId)) {
|
|
524
|
-
turnsById.set(hydratedTurnId, {
|
|
525
|
-
turnId: hydratedTurnId,
|
|
526
|
-
// C2 fix: read the explicit `user_prompt` column persisted at
|
|
527
|
-
// startTurn time. Deriving from messages_json is unsafe — each
|
|
528
|
-
// tool-loop iteration overwrites messages_json with the
|
|
529
|
-
// cumulative conversation snapshot, so `messages[0].content`
|
|
530
|
-
// would be turn-1's prompt for every subsequent turn header.
|
|
531
|
-
userPrompt: r.user_prompt || '',
|
|
532
|
-
sessionId: r.group_id || null,
|
|
533
|
-
vpId: r.vp_id || null,
|
|
534
|
-
threadId: r.thread_id || null,
|
|
535
|
-
openedAt: r.started_at || 0,
|
|
536
|
-
closedAt: r.ended_at || null,
|
|
537
|
-
totalMs: 0,
|
|
538
|
-
totalTokens: 0,
|
|
539
|
-
loopCount: 0,
|
|
540
|
-
memoryLoaded: null,
|
|
541
|
-
memoryAdjust: null,
|
|
542
|
-
tools: [],
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
const t = turnsById.get(hydratedTurnId);
|
|
546
|
-
t.loopCount += 1;
|
|
547
|
-
// Aggregate per-loop latency / tokens so the Turn header shows the
|
|
548
|
-
// same totals the live `turn_close` event would have stamped.
|
|
549
|
-
t.totalMs += r.latency_ms || 0;
|
|
550
|
-
t.totalTokens += loop.usage.totalTokens || 0;
|
|
551
|
-
if (r.ended_at && (!t.closedAt || r.ended_at > t.closedAt)) t.closedAt = r.ended_at;
|
|
552
|
-
return loop;
|
|
553
|
-
});
|
|
554
|
-
// Attach tools to their parent Turn so the panel can render per-tool
|
|
555
|
-
// timing without scanning the loop bodies.
|
|
556
|
-
for (const tool of tools) {
|
|
557
|
-
// Find which loop row this tool belongs to; use the same hydrated
|
|
558
|
-
// identity as the loop/turn records so split legacy rows keep tools.
|
|
559
|
-
const owner = rows.find(r => r.id === tool.turn_id);
|
|
560
|
-
if (!owner) continue;
|
|
561
|
-
const t = turnsById.get(turnKeyForRow(owner));
|
|
562
|
-
if (!t) continue;
|
|
563
|
-
t.tools.push({
|
|
564
|
-
loopNumber: owner.turn_number || 0,
|
|
565
|
-
callId: tool.tool_call_id || tool.id,
|
|
566
|
-
traceToolId: tool.id,
|
|
567
|
-
name: tool.tool_name,
|
|
568
|
-
toolOutput: tool.tool_output == null ? null : String(tool.tool_output),
|
|
569
|
-
durationMs: tool.duration_ms || 0,
|
|
570
|
-
isError: !!tool.is_error,
|
|
571
|
-
});
|
|
572
|
-
}
|
|
458
|
+
const scopedWhere = [];
|
|
459
|
+
const scopedArgs = [];
|
|
460
|
+
if (sessionId) { scopedWhere.push('group_id = ?'); scopedArgs.push(sessionId); }
|
|
461
|
+
if (threadId) { scopedWhere.push('thread_id = ?'); scopedArgs.push(threadId); }
|
|
462
|
+
const whereSql = scopedWhere.length ? `WHERE ${scopedWhere.join(' AND ')}` : '';
|
|
463
|
+
|
|
573
464
|
const dreamEvents = [];
|
|
574
465
|
if (dreamLim > 0) {
|
|
575
466
|
const eventRows = this.#db.prepare(`
|
|
@@ -597,10 +488,167 @@ export class DebugTrace {
|
|
|
597
488
|
dreamEvents.reverse();
|
|
598
489
|
}
|
|
599
490
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
491
|
+
const duplicateLoopTraceIdsForRows = (rows) => {
|
|
492
|
+
const duplicateLoopTraceIds = new Set();
|
|
493
|
+
const seenLoopKeys = new Set();
|
|
494
|
+
for (const r of rows) {
|
|
495
|
+
const traceId = r.trace_id || r.id;
|
|
496
|
+
const key = `${traceId}#${r.turn_number || 0}`;
|
|
497
|
+
if (seenLoopKeys.has(key)) duplicateLoopTraceIds.add(traceId);
|
|
498
|
+
else seenLoopKeys.add(key);
|
|
499
|
+
}
|
|
500
|
+
return duplicateLoopTraceIds;
|
|
501
|
+
};
|
|
502
|
+
const detailRequestedByRowId = (rows) => {
|
|
503
|
+
if (!requestedDetailTurnId || rows.length !== 1) return false;
|
|
504
|
+
const row = rows[0];
|
|
505
|
+
return row?.id === requestedDetailTurnId && row?.trace_id !== requestedDetailTurnId;
|
|
506
|
+
};
|
|
507
|
+
const summarizeRows = (rows, duplicateLoopTraceIds = duplicateLoopTraceIdsForRows(rows), forceRowId = false) => {
|
|
508
|
+
const turnKeyForRow = (r) => {
|
|
509
|
+
if (forceRowId) return r.id || r.trace_id;
|
|
510
|
+
const baseTurnId = r.trace_id || r.id;
|
|
511
|
+
return duplicateLoopTraceIds.has(baseTurnId) ? (r.id || baseTurnId) : baseTurnId;
|
|
512
|
+
};
|
|
513
|
+
const turnsById = new Map();
|
|
514
|
+
for (const r of rows) {
|
|
515
|
+
const hydratedTurnId = turnKeyForRow(r);
|
|
516
|
+
const parsedUsage = parseJsonSafe(r.usage_json);
|
|
517
|
+
const usage = normalizeUsage(parsedUsage, r);
|
|
518
|
+
if (!turnsById.has(hydratedTurnId)) {
|
|
519
|
+
turnsById.set(hydratedTurnId, {
|
|
520
|
+
turnId: hydratedTurnId,
|
|
521
|
+
userPrompt: r.user_prompt || '',
|
|
522
|
+
sessionId: r.group_id || null,
|
|
523
|
+
vpId: r.vp_id || null,
|
|
524
|
+
threadId: r.thread_id || null,
|
|
525
|
+
openedAt: r.started_at || 0,
|
|
526
|
+
closedAt: r.ended_at || null,
|
|
527
|
+
totalMs: 0,
|
|
528
|
+
totalTokens: 0,
|
|
529
|
+
summaryInputTokens: 0,
|
|
530
|
+
summaryOutputTokens: 0,
|
|
531
|
+
loopCount: 0,
|
|
532
|
+
memoryLoaded: null,
|
|
533
|
+
memoryAdjust: null,
|
|
534
|
+
tools: [],
|
|
535
|
+
detailsLoaded: false,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
const t = turnsById.get(hydratedTurnId);
|
|
539
|
+
t.loopCount += 1;
|
|
540
|
+
t.totalMs += r.latency_ms || 0;
|
|
541
|
+
t.totalTokens += usage.totalTokens || 0;
|
|
542
|
+
t.summaryInputTokens += usage.totalInputTokens || 0;
|
|
543
|
+
t.summaryOutputTokens += usage.outputTokens || 0;
|
|
544
|
+
if (r.started_at && (!t.openedAt || r.started_at < t.openedAt)) t.openedAt = r.started_at;
|
|
545
|
+
if (r.ended_at && (!t.closedAt || r.ended_at > t.closedAt)) t.closedAt = r.ended_at;
|
|
546
|
+
if (!t.userPrompt && r.user_prompt) t.userPrompt = r.user_prompt;
|
|
547
|
+
}
|
|
548
|
+
return Array.from(turnsById.values()).sort((a, b) => (a.openedAt || 0) - (b.openedAt || 0));
|
|
549
|
+
};
|
|
550
|
+
const expandRows = (rows) => {
|
|
551
|
+
const duplicateLoopTraceIds = duplicateLoopTraceIdsForRows(rows);
|
|
552
|
+
const forceRowId = detailRequestedByRowId(rows);
|
|
553
|
+
const turnKeyForRow = (r) => {
|
|
554
|
+
if (forceRowId) return r.id || r.trace_id;
|
|
555
|
+
const baseTurnId = r.trace_id || r.id;
|
|
556
|
+
return duplicateLoopTraceIds.has(baseTurnId) ? (r.id || baseTurnId) : baseTurnId;
|
|
557
|
+
};
|
|
558
|
+
const loopInstanceIdForRow = (r) => {
|
|
559
|
+
const baseTurnId = r.trace_id || r.id;
|
|
560
|
+
return forceRowId || duplicateLoopTraceIds.has(baseTurnId) ? (r.id || `${baseTurnId}#${r.turn_number || 0}`) : null;
|
|
561
|
+
};
|
|
562
|
+
const turnsById = new Map(summarizeRows(rows, duplicateLoopTraceIds, forceRowId).map((t) => [t.turnId, { ...t, detailsLoaded: true }]));
|
|
563
|
+
const loops = rows.map((r) => {
|
|
564
|
+
const parsedMessages = parseJsonSafe(r.messages_json) || [];
|
|
565
|
+
const parsedUsage = parseJsonSafe(r.usage_json);
|
|
566
|
+
const hydratedTurnId = turnKeyForRow(r);
|
|
567
|
+
const loopInstanceId = loopInstanceIdForRow(r);
|
|
568
|
+
return {
|
|
569
|
+
turnId: hydratedTurnId,
|
|
570
|
+
...(loopInstanceId ? { loopInstanceId } : {}),
|
|
571
|
+
loopNumber: r.turn_number || 0,
|
|
572
|
+
model: r.model || null,
|
|
573
|
+
systemPrompt: r.system_prompt || '',
|
|
574
|
+
messages: parsedMessages,
|
|
575
|
+
response: r.response_text || '',
|
|
576
|
+
toolCalls: parseJsonSafe(r.tool_calls_json) || [],
|
|
577
|
+
usage: normalizeUsage(parsedUsage, r),
|
|
578
|
+
latencyMs: r.latency_ms || 0,
|
|
579
|
+
ttfbMs: r.ttfb_ms || null,
|
|
580
|
+
stopReason: r.stop_reason || null,
|
|
581
|
+
rawRequest: r.raw_request || null,
|
|
582
|
+
rawResponse: r.raw_response || null,
|
|
583
|
+
sessionId: r.group_id || null,
|
|
584
|
+
vpId: r.vp_id || null,
|
|
585
|
+
threadId: r.thread_id || null,
|
|
586
|
+
};
|
|
587
|
+
});
|
|
588
|
+
const turnIds = rows.map(r => r.id);
|
|
589
|
+
const tools = turnIds.length > 0
|
|
590
|
+
? this.#db.prepare(
|
|
591
|
+
`SELECT * FROM trace_tools WHERE turn_id IN (${turnIds.map(() => '?').join(',')}) ORDER BY created_at`
|
|
592
|
+
).all(...turnIds)
|
|
593
|
+
: [];
|
|
594
|
+
for (const tool of tools) {
|
|
595
|
+
const owner = rows.find(r => r.id === tool.turn_id);
|
|
596
|
+
if (!owner) continue;
|
|
597
|
+
const t = turnsById.get(turnKeyForRow(owner));
|
|
598
|
+
if (!t) continue;
|
|
599
|
+
t.tools.push({
|
|
600
|
+
loopNumber: owner.turn_number || 0,
|
|
601
|
+
callId: tool.tool_call_id || tool.id,
|
|
602
|
+
traceToolId: tool.id,
|
|
603
|
+
name: tool.tool_name,
|
|
604
|
+
toolOutput: tool.tool_output == null ? null : String(tool.tool_output),
|
|
605
|
+
durationMs: tool.duration_ms || 0,
|
|
606
|
+
isError: !!tool.is_error,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return { loops, turns: Array.from(turnsById.values()).sort((a, b) => (a.openedAt || 0) - (b.openedAt || 0)) };
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
if (requestedDetailTurnId) {
|
|
613
|
+
const detailWhere = [`(trace_id = ? OR id = ?)`];
|
|
614
|
+
const detailArgs = [requestedDetailTurnId, requestedDetailTurnId];
|
|
615
|
+
if (sessionId) { detailWhere.push('group_id = ?'); detailArgs.push(sessionId); }
|
|
616
|
+
if (threadId) { detailWhere.push('thread_id = ?'); detailArgs.push(threadId); }
|
|
617
|
+
detailArgs.push(5000);
|
|
618
|
+
const rows = this.#db.prepare(`
|
|
619
|
+
SELECT * FROM trace_turns
|
|
620
|
+
WHERE ${detailWhere.join(' AND ')}
|
|
621
|
+
ORDER BY started_at ASC, turn_number ASC, rowid ASC
|
|
622
|
+
LIMIT ?
|
|
623
|
+
`).all(...detailArgs);
|
|
624
|
+
const expanded = expandRows(rows);
|
|
625
|
+
return { ...expanded, dreamEvents, hasMore: false, limit: rows.length, indexOnly: false, detailTurnId: requestedDetailTurnId };
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
if (indexOnly) {
|
|
629
|
+
const rows = this.#db.prepare(`
|
|
630
|
+
SELECT id, trace_id, message_id, mode, turn_number, model,
|
|
631
|
+
input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
|
|
632
|
+
stop_reason, latency_ms, started_at, ended_at, group_id, vp_id,
|
|
633
|
+
thread_id, usage_json, user_prompt
|
|
634
|
+
FROM trace_turns
|
|
635
|
+
${whereSql}
|
|
636
|
+
ORDER BY started_at ASC, turn_number ASC, rowid ASC
|
|
637
|
+
`).all(...scopedArgs);
|
|
638
|
+
return { loops: [], turns: summarizeRows(rows), dreamEvents, hasMore: false, limit: lim, indexOnly: true };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const args = [...scopedArgs, lim + 1];
|
|
642
|
+
const fetchedRows = this.#db.prepare(`
|
|
643
|
+
SELECT * FROM trace_turns
|
|
644
|
+
${whereSql}
|
|
645
|
+
ORDER BY started_at DESC
|
|
646
|
+
LIMIT ?
|
|
647
|
+
`).all(...args);
|
|
648
|
+
const hasMore = fetchedRows.length > lim;
|
|
649
|
+
const rows = (hasMore ? fetchedRows.slice(0, lim) : fetchedRows).reverse();
|
|
650
|
+
const expanded = expandRows(rows);
|
|
651
|
+
return { ...expanded, dreamEvents, hasMore, limit: lim, indexOnly: false };
|
|
604
652
|
}
|
|
605
653
|
|
|
606
654
|
/**
|
package/yeaft/llm/anthropic.js
CHANGED
|
@@ -34,10 +34,10 @@ function thinkingV1Enabled() {
|
|
|
34
34
|
return process.env.YEAFT_THINKING_V1 === '1';
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function applyAnthropicThinking(body, model, effort) {
|
|
38
|
-
const cap = getThinkingCapability(model);
|
|
37
|
+
function applyAnthropicThinking(body, model, effort, effortContext = {}) {
|
|
38
|
+
const cap = getThinkingCapability(model, effortContext);
|
|
39
39
|
if (!cap.supportsThinking) return;
|
|
40
|
-
if (!getModelEffortOptions(model).includes(effort)) return;
|
|
40
|
+
if (!getModelEffortOptions(model, effortContext).includes(effort)) return;
|
|
41
41
|
|
|
42
42
|
if (cap.thinkingProtocol === 'anthropic-adaptive') {
|
|
43
43
|
body.thinking = { type: 'adaptive' };
|
|
@@ -207,10 +207,10 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
/**
|
|
210
|
-
* @param {{ model: string, system: string, messages: import('./adapter.js').UnifiedMessage[], tools?: import('./adapter.js').UnifiedToolDef[], maxTokens?: number, effort?: 'low'|'medium'|'high'|'xhigh'|'max', effortSource?: 'user'|'auto', signal?: AbortSignal }} params
|
|
210
|
+
* @param {{ model: string, system: string, messages: import('./adapter.js').UnifiedMessage[], tools?: import('./adapter.js').UnifiedToolDef[], maxTokens?: number, effort?: 'low'|'medium'|'high'|'xhigh'|'max', effortSource?: 'user'|'auto', effortContext?: object, signal?: AbortSignal, onRawExchange?: ({rawRequest, rawResponse}) => void }} params
|
|
211
211
|
* @returns {AsyncGenerator<import('./adapter.js').StreamEvent>}
|
|
212
212
|
*/
|
|
213
|
-
async *stream({ model, system, messages, tools, maxTokens = 16384, effort, effortSource, signal, onRawExchange }) {
|
|
213
|
+
async *stream({ model, system, messages, tools, maxTokens = 16384, effort, effortSource, effortContext, signal, onRawExchange }) {
|
|
214
214
|
if (signal?.aborted) throw new LLMAbortError();
|
|
215
215
|
|
|
216
216
|
const body = {
|
|
@@ -226,7 +226,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
226
226
|
// models use budget_tokens. Unsupported combinations silently drop effort.
|
|
227
227
|
const normEffort = normalizeEffort(effort);
|
|
228
228
|
if ((thinkingV1Enabled() || effortSource === 'user') && normEffort) {
|
|
229
|
-
applyAnthropicThinking(body, model, normEffort);
|
|
229
|
+
applyAnthropicThinking(body, model, normEffort, effortContext);
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
const translatedTools = this.#translateTools(tools);
|
|
@@ -473,7 +473,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
473
473
|
* models silently drop the param. max_tokens auto-widens to budget+1024
|
|
474
474
|
* when needed.
|
|
475
475
|
*/
|
|
476
|
-
async call({ model, system, messages, maxTokens = 4096, effort, effortSource, signal }) {
|
|
476
|
+
async call({ model, system, messages, maxTokens = 4096, effort, effortSource, effortContext, signal }) {
|
|
477
477
|
if (signal?.aborted) throw new LLMAbortError();
|
|
478
478
|
|
|
479
479
|
const body = {
|
|
@@ -486,7 +486,7 @@ export class AnthropicAdapter extends LLMAdapter {
|
|
|
486
486
|
// task-327c: mirror stream()'s thinking injection for side queries.
|
|
487
487
|
const normEffort = normalizeEffort(effort);
|
|
488
488
|
if ((thinkingV1Enabled() || effortSource === 'user') && normEffort) {
|
|
489
|
-
applyAnthropicThinking(body, model, normEffort);
|
|
489
|
+
applyAnthropicThinking(body, model, normEffort, effortContext);
|
|
490
490
|
}
|
|
491
491
|
|
|
492
492
|
let response;
|
package/yeaft/llm/router.js
CHANGED
|
@@ -552,9 +552,16 @@ export class AdapterRouter extends LLMAdapter {
|
|
|
552
552
|
*/
|
|
553
553
|
async *stream(params) {
|
|
554
554
|
const resolved = await this.#resolveAdapter(params.model);
|
|
555
|
+
const effortContext = {
|
|
556
|
+
protocol: resolved.protocol,
|
|
557
|
+
supportsEffort: resolved.entry?.supportsEffort,
|
|
558
|
+
effortOptions: resolved.entry?.effortOptions,
|
|
559
|
+
thinkingProtocol: resolved.entry?.thinkingProtocol,
|
|
560
|
+
maxBudgetTokens: resolved.entry?.maxBudgetTokens,
|
|
561
|
+
};
|
|
555
562
|
const filtered = filterEffortForModel({ ...params, model: resolved.modelId }, resolved);
|
|
556
563
|
const sanitized = sanitizeMessagesForWire(filtered);
|
|
557
|
-
yield* resolved.adapter.stream({ ...sanitized, model: resolved.modelId });
|
|
564
|
+
yield* resolved.adapter.stream({ ...sanitized, model: resolved.modelId, effortContext });
|
|
558
565
|
}
|
|
559
566
|
|
|
560
567
|
/**
|
|
@@ -565,9 +572,16 @@ export class AdapterRouter extends LLMAdapter {
|
|
|
565
572
|
*/
|
|
566
573
|
async call(params) {
|
|
567
574
|
const resolved = await this.#resolveAdapter(params.model);
|
|
575
|
+
const effortContext = {
|
|
576
|
+
protocol: resolved.protocol,
|
|
577
|
+
supportsEffort: resolved.entry?.supportsEffort,
|
|
578
|
+
effortOptions: resolved.entry?.effortOptions,
|
|
579
|
+
thinkingProtocol: resolved.entry?.thinkingProtocol,
|
|
580
|
+
maxBudgetTokens: resolved.entry?.maxBudgetTokens,
|
|
581
|
+
};
|
|
568
582
|
const filtered = filterEffortForModel({ ...params, model: resolved.modelId }, resolved);
|
|
569
583
|
const sanitized = sanitizeMessagesForWire(filtered);
|
|
570
|
-
return resolved.adapter.call({ ...sanitized, model: resolved.modelId });
|
|
584
|
+
return resolved.adapter.call({ ...sanitized, model: resolved.modelId, effortContext });
|
|
571
585
|
}
|
|
572
586
|
|
|
573
587
|
/**
|
package/yeaft/models.js
CHANGED
|
@@ -537,8 +537,13 @@ export function thinkingBudgetForEffort(model, effort) {
|
|
|
537
537
|
*/
|
|
538
538
|
export function getThinkingCapability(model, context = {}) {
|
|
539
539
|
const info = MODEL_REGISTRY.get(model);
|
|
540
|
+
const modelId = parseModelRef(model).modelId;
|
|
540
541
|
const overrideOptions = normalizeEffortOptions(context.effortOptions);
|
|
541
|
-
const overrideProtocol = context.thinkingProtocol || (
|
|
542
|
+
const overrideProtocol = context.thinkingProtocol || (
|
|
543
|
+
context.protocol === 'anthropic'
|
|
544
|
+
? (/^deepseek/i.test(modelId) ? 'anthropic-adaptive' : 'anthropic')
|
|
545
|
+
: context.protocol === 'openai-responses' ? 'openai-reasoning' : null
|
|
546
|
+
);
|
|
542
547
|
if (context.supportsEffort === true || overrideOptions) {
|
|
543
548
|
return {
|
|
544
549
|
supportsThinking: true,
|
|
@@ -560,7 +565,15 @@ export function getThinkingCapability(model, context = {}) {
|
|
|
560
565
|
};
|
|
561
566
|
}
|
|
562
567
|
if (context.protocol === 'anthropic' && inferred?.thinkingProtocol === 'openai-reasoning') {
|
|
563
|
-
|
|
568
|
+
if (/^deepseek/i.test(modelId)) {
|
|
569
|
+
inferred = {
|
|
570
|
+
...inferred,
|
|
571
|
+
thinkingProtocol: 'anthropic-adaptive',
|
|
572
|
+
effortOptions: DEEPSEEK_REASONING_EFFORT_OPTIONS,
|
|
573
|
+
};
|
|
574
|
+
} else {
|
|
575
|
+
inferred = null;
|
|
576
|
+
}
|
|
564
577
|
}
|
|
565
578
|
if ((!info || !info.supportsThinking) && !inferred) {
|
|
566
579
|
return {
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -4366,12 +4366,14 @@ export async function handleYeaftFetchToolStats(_msg = {}) {
|
|
|
4366
4366
|
* splices into place.
|
|
4367
4367
|
*
|
|
4368
4368
|
* Inputs (all optional):
|
|
4369
|
-
* - `limit`
|
|
4370
|
-
* - `
|
|
4371
|
-
* - `
|
|
4369
|
+
* - `limit` — legacy recent-detail loop cap; ignored by index-only
|
|
4370
|
+
* - `indexOnly` — list request summaries without loop/detail payloads
|
|
4371
|
+
* - `detailTurnId` — fetch full loops/tools for one request
|
|
4372
|
+
* - `sessionId` — narrow by Session
|
|
4373
|
+
* - `threadId` — narrow by thread
|
|
4372
4374
|
*
|
|
4373
4375
|
* Sends:
|
|
4374
|
-
* { type: 'yeaft_debug_history', loops: [...], turns: [...] }
|
|
4376
|
+
* { type: 'yeaft_debug_history', loops: [...], turns: [...], indexOnly, detailTurnId }
|
|
4375
4377
|
*
|
|
4376
4378
|
* Best-effort: if the session / trace isn't ready, sends an empty
|
|
4377
4379
|
* snapshot so the panel renders a placeholder instead of spinning.
|
|
@@ -4381,13 +4383,15 @@ export async function handleYeaftFetchDebugHistory(msg = {}) {
|
|
|
4381
4383
|
const dreamLimit = Number.isFinite(msg?.dreamLimit) ? Number(msg.dreamLimit) : 5;
|
|
4382
4384
|
const sessionId = typeof msg?.sessionId === 'string' && msg.sessionId ? msg.sessionId : null;
|
|
4383
4385
|
const threadId = typeof msg?.threadId === 'string' && msg.threadId ? msg.threadId : null;
|
|
4386
|
+
const indexOnly = !!msg?.indexOnly;
|
|
4387
|
+
const detailTurnId = typeof msg?.detailTurnId === 'string' && msg.detailTurnId ? msg.detailTurnId : null;
|
|
4384
4388
|
let loops = [];
|
|
4385
4389
|
let turns = [];
|
|
4386
4390
|
let dreamEvents = [];
|
|
4387
4391
|
let hasMore = false;
|
|
4388
4392
|
try {
|
|
4389
4393
|
if (session?.trace && typeof session.trace.fetchRecentDebugHistory === 'function') {
|
|
4390
|
-
const out = session.trace.fetchRecentDebugHistory({ limit, dreamLimit, sessionId, threadId });
|
|
4394
|
+
const out = session.trace.fetchRecentDebugHistory({ limit, dreamLimit, sessionId, threadId, indexOnly, detailTurnId });
|
|
4391
4395
|
loops = Array.isArray(out?.loops) ? out.loops : [];
|
|
4392
4396
|
turns = Array.isArray(out?.turns) ? out.turns : [];
|
|
4393
4397
|
dreamEvents = Array.isArray(out?.dreamEvents) ? out.dreamEvents : [];
|
|
@@ -4412,6 +4416,8 @@ export async function handleYeaftFetchDebugHistory(msg = {}) {
|
|
|
4412
4416
|
threadId,
|
|
4413
4417
|
hasMore,
|
|
4414
4418
|
limit,
|
|
4419
|
+
indexOnly,
|
|
4420
|
+
detailTurnId,
|
|
4415
4421
|
});
|
|
4416
4422
|
}
|
|
4417
4423
|
|