@lov3kaizen/agentsea-debugger 0.5.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/LICENSE +21 -0
- package/README.md +364 -0
- package/dist/Recorder-Dc9qR2V2.d.ts +138 -0
- package/dist/ReplayEngine-Dyyqy5bw.d.ts +54 -0
- package/dist/analysis/index.d.ts +4 -0
- package/dist/analysis/index.js +1840 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/diff-CLShBdWe.d.ts +24 -0
- package/dist/index-1W27DYJt.d.ts +366 -0
- package/dist/index-DRrKPSo9.d.ts +233 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +6232 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/agentsea/index.d.ts +8 -0
- package/dist/integrations/agentsea/index.js +5826 -0
- package/dist/integrations/agentsea/index.js.map +1 -0
- package/dist/recording/index.d.ts +91 -0
- package/dist/recording/index.js +1207 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording.types-Ck7pbikw.d.ts +281 -0
- package/dist/replay/index.d.ts +80 -0
- package/dist/replay/index.js +1112 -0
- package/dist/replay/index.js.map +1 -0
- package/dist/replay.types-C9hJizI-.d.ts +76 -0
- package/dist/storage/index.d.ts +135 -0
- package/dist/storage/index.js +588 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/visualization/index.d.ts +123 -0
- package/dist/visualization/index.js +789 -0
- package/dist/visualization/index.js.map +1 -0
- package/dist/visualization.types-dWjGE037.d.ts +98 -0
- package/package.json +99 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
|
|
3
|
+
// src/utils/helpers.ts
|
|
4
|
+
function generateId(prefix = "") {
|
|
5
|
+
const id = nanoid(12);
|
|
6
|
+
return prefix ? `${prefix}_${id}` : id;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// src/visualization/DecisionTree.ts
|
|
10
|
+
var DEFAULT_BUILD_OPTIONS = {
|
|
11
|
+
includeToolCalls: true,
|
|
12
|
+
allSteps: false,
|
|
13
|
+
maxDepth: 50,
|
|
14
|
+
collapseSimilar: false
|
|
15
|
+
};
|
|
16
|
+
var DEFAULT_LAYOUT_OPTIONS = {
|
|
17
|
+
direction: "vertical",
|
|
18
|
+
nodeSpacing: 50,
|
|
19
|
+
levelSpacing: 100,
|
|
20
|
+
nodeWidth: 200,
|
|
21
|
+
nodeHeight: 80
|
|
22
|
+
};
|
|
23
|
+
var DecisionTreeBuilder = class {
|
|
24
|
+
nodes = /* @__PURE__ */ new Map();
|
|
25
|
+
rootId;
|
|
26
|
+
buildOptions;
|
|
27
|
+
layoutOptions;
|
|
28
|
+
constructor(buildOptions, layoutOptions) {
|
|
29
|
+
this.buildOptions = {
|
|
30
|
+
...DEFAULT_BUILD_OPTIONS,
|
|
31
|
+
...buildOptions
|
|
32
|
+
};
|
|
33
|
+
this.layoutOptions = {
|
|
34
|
+
...DEFAULT_LAYOUT_OPTIONS,
|
|
35
|
+
...layoutOptions
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build tree from a recording
|
|
40
|
+
*/
|
|
41
|
+
build(recording) {
|
|
42
|
+
this.clear();
|
|
43
|
+
const rootNode = this.addNode({
|
|
44
|
+
label: "Start",
|
|
45
|
+
type: "action",
|
|
46
|
+
stepIndex: -1,
|
|
47
|
+
metadata: {
|
|
48
|
+
agentId: recording.agentId,
|
|
49
|
+
agentName: recording.agentName
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
this.rootId = rootNode.id;
|
|
53
|
+
let currentParentId = rootNode.id;
|
|
54
|
+
let depth = 0;
|
|
55
|
+
for (const step of recording.steps) {
|
|
56
|
+
if (depth >= this.buildOptions.maxDepth) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
const node = this.processStep(step, currentParentId, depth);
|
|
60
|
+
if (node) {
|
|
61
|
+
if (step.type === "decision") {
|
|
62
|
+
currentParentId = node.id;
|
|
63
|
+
depth++;
|
|
64
|
+
} else if (this.buildOptions.allSteps) {
|
|
65
|
+
currentParentId = node.id;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
this.addNode({
|
|
70
|
+
label: recording.status === "completed" ? "Complete" : "Failed",
|
|
71
|
+
type: "outcome",
|
|
72
|
+
parentId: currentParentId,
|
|
73
|
+
metadata: {
|
|
74
|
+
status: recording.status,
|
|
75
|
+
durationMs: recording.durationMs
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return this.export();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Process a step into a node
|
|
82
|
+
*/
|
|
83
|
+
processStep(step, parentId, depth) {
|
|
84
|
+
if (step.type === "decision" && step.decision) {
|
|
85
|
+
return this.addDecisionNode(step, parentId, depth);
|
|
86
|
+
}
|
|
87
|
+
if (this.buildOptions.includeToolCalls && step.type === "tool-call") {
|
|
88
|
+
return this.addNode({
|
|
89
|
+
label: `Tool: ${step.toolCall?.name ?? "unknown"}`,
|
|
90
|
+
type: "action",
|
|
91
|
+
stepIndex: step.index,
|
|
92
|
+
parentId,
|
|
93
|
+
metadata: {
|
|
94
|
+
toolName: step.toolCall?.name,
|
|
95
|
+
arguments: step.toolCall?.arguments
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (this.buildOptions.allSteps) {
|
|
100
|
+
return this.addNode({
|
|
101
|
+
label: this.getStepLabel(step),
|
|
102
|
+
type: "action",
|
|
103
|
+
stepIndex: step.index,
|
|
104
|
+
parentId
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Add a decision node with branches
|
|
111
|
+
*/
|
|
112
|
+
addDecisionNode(step, parentId, _depth) {
|
|
113
|
+
const decision = step.decision;
|
|
114
|
+
const decisionNode = this.addNode({
|
|
115
|
+
label: decision.reason ?? "Decision",
|
|
116
|
+
type: "decision",
|
|
117
|
+
decision,
|
|
118
|
+
stepIndex: step.index,
|
|
119
|
+
parentId,
|
|
120
|
+
metadata: {
|
|
121
|
+
confidence: decision.confidence,
|
|
122
|
+
optionsCount: decision.options.length
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
for (const option of decision.options) {
|
|
126
|
+
const isChosen = option.id === decision.chosen.id;
|
|
127
|
+
this.addNode({
|
|
128
|
+
label: option.description,
|
|
129
|
+
type: "branch",
|
|
130
|
+
parentId: decisionNode.id,
|
|
131
|
+
metadata: {
|
|
132
|
+
optionId: option.id,
|
|
133
|
+
chosen: isChosen,
|
|
134
|
+
score: option.score
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return decisionNode;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get label for a step
|
|
142
|
+
*/
|
|
143
|
+
getStepLabel(step) {
|
|
144
|
+
switch (step.type) {
|
|
145
|
+
case "input":
|
|
146
|
+
return "User Input";
|
|
147
|
+
case "prompt":
|
|
148
|
+
return "Send Prompt";
|
|
149
|
+
case "response":
|
|
150
|
+
return "LLM Response";
|
|
151
|
+
case "tool-call":
|
|
152
|
+
return `Call: ${step.toolCall?.name}`;
|
|
153
|
+
case "tool-result":
|
|
154
|
+
return `Result: ${step.toolCall?.success ? "OK" : "Failed"}`;
|
|
155
|
+
case "decision":
|
|
156
|
+
return "Decision";
|
|
157
|
+
case "error":
|
|
158
|
+
return `Error: ${step.error?.name}`;
|
|
159
|
+
default:
|
|
160
|
+
return step.type;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Add a node
|
|
165
|
+
*/
|
|
166
|
+
addNode(options) {
|
|
167
|
+
const id = generateId("node");
|
|
168
|
+
const node = {
|
|
169
|
+
id,
|
|
170
|
+
label: options.label,
|
|
171
|
+
type: options.type,
|
|
172
|
+
decision: options.decision,
|
|
173
|
+
stepIndex: options.stepIndex,
|
|
174
|
+
children: [],
|
|
175
|
+
metadata: options.metadata
|
|
176
|
+
};
|
|
177
|
+
this.nodes.set(id, node);
|
|
178
|
+
if (options.parentId) {
|
|
179
|
+
const parent = this.nodes.get(options.parentId);
|
|
180
|
+
if (parent) {
|
|
181
|
+
parent.children.push(id);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!options.parentId && !this.rootId) {
|
|
185
|
+
this.rootId = id;
|
|
186
|
+
}
|
|
187
|
+
return node;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get a node by ID
|
|
191
|
+
*/
|
|
192
|
+
getNode(id) {
|
|
193
|
+
return this.nodes.get(id);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get root node
|
|
197
|
+
*/
|
|
198
|
+
getRoot() {
|
|
199
|
+
return this.rootId ? this.nodes.get(this.rootId) : void 0;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get all nodes
|
|
203
|
+
*/
|
|
204
|
+
getNodes() {
|
|
205
|
+
return Array.from(this.nodes.values());
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get nodes at depth
|
|
209
|
+
*/
|
|
210
|
+
getNodesAtDepth(depth) {
|
|
211
|
+
const result = [];
|
|
212
|
+
const traverse = (nodeId, currentDepth) => {
|
|
213
|
+
const node = this.nodes.get(nodeId);
|
|
214
|
+
if (!node) return;
|
|
215
|
+
if (currentDepth === depth) {
|
|
216
|
+
result.push(node);
|
|
217
|
+
} else {
|
|
218
|
+
for (const childId of node.children) {
|
|
219
|
+
traverse(childId, currentDepth + 1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
if (this.rootId) {
|
|
224
|
+
traverse(this.rootId, 0);
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get tree depth
|
|
230
|
+
*/
|
|
231
|
+
getDepth() {
|
|
232
|
+
let maxDepth = 0;
|
|
233
|
+
const traverse = (nodeId, depth) => {
|
|
234
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
235
|
+
const node = this.nodes.get(nodeId);
|
|
236
|
+
if (node) {
|
|
237
|
+
for (const childId of node.children) {
|
|
238
|
+
traverse(childId, depth + 1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
if (this.rootId) {
|
|
243
|
+
traverse(this.rootId, 0);
|
|
244
|
+
}
|
|
245
|
+
return maxDepth;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Find path to node
|
|
249
|
+
*/
|
|
250
|
+
findPath(nodeId) {
|
|
251
|
+
const path = [];
|
|
252
|
+
const findParent = (targetId) => {
|
|
253
|
+
for (const [id, node] of this.nodes) {
|
|
254
|
+
if (node.children.includes(targetId)) {
|
|
255
|
+
return id;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
};
|
|
260
|
+
let currentId = nodeId;
|
|
261
|
+
while (currentId) {
|
|
262
|
+
const node = this.nodes.get(currentId);
|
|
263
|
+
if (node) {
|
|
264
|
+
path.unshift(node);
|
|
265
|
+
}
|
|
266
|
+
currentId = findParent(currentId);
|
|
267
|
+
}
|
|
268
|
+
return path;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Find nodes by step index
|
|
272
|
+
*/
|
|
273
|
+
findByStepIndex(stepIndex) {
|
|
274
|
+
return this.getNodes().filter((n) => n.stepIndex === stepIndex);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Find decision nodes
|
|
278
|
+
*/
|
|
279
|
+
findDecisionNodes() {
|
|
280
|
+
return this.getNodes().filter((n) => n.type === "decision");
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Clear the tree
|
|
284
|
+
*/
|
|
285
|
+
clear() {
|
|
286
|
+
this.nodes.clear();
|
|
287
|
+
this.rootId = void 0;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Export tree structure
|
|
291
|
+
*/
|
|
292
|
+
export() {
|
|
293
|
+
const root = this.getRoot();
|
|
294
|
+
return {
|
|
295
|
+
id: generateId("tree"),
|
|
296
|
+
root: root ?? {
|
|
297
|
+
id: "empty",
|
|
298
|
+
label: "Empty Tree",
|
|
299
|
+
type: "action",
|
|
300
|
+
children: []
|
|
301
|
+
},
|
|
302
|
+
nodes: this.getNodes(),
|
|
303
|
+
metadata: {
|
|
304
|
+
nodeCount: this.nodes.size,
|
|
305
|
+
depth: this.getDepth(),
|
|
306
|
+
decisionCount: this.findDecisionNodes().length
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Export to Mermaid format
|
|
312
|
+
*/
|
|
313
|
+
toMermaid() {
|
|
314
|
+
const lines = ["graph TD"];
|
|
315
|
+
const traverse = (nodeId) => {
|
|
316
|
+
const node = this.nodes.get(nodeId);
|
|
317
|
+
if (!node) return;
|
|
318
|
+
const shape = this.getMermaidShape(node);
|
|
319
|
+
lines.push(` ${nodeId}${shape}`);
|
|
320
|
+
for (const childId of node.children) {
|
|
321
|
+
const child = this.nodes.get(childId);
|
|
322
|
+
const edgeLabel = child?.metadata?.chosen ? " --> |chosen|" : " --> ";
|
|
323
|
+
lines.push(` ${nodeId}${edgeLabel}${childId}`);
|
|
324
|
+
traverse(childId);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
if (this.rootId) {
|
|
328
|
+
traverse(this.rootId);
|
|
329
|
+
}
|
|
330
|
+
return lines.join("\n");
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get Mermaid shape for node
|
|
334
|
+
*/
|
|
335
|
+
getMermaidShape(node) {
|
|
336
|
+
const label = node.label.replace(/"/g, "'");
|
|
337
|
+
switch (node.type) {
|
|
338
|
+
case "decision":
|
|
339
|
+
return `{${label}}`;
|
|
340
|
+
case "outcome":
|
|
341
|
+
return `((${label}))`;
|
|
342
|
+
case "branch":
|
|
343
|
+
return `[/${label}/]`;
|
|
344
|
+
default:
|
|
345
|
+
return `[${label}]`;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Calculate layout positions
|
|
350
|
+
*/
|
|
351
|
+
calculateLayout() {
|
|
352
|
+
const positions = /* @__PURE__ */ new Map();
|
|
353
|
+
const levelWidths = [];
|
|
354
|
+
const levelPositions = [];
|
|
355
|
+
const calculateLevelWidths = (nodeId, level) => {
|
|
356
|
+
if (!levelWidths[level]) {
|
|
357
|
+
levelWidths[level] = 0;
|
|
358
|
+
}
|
|
359
|
+
levelWidths[level]++;
|
|
360
|
+
const node = this.nodes.get(nodeId);
|
|
361
|
+
if (node) {
|
|
362
|
+
for (const childId of node.children) {
|
|
363
|
+
calculateLevelWidths(childId, level + 1);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
if (this.rootId) {
|
|
368
|
+
calculateLevelWidths(this.rootId, 0);
|
|
369
|
+
}
|
|
370
|
+
for (let i = 0; i < levelWidths.length; i++) {
|
|
371
|
+
levelPositions[i] = 0;
|
|
372
|
+
}
|
|
373
|
+
const assignPositions = (nodeId, level) => {
|
|
374
|
+
const node = this.nodes.get(nodeId);
|
|
375
|
+
if (!node) return;
|
|
376
|
+
const levelWidth = levelWidths[level] * this.layoutOptions.nodeSpacing;
|
|
377
|
+
const startX = -levelWidth / 2;
|
|
378
|
+
const x = startX + levelPositions[level] * this.layoutOptions.nodeSpacing;
|
|
379
|
+
const y = level * this.layoutOptions.levelSpacing;
|
|
380
|
+
positions.set(nodeId, { x, y });
|
|
381
|
+
levelPositions[level]++;
|
|
382
|
+
for (const childId of node.children) {
|
|
383
|
+
assignPositions(childId, level + 1);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
if (this.rootId) {
|
|
387
|
+
assignPositions(this.rootId, 0);
|
|
388
|
+
}
|
|
389
|
+
return positions;
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
function createDecisionTreeBuilder(buildOptions, layoutOptions) {
|
|
393
|
+
return new DecisionTreeBuilder(buildOptions, layoutOptions);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/visualization/FlowGraph.ts
|
|
397
|
+
var DEFAULT_NODE_STYLES = {
|
|
398
|
+
input: { fill: "#e3f2fd", shape: "rectangle" },
|
|
399
|
+
prompt: { fill: "#f3e5f5", shape: "rectangle" },
|
|
400
|
+
response: { fill: "#e8f5e9", shape: "rectangle" },
|
|
401
|
+
output: { fill: "#c8e6c9", shape: "rectangle" },
|
|
402
|
+
"tool-call": { fill: "#fff3e0", shape: "hexagon" },
|
|
403
|
+
"tool-result": { fill: "#fff8e1", shape: "rectangle" },
|
|
404
|
+
decision: { fill: "#fce4ec", shape: "diamond" },
|
|
405
|
+
error: { fill: "#ffebee", shape: "rectangle" },
|
|
406
|
+
"memory-read": { fill: "#e0f7fa", shape: "ellipse" },
|
|
407
|
+
"memory-write": { fill: "#e0f2f1", shape: "ellipse" },
|
|
408
|
+
handoff: { fill: "#ede7f6", shape: "hexagon" },
|
|
409
|
+
delegation: { fill: "#e8eaf6", shape: "hexagon" },
|
|
410
|
+
custom: { fill: "#fafafa", shape: "rectangle" }
|
|
411
|
+
};
|
|
412
|
+
var DEFAULT_BUILD_OPTIONS2 = {
|
|
413
|
+
groupSimilar: false,
|
|
414
|
+
includeDurations: true,
|
|
415
|
+
includeToolDetails: true,
|
|
416
|
+
maxNodes: 200,
|
|
417
|
+
nodeStyles: {}
|
|
418
|
+
};
|
|
419
|
+
var FlowGraphBuilder = class {
|
|
420
|
+
nodes = /* @__PURE__ */ new Map();
|
|
421
|
+
edges = /* @__PURE__ */ new Map();
|
|
422
|
+
options;
|
|
423
|
+
nodeStyles;
|
|
424
|
+
constructor(options) {
|
|
425
|
+
this.options = {
|
|
426
|
+
...DEFAULT_BUILD_OPTIONS2,
|
|
427
|
+
...options
|
|
428
|
+
};
|
|
429
|
+
this.nodeStyles = {
|
|
430
|
+
...DEFAULT_NODE_STYLES,
|
|
431
|
+
...options?.nodeStyles
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Build graph from a recording
|
|
436
|
+
*/
|
|
437
|
+
build(recording) {
|
|
438
|
+
this.clear();
|
|
439
|
+
const startNode = this.addNode({
|
|
440
|
+
label: "Start",
|
|
441
|
+
type: "input",
|
|
442
|
+
metadata: {
|
|
443
|
+
agentId: recording.agentId,
|
|
444
|
+
agentName: recording.agentName
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
let previousNodeId = startNode.id;
|
|
448
|
+
let nodeCount = 1;
|
|
449
|
+
for (const step of recording.steps) {
|
|
450
|
+
if (nodeCount >= this.options.maxNodes) {
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
if (this.options.groupSimilar && previousNodeId) {
|
|
454
|
+
const prevNode = this.nodes.get(previousNodeId);
|
|
455
|
+
if (prevNode && prevNode.type === step.type) {
|
|
456
|
+
this.updateNodeForGroup(prevNode, step);
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
const node = this.createNodeFromStep(step);
|
|
461
|
+
this.nodes.set(node.id, node);
|
|
462
|
+
if (previousNodeId) {
|
|
463
|
+
this.addEdge(previousNodeId, node.id, {
|
|
464
|
+
durationMs: step.durationMs
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
previousNodeId = node.id;
|
|
468
|
+
nodeCount++;
|
|
469
|
+
}
|
|
470
|
+
const endNode = this.addNode({
|
|
471
|
+
label: recording.status === "completed" ? "Complete" : "Failed",
|
|
472
|
+
type: recording.status === "completed" ? "response" : "error",
|
|
473
|
+
metadata: {
|
|
474
|
+
status: recording.status,
|
|
475
|
+
durationMs: recording.durationMs
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
if (previousNodeId) {
|
|
479
|
+
this.addEdge(previousNodeId, endNode.id);
|
|
480
|
+
}
|
|
481
|
+
return this.export();
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Create a node from a step
|
|
485
|
+
*/
|
|
486
|
+
createNodeFromStep(step) {
|
|
487
|
+
const style = this.nodeStyles[step.type] ?? DEFAULT_NODE_STYLES.custom;
|
|
488
|
+
const node = {
|
|
489
|
+
id: generateId("node"),
|
|
490
|
+
label: this.getStepLabel(step),
|
|
491
|
+
type: step.type,
|
|
492
|
+
stepIndex: step.index,
|
|
493
|
+
style,
|
|
494
|
+
metadata: this.getStepMetadata(step)
|
|
495
|
+
};
|
|
496
|
+
return node;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Get label for a step
|
|
500
|
+
*/
|
|
501
|
+
getStepLabel(step) {
|
|
502
|
+
switch (step.type) {
|
|
503
|
+
case "input":
|
|
504
|
+
return "User Input";
|
|
505
|
+
case "prompt":
|
|
506
|
+
return "Prompt";
|
|
507
|
+
case "response":
|
|
508
|
+
return "Response";
|
|
509
|
+
case "tool-call":
|
|
510
|
+
if (this.options.includeToolDetails && step.toolCall) {
|
|
511
|
+
return `${step.toolCall.name}()`;
|
|
512
|
+
}
|
|
513
|
+
return "Tool Call";
|
|
514
|
+
case "tool-result":
|
|
515
|
+
return step.toolCall?.success ? "Success" : "Failed";
|
|
516
|
+
case "decision":
|
|
517
|
+
return step.decision?.reason ?? "Decision";
|
|
518
|
+
case "error":
|
|
519
|
+
return step.error?.name ?? "Error";
|
|
520
|
+
case "memory-read":
|
|
521
|
+
return "Read Memory";
|
|
522
|
+
case "memory-write":
|
|
523
|
+
return "Write Memory";
|
|
524
|
+
case "handoff":
|
|
525
|
+
case "delegation":
|
|
526
|
+
return "Hand Off";
|
|
527
|
+
default:
|
|
528
|
+
return step.type;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Get metadata for a step
|
|
533
|
+
*/
|
|
534
|
+
getStepMetadata(step) {
|
|
535
|
+
const metadata = {
|
|
536
|
+
stepIndex: step.index,
|
|
537
|
+
timestamp: step.timestamp
|
|
538
|
+
};
|
|
539
|
+
if (this.options.includeDurations && step.durationMs) {
|
|
540
|
+
metadata.durationMs = step.durationMs;
|
|
541
|
+
}
|
|
542
|
+
if (step.toolCall) {
|
|
543
|
+
metadata.toolName = step.toolCall.name;
|
|
544
|
+
if (this.options.includeToolDetails) {
|
|
545
|
+
metadata.toolArguments = step.toolCall.arguments;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (step.decision) {
|
|
549
|
+
metadata.confidence = step.decision.confidence;
|
|
550
|
+
metadata.chosenOption = step.decision.chosen.description;
|
|
551
|
+
}
|
|
552
|
+
if (step.error) {
|
|
553
|
+
metadata.errorMessage = step.error.message;
|
|
554
|
+
}
|
|
555
|
+
return metadata;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Update node when grouping similar steps
|
|
559
|
+
*/
|
|
560
|
+
updateNodeForGroup(node, step) {
|
|
561
|
+
const count = node.metadata?.groupCount ?? 1;
|
|
562
|
+
node.metadata = {
|
|
563
|
+
...node.metadata,
|
|
564
|
+
groupCount: count + 1,
|
|
565
|
+
lastStepIndex: step.index
|
|
566
|
+
};
|
|
567
|
+
node.label = `${node.label} (${count + 1})`;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Add a node
|
|
571
|
+
*/
|
|
572
|
+
addNode(options) {
|
|
573
|
+
const id = generateId("node");
|
|
574
|
+
const style = options.style ?? this.nodeStyles[options.type] ?? DEFAULT_NODE_STYLES.custom;
|
|
575
|
+
const node = {
|
|
576
|
+
id,
|
|
577
|
+
label: options.label,
|
|
578
|
+
type: options.type,
|
|
579
|
+
stepIndex: options.stepIndex,
|
|
580
|
+
style,
|
|
581
|
+
metadata: options.metadata
|
|
582
|
+
};
|
|
583
|
+
this.nodes.set(id, node);
|
|
584
|
+
return node;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Add an edge
|
|
588
|
+
*/
|
|
589
|
+
addEdge(sourceId, targetId, options) {
|
|
590
|
+
const id = generateId("edge");
|
|
591
|
+
const edge = {
|
|
592
|
+
id,
|
|
593
|
+
source: sourceId,
|
|
594
|
+
target: targetId,
|
|
595
|
+
label: options?.label,
|
|
596
|
+
style: options?.style,
|
|
597
|
+
metadata: options?.durationMs ? { durationMs: options.durationMs } : void 0
|
|
598
|
+
};
|
|
599
|
+
this.edges.set(id, edge);
|
|
600
|
+
return edge;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Get a node by ID
|
|
604
|
+
*/
|
|
605
|
+
getNode(id) {
|
|
606
|
+
return this.nodes.get(id);
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Get all nodes
|
|
610
|
+
*/
|
|
611
|
+
getNodes() {
|
|
612
|
+
return Array.from(this.nodes.values());
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Get nodes by type
|
|
616
|
+
*/
|
|
617
|
+
getNodesByType(type) {
|
|
618
|
+
return this.getNodes().filter((n) => n.type === type);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Get an edge by ID
|
|
622
|
+
*/
|
|
623
|
+
getEdge(id) {
|
|
624
|
+
return this.edges.get(id);
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Get all edges
|
|
628
|
+
*/
|
|
629
|
+
getEdges() {
|
|
630
|
+
return Array.from(this.edges.values());
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Get edges from a node
|
|
634
|
+
*/
|
|
635
|
+
getEdgesFrom(nodeId) {
|
|
636
|
+
return this.getEdges().filter((e) => e.source === nodeId);
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Get edges to a node
|
|
640
|
+
*/
|
|
641
|
+
getEdgesTo(nodeId) {
|
|
642
|
+
return this.getEdges().filter((e) => e.target === nodeId);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Find path between nodes
|
|
646
|
+
*/
|
|
647
|
+
findPath(startId, endId) {
|
|
648
|
+
const visited = /* @__PURE__ */ new Set();
|
|
649
|
+
const path = [];
|
|
650
|
+
const dfs = (currentId) => {
|
|
651
|
+
if (visited.has(currentId)) {
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
visited.add(currentId);
|
|
655
|
+
const node = this.nodes.get(currentId);
|
|
656
|
+
if (!node) {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
path.push(node);
|
|
660
|
+
if (currentId === endId) {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
for (const edge of this.getEdgesFrom(currentId)) {
|
|
664
|
+
if (dfs(edge.target)) {
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
path.pop();
|
|
669
|
+
return false;
|
|
670
|
+
};
|
|
671
|
+
return dfs(startId) ? path : null;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Get graph statistics
|
|
675
|
+
*/
|
|
676
|
+
getStats() {
|
|
677
|
+
const nodesByType = {};
|
|
678
|
+
let totalDuration = 0;
|
|
679
|
+
let durationCount = 0;
|
|
680
|
+
for (const node of this.nodes.values()) {
|
|
681
|
+
nodesByType[node.type] = (nodesByType[node.type] ?? 0) + 1;
|
|
682
|
+
const duration = node.metadata?.durationMs;
|
|
683
|
+
if (duration) {
|
|
684
|
+
totalDuration += duration;
|
|
685
|
+
durationCount++;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
nodeCount: this.nodes.size,
|
|
690
|
+
edgeCount: this.edges.size,
|
|
691
|
+
nodesByType,
|
|
692
|
+
avgDuration: durationCount > 0 ? totalDuration / durationCount : 0
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Clear the graph
|
|
697
|
+
*/
|
|
698
|
+
clear() {
|
|
699
|
+
this.nodes.clear();
|
|
700
|
+
this.edges.clear();
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Export graph structure
|
|
704
|
+
*/
|
|
705
|
+
export() {
|
|
706
|
+
return {
|
|
707
|
+
id: generateId("graph"),
|
|
708
|
+
nodes: this.getNodes(),
|
|
709
|
+
edges: this.getEdges(),
|
|
710
|
+
metadata: this.getStats()
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Export to Mermaid format
|
|
715
|
+
*/
|
|
716
|
+
toMermaid() {
|
|
717
|
+
const lines = ["flowchart TD"];
|
|
718
|
+
for (const node of this.nodes.values()) {
|
|
719
|
+
const shape = this.getMermaidShape(node);
|
|
720
|
+
lines.push(` ${node.id}${shape}`);
|
|
721
|
+
}
|
|
722
|
+
for (const edge of this.edges.values()) {
|
|
723
|
+
const label = edge.label ? ` |${edge.label}|` : "";
|
|
724
|
+
const arrow = edge.style?.lineType === "dashed" ? "-.->" : "-->";
|
|
725
|
+
lines.push(` ${edge.source}${arrow}${label}${edge.target}`);
|
|
726
|
+
}
|
|
727
|
+
return lines.join("\n");
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Get Mermaid shape for node
|
|
731
|
+
*/
|
|
732
|
+
getMermaidShape(node) {
|
|
733
|
+
const label = node.label.replace(/"/g, "'");
|
|
734
|
+
switch (node.style?.shape) {
|
|
735
|
+
case "ellipse":
|
|
736
|
+
return `([${label}])`;
|
|
737
|
+
case "diamond":
|
|
738
|
+
return `{${label}}`;
|
|
739
|
+
case "hexagon":
|
|
740
|
+
return `{{${label}}}`;
|
|
741
|
+
default:
|
|
742
|
+
return `[${label}]`;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Export to DOT format (Graphviz)
|
|
747
|
+
*/
|
|
748
|
+
toDOT() {
|
|
749
|
+
const lines = ["digraph G {", " rankdir=TB;"];
|
|
750
|
+
for (const node of this.nodes.values()) {
|
|
751
|
+
const shape = this.getDOTShape(node);
|
|
752
|
+
const fill = node.style?.fill ?? "#ffffff";
|
|
753
|
+
lines.push(
|
|
754
|
+
` "${node.id}" [label="${node.label}", shape=${shape}, style=filled, fillcolor="${fill}"];`
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
for (const edge of this.edges.values()) {
|
|
758
|
+
const label = edge.label ? `, label="${edge.label}"` : "";
|
|
759
|
+
const style = edge.style?.lineType === "dashed" ? ", style=dashed" : "";
|
|
760
|
+
lines.push(
|
|
761
|
+
` "${edge.source}" -> "${edge.target}" [${label}${style}];`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
lines.push("}");
|
|
765
|
+
return lines.join("\n");
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Get DOT shape for node
|
|
769
|
+
*/
|
|
770
|
+
getDOTShape(node) {
|
|
771
|
+
switch (node.style?.shape) {
|
|
772
|
+
case "ellipse":
|
|
773
|
+
return "ellipse";
|
|
774
|
+
case "diamond":
|
|
775
|
+
return "diamond";
|
|
776
|
+
case "hexagon":
|
|
777
|
+
return "hexagon";
|
|
778
|
+
default:
|
|
779
|
+
return "box";
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
function createFlowGraphBuilder(options) {
|
|
784
|
+
return new FlowGraphBuilder(options);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export { DecisionTreeBuilder, FlowGraphBuilder, createDecisionTreeBuilder, createFlowGraphBuilder };
|
|
788
|
+
//# sourceMappingURL=index.js.map
|
|
789
|
+
//# sourceMappingURL=index.js.map
|