@yeaft/webchat-agent 1.0.19 → 1.0.20
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 +36 -38
package/package.json
CHANGED
package/yeaft/debug-trace.js
CHANGED
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
statSync,
|
|
22
22
|
writeFileSync,
|
|
23
23
|
} from 'fs';
|
|
24
|
-
import { rename as renameAsync, writeFile as writeFileAsync } from 'fs/promises';
|
|
25
24
|
import { basename, dirname, extname, join } from 'path';
|
|
26
25
|
import { randomUUID } from 'crypto';
|
|
27
26
|
|
|
@@ -33,7 +32,8 @@ const MAX_TEXT_BYTES = 1024 * 1024;
|
|
|
33
32
|
const MAX_TOOL_INPUT = 10 * 1024;
|
|
34
33
|
const MAX_INLINE_VALUE_BYTES = 1024 * 1024;
|
|
35
34
|
const MAX_RAW_REQUEST_BYTES = 2 * 1024 * 1024;
|
|
36
|
-
const
|
|
35
|
+
const TRACE_FLUSH_INTERVAL_MS = 5_000;
|
|
36
|
+
const TRACE_FLUSH_DIRTY_LOOPS = 10;
|
|
37
37
|
|
|
38
38
|
function isPlainObject(value) {
|
|
39
39
|
return value && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -66,13 +66,6 @@ function atomicWriteJson(filePath, value) {
|
|
|
66
66
|
renameSync(tmp, filePath);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
async function atomicWriteJsonAsync(filePath, value) {
|
|
70
|
-
ensureDir(dirname(filePath));
|
|
71
|
-
const tmp = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
72
|
-
await writeFileAsync(tmp, JSON.stringify(value), 'utf8');
|
|
73
|
-
await renameAsync(tmp, filePath);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
69
|
function readJson(filePath) {
|
|
77
70
|
try {
|
|
78
71
|
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
@@ -541,10 +534,10 @@ export class DebugTrace {
|
|
|
541
534
|
#turnIndex = new Map();
|
|
542
535
|
/** @type {Map<string, object>} */
|
|
543
536
|
#requestCache = new Map();
|
|
544
|
-
/** @type {Map<string, object>} */
|
|
537
|
+
/** @type {Map<string, { trace: object, dirtyLoops: number, firstDirtyAt: number }>} */
|
|
545
538
|
#pendingWrites = new Map();
|
|
546
|
-
/** @type {
|
|
547
|
-
#
|
|
539
|
+
/** @type {NodeJS.Timeout|null} */
|
|
540
|
+
#flushTimer = null;
|
|
548
541
|
/** @type {number} */
|
|
549
542
|
#sequence = 0;
|
|
550
543
|
|
|
@@ -626,7 +619,7 @@ export class DebugTrace {
|
|
|
626
619
|
trace.updatedAt = loop.at;
|
|
627
620
|
trace.active = info.stopReason ? !['end_turn', 'error', 'aborted'].includes(String(info.stopReason)) : false;
|
|
628
621
|
trace._lastSnapshot = snapshot;
|
|
629
|
-
|
|
622
|
+
this.#markDirty(trace, { dirtyLoops: 1, force: !trace.active });
|
|
630
623
|
}
|
|
631
624
|
|
|
632
625
|
logTool(turnId, { toolName, toolCallId = null, toolInput = null, toolOutput = null, durationMs = null, isError = false } = {}) {
|
|
@@ -649,7 +642,7 @@ export class DebugTrace {
|
|
|
649
642
|
createdAt: Date.now(),
|
|
650
643
|
});
|
|
651
644
|
trace.updatedAt = Date.now();
|
|
652
|
-
|
|
645
|
+
this.#markDirty(trace, { dirtyLoops: 0, force: !trace.active });
|
|
653
646
|
return id;
|
|
654
647
|
}
|
|
655
648
|
|
|
@@ -888,39 +881,44 @@ export class DebugTrace {
|
|
|
888
881
|
return toWrite;
|
|
889
882
|
}
|
|
890
883
|
|
|
891
|
-
#
|
|
884
|
+
#markDirty(trace, { dirtyLoops = 0, force = false } = {}) {
|
|
892
885
|
if (!trace?.requestKey) return;
|
|
893
886
|
const key = this.#traceWriteKey(trace);
|
|
894
|
-
this.#pendingWrites.
|
|
887
|
+
const existing = this.#pendingWrites.get(key);
|
|
888
|
+
const now = Date.now();
|
|
889
|
+
const item = existing || { trace, dirtyLoops: 0, firstDirtyAt: now };
|
|
890
|
+
item.trace = trace;
|
|
891
|
+
item.dirtyLoops += Math.max(0, Number(dirtyLoops) || 0);
|
|
892
|
+
this.#pendingWrites.set(key, item);
|
|
895
893
|
this.#requestCache.set(trace.requestKey, trace);
|
|
896
|
-
if (this.#flushTimers.has(key)) return;
|
|
897
|
-
const timer = setTimeout(() => this.#flushOneAsync(key), TRACE_FLUSH_DELAY_MS);
|
|
898
|
-
this.#flushTimers.set(key, timer);
|
|
899
|
-
}
|
|
900
894
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
this.#flushTimers.delete(key);
|
|
905
|
-
const trace = this.#pendingWrites.get(key);
|
|
906
|
-
if (!trace) return;
|
|
907
|
-
this.#pendingWrites.delete(key);
|
|
908
|
-
try {
|
|
909
|
-
await atomicWriteJsonAsync(this.#traceFile(trace), this.#serializableTrace(trace));
|
|
910
|
-
this.#pruneSession(trace.sessionId || null, REQUEST_RETENTION);
|
|
911
|
-
} catch (err) {
|
|
912
|
-
console.warn('[Yeaft] debug trace write failed:', err?.message || err);
|
|
895
|
+
if (force || item.dirtyLoops >= TRACE_FLUSH_DIRTY_LOOPS) {
|
|
896
|
+
this.#flushPendingSync();
|
|
897
|
+
return;
|
|
913
898
|
}
|
|
899
|
+
this.#scheduleFlushTimer(now);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
#scheduleFlushTimer(now = Date.now()) {
|
|
903
|
+
if (this.#flushTimer || this.#pendingWrites.size === 0) return;
|
|
904
|
+
const oldestDirtyAt = Math.min(...Array.from(this.#pendingWrites.values()).map(item => item.firstDirtyAt || now));
|
|
905
|
+
const dueIn = Math.max(0, TRACE_FLUSH_INTERVAL_MS - (now - oldestDirtyAt));
|
|
906
|
+
this.#flushTimer = setTimeout(() => {
|
|
907
|
+
this.#flushTimer = null;
|
|
908
|
+
this.#flushPendingSync();
|
|
909
|
+
}, dueIn);
|
|
910
|
+
if (typeof this.#flushTimer.unref === 'function') this.#flushTimer.unref();
|
|
914
911
|
}
|
|
915
912
|
|
|
916
913
|
#flushPendingSync() {
|
|
917
|
-
|
|
914
|
+
if (this.#flushTimer) {
|
|
915
|
+
clearTimeout(this.#flushTimer);
|
|
916
|
+
this.#flushTimer = null;
|
|
917
|
+
}
|
|
918
|
+
const entries = Array.from(this.#pendingWrites.values());
|
|
918
919
|
if (entries.length === 0) return;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
if (timer) clearTimeout(timer);
|
|
922
|
-
this.#flushTimers.delete(key);
|
|
923
|
-
this.#pendingWrites.delete(key);
|
|
920
|
+
this.#pendingWrites.clear();
|
|
921
|
+
for (const { trace } of entries) {
|
|
924
922
|
try {
|
|
925
923
|
atomicWriteJson(this.#traceFile(trace), this.#serializableTrace(trace));
|
|
926
924
|
} catch (err) {
|