@yeaft/webchat-agent 1.0.370 → 1.0.372
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/connection/buffer.js +60 -46
- package/context.js +5 -1
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +52 -52
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +1 -1
- package/package.json +1 -1
- package/yeaft/debug-trace.js +93 -34
- package/yeaft/engine.js +14 -1
|
Binary file
|
package/package.json
CHANGED
package/yeaft/debug-trace.js
CHANGED
|
@@ -127,9 +127,19 @@ function regexHasUnsafeQuantifiedGroup(pattern) {
|
|
|
127
127
|
function buildTraceSearchDocument(trace) {
|
|
128
128
|
const loops = Array.isArray(trace?.loops) ? trace.loops : [];
|
|
129
129
|
const tools = Array.isArray(trace?.tools) ? trace.tools : [];
|
|
130
|
-
const toolNames =
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
const toolNames = [
|
|
131
|
+
...tools.map(t => t?.toolName || t?.name || '').filter(Boolean),
|
|
132
|
+
...(Array.isArray(trace?.toolNames) ? trace.toolNames : []),
|
|
133
|
+
].join(' ');
|
|
134
|
+
const loopModels = [
|
|
135
|
+
...loops.map(l => l?.model || '').filter(Boolean),
|
|
136
|
+
...(Array.isArray(trace?.loopModels) ? trace.loopModels : []),
|
|
137
|
+
].join(' ');
|
|
138
|
+
const stopReasons = [
|
|
139
|
+
...loops.map(l => l?.stopReason || '').filter(Boolean),
|
|
140
|
+
...(Array.isArray(trace?.stopReasons) ? trace.stopReasons : []),
|
|
141
|
+
trace?.finalStopReason || '',
|
|
142
|
+
].filter(Boolean).join(' ');
|
|
133
143
|
return [
|
|
134
144
|
trace?.requestId,
|
|
135
145
|
trace?.traceId,
|
|
@@ -596,7 +606,24 @@ function turnLocatorPath(rootDir, sessionId, turnId) {
|
|
|
596
606
|
}
|
|
597
607
|
|
|
598
608
|
function serializableTraceMeta(trace) {
|
|
599
|
-
const
|
|
609
|
+
const loops = Array.isArray(trace?.loops) ? trace.loops : [];
|
|
610
|
+
const tools = Array.isArray(trace?.tools) ? trace.tools : [];
|
|
611
|
+
const usage = loops.reduce((acc, loop) => {
|
|
612
|
+
const normalized = normalizeUsage(loop?.usage || {});
|
|
613
|
+
acc.totalMs += Number(loop?.latencyMs || 0);
|
|
614
|
+
acc.totalTokens += Number(normalized.totalTokens || 0);
|
|
615
|
+
acc.summaryInputTokens += Number(normalized.totalInputTokens || 0);
|
|
616
|
+
acc.summaryOutputTokens += Number(normalized.outputTokens || 0);
|
|
617
|
+
return acc;
|
|
618
|
+
}, { totalMs: 0, totalTokens: 0, summaryInputTokens: 0, summaryOutputTokens: 0 });
|
|
619
|
+
const meta = {
|
|
620
|
+
...trace,
|
|
621
|
+
loopCount: loops.length,
|
|
622
|
+
...usage,
|
|
623
|
+
loopModels: [...new Set(loops.map(loop => loop?.model).filter(Boolean))],
|
|
624
|
+
stopReasons: [...new Set(loops.map(loop => loop?.stopReason).filter(Boolean))],
|
|
625
|
+
toolNames: [...new Set(tools.map(tool => tool?.toolName || tool?.name).filter(Boolean))],
|
|
626
|
+
};
|
|
600
627
|
delete meta._lastSnapshot;
|
|
601
628
|
delete meta._persistedFormat;
|
|
602
629
|
delete meta._persistedRequestDir;
|
|
@@ -689,11 +716,11 @@ function summarizeTrace(trace, detailsLoaded = false) {
|
|
|
689
716
|
threadId: trace?.threadId || null,
|
|
690
717
|
openedAt: trace?.openedAt || 0,
|
|
691
718
|
closedAt: trace?.closedAt || null,
|
|
692
|
-
totalMs: usage.totalMs,
|
|
693
|
-
totalTokens: usage.totalTokens,
|
|
694
|
-
summaryInputTokens: usage.summaryInputTokens,
|
|
695
|
-
summaryOutputTokens: usage.summaryOutputTokens,
|
|
696
|
-
loopCount: loops.length,
|
|
719
|
+
totalMs: loops.length > 0 ? usage.totalMs : Number(trace?.totalMs || 0),
|
|
720
|
+
totalTokens: loops.length > 0 ? usage.totalTokens : Number(trace?.totalTokens || 0),
|
|
721
|
+
summaryInputTokens: loops.length > 0 ? usage.summaryInputTokens : Number(trace?.summaryInputTokens || 0),
|
|
722
|
+
summaryOutputTokens: loops.length > 0 ? usage.summaryOutputTokens : Number(trace?.summaryOutputTokens || 0),
|
|
723
|
+
loopCount: loops.length > 0 ? loops.length : Number(trace?.loopCount || 0),
|
|
697
724
|
memoryLoaded: null,
|
|
698
725
|
memoryAdjust: null,
|
|
699
726
|
tools: Array.isArray(trace?.tools) ? trace.tools.map(t => ({
|
|
@@ -1123,33 +1150,39 @@ export class DebugTrace {
|
|
|
1123
1150
|
}
|
|
1124
1151
|
|
|
1125
1152
|
async queryByMessage(messageId) {
|
|
1126
|
-
await this.#ensureHydrated();
|
|
1127
1153
|
await this.#drainWrites();
|
|
1128
|
-
const traces = this.#
|
|
1129
|
-
.filter(
|
|
1130
|
-
.map(({ trace }) => trace);
|
|
1154
|
+
const traces = (await readTraceSummaries(this.#rootDir)).map(item => item.trace)
|
|
1155
|
+
.filter(trace => trace.messageId === messageId);
|
|
1131
1156
|
return this.#expandLegacy(traces);
|
|
1132
1157
|
}
|
|
1133
1158
|
|
|
1134
1159
|
async queryByTrace(traceId) {
|
|
1135
|
-
await this.#ensureHydrated();
|
|
1136
1160
|
await this.#drainWrites();
|
|
1137
|
-
const traces = this.#
|
|
1138
|
-
.filter(
|
|
1139
|
-
.map(({ trace }) => trace);
|
|
1161
|
+
const traces = (await readTraceSummaries(this.#rootDir)).map(item => item.trace)
|
|
1162
|
+
.filter(trace => trace.traceId === traceId || trace.requestId === traceId);
|
|
1140
1163
|
return this.#expandLegacy(traces);
|
|
1141
1164
|
}
|
|
1142
1165
|
|
|
1143
1166
|
async queryRecent(limit = 20) {
|
|
1144
|
-
await this.#ensureHydrated();
|
|
1145
1167
|
await this.#drainWrites();
|
|
1146
1168
|
const lim = Math.max(1, Math.min(MAX_HISTORY_LIMIT, Number(limit) || MAX_HISTORY_LIMIT));
|
|
1147
|
-
return this.#
|
|
1169
|
+
return (await readTraceSummaries(this.#rootDir))
|
|
1148
1170
|
.slice(-lim)
|
|
1149
1171
|
.reverse()
|
|
1150
1172
|
.flatMap(({ trace }) => traceToLegacyRows(trace));
|
|
1151
1173
|
}
|
|
1152
1174
|
|
|
1175
|
+
finalizeQuery(traceId, { sessionId = null, stopReason = 'end_turn' } = {}) {
|
|
1176
|
+
for (const trace of this.#requestCache.values()) {
|
|
1177
|
+
if (trace.traceId !== traceId || (sessionId != null && trace.sessionId !== sessionId)) continue;
|
|
1178
|
+
trace.active = false;
|
|
1179
|
+
trace.closedAt ||= Date.now();
|
|
1180
|
+
trace.updatedAt = Date.now();
|
|
1181
|
+
trace.finalStopReason = stopReason;
|
|
1182
|
+
this.#appendTraceRecord(trace, 'finalize', { at: trace.updatedAt, stopReason }, { writeMeta: true, evictAfterWrite: true });
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1153
1186
|
async fetchTurnDebug({ sessionId, turnId, dreamLimit = 0 } = {}) {
|
|
1154
1187
|
const requestedSessionId = typeof sessionId === 'string' && sessionId ? sessionId : null;
|
|
1155
1188
|
const requestedTurnId = typeof turnId === 'string' && turnId ? turnId : null;
|
|
@@ -1166,10 +1199,7 @@ export class DebugTrace {
|
|
|
1166
1199
|
const locator = await readJson(turnLocatorPath(this.#rootDir, requestedSessionId, requestedTurnId));
|
|
1167
1200
|
if (locator?.requestKey && locator.sessionId === requestedSessionId && locator.requestId === requestedTurnId) {
|
|
1168
1201
|
const located = await readRequestDir(requestDirFor(this.#rootDir, requestedSessionId, locator.requestKey));
|
|
1169
|
-
if (traceMatchesIdentity(located, requestedSessionId, requestedTurnId))
|
|
1170
|
-
trace = located;
|
|
1171
|
-
this.#requestCache.set(trace.requestKey, trace);
|
|
1172
|
-
}
|
|
1202
|
+
if (traceMatchesIdentity(located, requestedSessionId, requestedTurnId)) trace = located;
|
|
1173
1203
|
}
|
|
1174
1204
|
}
|
|
1175
1205
|
if (!trace) {
|
|
@@ -1178,7 +1208,6 @@ export class DebugTrace {
|
|
|
1178
1208
|
for (const item of await readTraceSummaries(this.#rootDir, requestedSessionId)) {
|
|
1179
1209
|
if (traceMatchesIdentity(item.trace, requestedSessionId, requestedTurnId)) {
|
|
1180
1210
|
trace = item.trace;
|
|
1181
|
-
this.#requestCache.set(trace.requestKey, trace);
|
|
1182
1211
|
break;
|
|
1183
1212
|
}
|
|
1184
1213
|
}
|
|
@@ -1195,14 +1224,13 @@ export class DebugTrace {
|
|
|
1195
1224
|
const detail = await this.fetchTurnDebug({ sessionId, turnId: requestedDetailTurnId, dreamLimit });
|
|
1196
1225
|
return { ...detail, hasMore: false, limit: detail.loops.length, indexOnly: false };
|
|
1197
1226
|
}
|
|
1198
|
-
await this.#ensureHydrated();
|
|
1199
1227
|
await this.#drainWrites();
|
|
1200
1228
|
const lim = Math.max(1, Math.min(MAX_HISTORY_LIMIT, Number(limit) || MAX_HISTORY_LIMIT));
|
|
1201
1229
|
const searchRegex = requestedDetailTurnId ? null : compileTraceSearchRegex(search);
|
|
1202
|
-
const traces = this.#
|
|
1203
|
-
.
|
|
1204
|
-
.filter(
|
|
1205
|
-
.
|
|
1230
|
+
const traces = (await readTraceHeaders(this.#rootDir, sessionId))
|
|
1231
|
+
.map(({ trace }) => trace)
|
|
1232
|
+
.filter(trace => !threadId || trace.threadId === threadId)
|
|
1233
|
+
.filter(trace => requestedDetailTurnId || traceMatchesRegex(trace, searchRegex));
|
|
1206
1234
|
const dreamEvents = this.#readDreamEvents({ sessionId, dreamLimit });
|
|
1207
1235
|
if (requestedDetailTurnId) {
|
|
1208
1236
|
const trace = traces.find(t => t.requestId === requestedDetailTurnId || t.traceId === requestedDetailTurnId);
|
|
@@ -1214,14 +1242,22 @@ export class DebugTrace {
|
|
|
1214
1242
|
if (indexOnly) {
|
|
1215
1243
|
return {
|
|
1216
1244
|
loops: [],
|
|
1217
|
-
turns: selected.map(trace =>
|
|
1245
|
+
turns: selected.map(trace => ({
|
|
1246
|
+
...summarizeTrace(trace, false),
|
|
1247
|
+
loopCount: Number(trace.loopCount || 0),
|
|
1248
|
+
tools: [],
|
|
1249
|
+
})),
|
|
1218
1250
|
dreamEvents,
|
|
1219
1251
|
hasMore: traces.length > selected.length,
|
|
1220
1252
|
limit: lim,
|
|
1221
1253
|
indexOnly: true,
|
|
1222
1254
|
};
|
|
1223
1255
|
}
|
|
1224
|
-
const
|
|
1256
|
+
const selectedKeys = new Set(selected.map(trace => trace.requestKey));
|
|
1257
|
+
const detailed = (await readTraceSummaries(this.#rootDir, sessionId))
|
|
1258
|
+
.map(({ trace }) => trace)
|
|
1259
|
+
.filter(trace => selectedKeys.has(trace.requestKey));
|
|
1260
|
+
const expanded = detailed.reduce((acc, trace) => {
|
|
1225
1261
|
const item = expandTrace(trace);
|
|
1226
1262
|
acc.loops.push(...item.loops);
|
|
1227
1263
|
acc.turns.push(...item.turns);
|
|
@@ -1324,6 +1360,7 @@ export class DebugTrace {
|
|
|
1324
1360
|
const isUsableExisting = (t) => (
|
|
1325
1361
|
t?.sessionId === normalizedSessionId
|
|
1326
1362
|
&& t?.traceId === traceId
|
|
1363
|
+
&& t.active !== false
|
|
1327
1364
|
&& !(turnNumber === 1 && (t.loops || []).some(l => l.loopNumber === 1))
|
|
1328
1365
|
);
|
|
1329
1366
|
// Cache-only: the write path must NEVER touch disk (that was the O(N^2)
|
|
@@ -1374,6 +1411,23 @@ export class DebugTrace {
|
|
|
1374
1411
|
return this.#requestCache.get(requestKey) || null;
|
|
1375
1412
|
}
|
|
1376
1413
|
|
|
1414
|
+
async resumeTrace({ sessionId, turnId } = {}) {
|
|
1415
|
+
if (!sessionId || !turnId) return false;
|
|
1416
|
+
const locator = await readJson(turnLocatorPath(this.#rootDir, sessionId, turnId));
|
|
1417
|
+
let trace = null;
|
|
1418
|
+
if (locator?.requestKey && locator.sessionId === sessionId) {
|
|
1419
|
+
trace = await readRequestDir(requestDirFor(this.#rootDir, sessionId, locator.requestKey));
|
|
1420
|
+
}
|
|
1421
|
+
if (!traceMatchesIdentity(trace, sessionId, turnId)) {
|
|
1422
|
+
trace = (await readTraceSummaries(this.#rootDir, sessionId))
|
|
1423
|
+
.map(item => item.trace)
|
|
1424
|
+
.find(item => traceMatchesIdentity(item, sessionId, turnId)) || null;
|
|
1425
|
+
}
|
|
1426
|
+
if (!trace) return false;
|
|
1427
|
+
this.#requestCache.set(trace.requestKey, trace);
|
|
1428
|
+
return true;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1377
1431
|
#traceSummaries(sessionId = null) {
|
|
1378
1432
|
// Cache-only: #ensureHydrated() has already merged every on-disk trace
|
|
1379
1433
|
// into #requestCache exactly once, so we never touch disk here. This is
|
|
@@ -1448,7 +1502,7 @@ export class DebugTrace {
|
|
|
1448
1502
|
return tracePathFor(this.#rootDir, trace.sessionId || null, trace.requestKey);
|
|
1449
1503
|
}
|
|
1450
1504
|
|
|
1451
|
-
#appendTraceRecord(trace, type, record, { writeMeta = false } = {}) {
|
|
1505
|
+
#appendTraceRecord(trace, type, record, { writeMeta = false, evictAfterWrite = false } = {}) {
|
|
1452
1506
|
if (!this.#acceptingWrites || !trace?.requestKey || !record) return;
|
|
1453
1507
|
this.#requestCache.set(trace.requestKey, trace);
|
|
1454
1508
|
const initialize = !this.#initializedRequestKeys.has(trace.requestKey);
|
|
@@ -1459,6 +1513,7 @@ export class DebugTrace {
|
|
|
1459
1513
|
record: cloneJsonValue(record),
|
|
1460
1514
|
initialize,
|
|
1461
1515
|
writeMeta: !!writeMeta,
|
|
1516
|
+
evictAfterWrite: !!evictAfterWrite,
|
|
1462
1517
|
});
|
|
1463
1518
|
if (writeMeta) {
|
|
1464
1519
|
this.#flushPending();
|
|
@@ -1498,15 +1553,16 @@ export class DebugTrace {
|
|
|
1498
1553
|
for (const entry of entries) {
|
|
1499
1554
|
if (!this.#requestCache.has(entry.trace.requestKey)) continue;
|
|
1500
1555
|
const requestDir = requestDirFor(this.#rootDir, entry.trace.sessionId || null, entry.trace.requestKey);
|
|
1501
|
-
const batch = batches.get(requestDir) || { trace: entry.trace, initialize: false, writeMeta: false, lines: [] };
|
|
1556
|
+
const batch = batches.get(requestDir) || { trace: entry.trace, initialize: false, writeMeta: false, evictAfterWrite: false, lines: [] };
|
|
1502
1557
|
batch.trace = entry.trace;
|
|
1503
1558
|
batch.initialize ||= entry.initialize;
|
|
1504
1559
|
batch.writeMeta ||= entry.writeMeta;
|
|
1560
|
+
batch.evictAfterWrite ||= entry.evictAfterWrite;
|
|
1505
1561
|
batch.lines.push(`${JSON.stringify({ type: entry.type, record: entry.record })}\n`);
|
|
1506
1562
|
batches.set(requestDir, batch);
|
|
1507
1563
|
}
|
|
1508
1564
|
for (const [requestDir, batch] of batches) {
|
|
1509
|
-
const { trace, initialize, writeMeta } = batch;
|
|
1565
|
+
const { trace, initialize, writeMeta, evictAfterWrite } = batch;
|
|
1510
1566
|
const lines = [...batch.lines];
|
|
1511
1567
|
const legacyRequestDir = trace._persistedFormat === 'legacy'
|
|
1512
1568
|
? trace._persistedRequestDir || null
|
|
@@ -1542,6 +1598,7 @@ export class DebugTrace {
|
|
|
1542
1598
|
if (legacyRequestDir && legacyRequestDir !== requestDir) {
|
|
1543
1599
|
await removeRequestDirIfIdentityMatches(legacyRequestDir, trace);
|
|
1544
1600
|
}
|
|
1601
|
+
if (evictAfterWrite) this.#requestCache.delete(trace.requestKey);
|
|
1545
1602
|
} catch (err) {
|
|
1546
1603
|
console.warn('[Yeaft] debug trace append failed:', err?.message || err);
|
|
1547
1604
|
}
|
|
@@ -1721,6 +1778,8 @@ export class DebugTrace {
|
|
|
1721
1778
|
export class NullTrace {
|
|
1722
1779
|
startTurn() { return 'null'; }
|
|
1723
1780
|
endTurn() {}
|
|
1781
|
+
finalizeQuery() {}
|
|
1782
|
+
async resumeTrace() { return false; }
|
|
1724
1783
|
logTool() { return 'null'; }
|
|
1725
1784
|
logEvent() { return 'null'; }
|
|
1726
1785
|
event() { return 'null'; }
|
package/yeaft/engine.js
CHANGED
|
@@ -1972,12 +1972,18 @@ export class Engine {
|
|
|
1972
1972
|
* @yields {EngineEvent}
|
|
1973
1973
|
*/
|
|
1974
1974
|
async *query(params = {}) {
|
|
1975
|
+
const queryTraceId = typeof params.vpTurnId === 'string' && params.vpTurnId
|
|
1976
|
+
? params.vpTurnId : null;
|
|
1975
1977
|
let terminalEmitted = false;
|
|
1978
|
+
let terminalStopReason = 'error';
|
|
1976
1979
|
let lastTurnNumber = 0;
|
|
1977
1980
|
try {
|
|
1978
1981
|
for await (const event of this.#queryLifecycle(params)) {
|
|
1979
1982
|
if (Number.isFinite(event?.turnNumber)) lastTurnNumber = event.turnNumber;
|
|
1980
|
-
if (event?.type === 'turn_end' && event.terminal === true)
|
|
1983
|
+
if (event?.type === 'turn_end' && event.terminal === true) {
|
|
1984
|
+
terminalEmitted = true;
|
|
1985
|
+
terminalStopReason = event.stopReason || terminalStopReason;
|
|
1986
|
+
}
|
|
1981
1987
|
yield event;
|
|
1982
1988
|
}
|
|
1983
1989
|
} catch (err) {
|
|
@@ -2001,6 +2007,13 @@ export class Engine {
|
|
|
2001
2007
|
// hook must not retroactively turn the completed answer into an error.
|
|
2002
2008
|
console.warn('[Engine] post-turn maintenance failed:', err?.message || err);
|
|
2003
2009
|
}
|
|
2010
|
+
} finally {
|
|
2011
|
+
if (queryTraceId && typeof this.#trace?.finalizeQuery === 'function') {
|
|
2012
|
+
this.#trace.finalizeQuery(queryTraceId, {
|
|
2013
|
+
sessionId: params.sessionId || null,
|
|
2014
|
+
stopReason: terminalEmitted ? terminalStopReason : 'interrupted',
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2004
2017
|
}
|
|
2005
2018
|
}
|
|
2006
2019
|
|