@llamaindex/workflow-debugger 0.2.0 → 0.2.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/debugger.v0.2.1.css +10 -0
- package/dist/debugger.v0.2.1.js +633 -0
- package/package.json +7 -1
- package/CHANGELOG.md +0 -205
- package/eslint.config.js +0 -26
- package/index.html +0 -12
- package/postcss.config.js +0 -6
- package/src/App.tsx +0 -18
- package/src/components/code-block.tsx +0 -48
- package/src/components/error-boundary.tsx +0 -85
- package/src/components/json-schema-editor.tsx +0 -291
- package/src/components/run-details-panel.tsx +0 -290
- package/src/components/run-list-panel.tsx +0 -83
- package/src/components/send-event-dialog.tsx +0 -299
- package/src/components/workflow-config-panel.tsx +0 -247
- package/src/components/workflow-debugger.tsx +0 -342
- package/src/components/workflow-visualization.tsx +0 -720
- package/src/index.css +0 -86
- package/src/main.tsx +0 -24
- package/tailwind.config.js +0 -9
- package/tests/json-schema-editor.test.tsx +0 -62
- package/tests/test-setup.ts +0 -1
- package/tsconfig.build.json +0 -5
- package/tsconfig.json +0 -16
- package/ui_sample.png +0 -0
- package/vite.config.ts +0 -46
- package/vitest.config.ts +0 -16
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useWorkflowsClient,
|
|
3
|
-
Input,
|
|
4
|
-
Button,
|
|
5
|
-
Select,
|
|
6
|
-
SelectContent,
|
|
7
|
-
SelectItem,
|
|
8
|
-
SelectTrigger,
|
|
9
|
-
SelectValue,
|
|
10
|
-
Popover,
|
|
11
|
-
PopoverContent,
|
|
12
|
-
PopoverTrigger,
|
|
13
|
-
useWorkflows,
|
|
14
|
-
} from "@llamaindex/ui";
|
|
15
|
-
import { useState, useEffect, useCallback } from "react";
|
|
16
|
-
import { getHealth } from "@llamaindex/workflows-client";
|
|
17
|
-
import { WorkflowConfigPanel } from "./workflow-config-panel";
|
|
18
|
-
import { RunListPanel } from "./run-list-panel";
|
|
19
|
-
import { RunDetailsPanel } from "./run-details-panel";
|
|
20
|
-
import {
|
|
21
|
-
Settings,
|
|
22
|
-
PanelLeftClose,
|
|
23
|
-
PanelLeftOpen,
|
|
24
|
-
PanelRightOpen,
|
|
25
|
-
} from "lucide-react";
|
|
26
|
-
|
|
27
|
-
// Utility to handle keyboard shortcuts
|
|
28
|
-
function useKeyboardShortcut(key: string, callback: () => void, ctrl = true) {
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
const handler = (e: KeyboardEvent) => {
|
|
31
|
-
if (ctrl && e.ctrlKey && e.key.toLowerCase() === key.toLowerCase()) {
|
|
32
|
-
e.preventDefault();
|
|
33
|
-
callback();
|
|
34
|
-
} else if (!ctrl && e.key.toLowerCase() === key.toLowerCase()) {
|
|
35
|
-
e.preventDefault();
|
|
36
|
-
callback();
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
window.addEventListener("keydown", handler);
|
|
40
|
-
return () => window.removeEventListener("keydown", handler);
|
|
41
|
-
}, [key, callback, ctrl]);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Utility to resolve base URL from query param or default
|
|
45
|
-
function resolveBaseUrl(): string {
|
|
46
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
47
|
-
const apiParam = urlParams.get("api");
|
|
48
|
-
|
|
49
|
-
if (apiParam) {
|
|
50
|
-
return apiParam.endsWith("/") ? apiParam.slice(0, -1) : apiParam;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return "http://localhost:8000";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function WorkflowDebugger() {
|
|
57
|
-
const [baseUrl, setBaseUrl] = useState<string>(resolveBaseUrl);
|
|
58
|
-
const [editingUrl, setEditingUrl] = useState<string>(baseUrl);
|
|
59
|
-
const [selectedWorkflow, setSelectedWorkflow] = useState<string | null>(null);
|
|
60
|
-
const [activeHandlerId, setActiveHandlerId] = useState<string | null>(null);
|
|
61
|
-
const { state: workflowsState, sync: syncWorkflows } = useWorkflows();
|
|
62
|
-
// Default to a 3/5 ratio (left/right) => 37.5% / 62.5%
|
|
63
|
-
const [leftPanelWidth, setLeftPanelWidth] = useState(37.5); // percentage
|
|
64
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
65
|
-
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
66
|
-
const [configPanelCollapsed, setConfigPanelCollapsed] = useState(false);
|
|
67
|
-
const [isServerHealthy, setIsServerHealthy] = useState<boolean | null>(null);
|
|
68
|
-
const [connectionError, setConnectionError] = useState<string | null>(null);
|
|
69
|
-
|
|
70
|
-
const workflows = workflowsState.workflows;
|
|
71
|
-
const workflowsClient = useWorkflowsClient();
|
|
72
|
-
|
|
73
|
-
const checkHealth = useCallback(async (): Promise<void> => {
|
|
74
|
-
try {
|
|
75
|
-
setConnectionError(null);
|
|
76
|
-
const { data, error } = await getHealth({ client: workflowsClient });
|
|
77
|
-
if (error) {
|
|
78
|
-
throw new Error(String(error));
|
|
79
|
-
}
|
|
80
|
-
if (data && (data as { status?: string }).status) {
|
|
81
|
-
setIsServerHealthy(true);
|
|
82
|
-
} else {
|
|
83
|
-
setIsServerHealthy(false);
|
|
84
|
-
setConnectionError("Workflow server is unreachable");
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
setIsServerHealthy(false);
|
|
88
|
-
setConnectionError("Workflow server is unreachable");
|
|
89
|
-
}
|
|
90
|
-
}, [workflowsClient]);
|
|
91
|
-
|
|
92
|
-
// Update client config when baseUrl changes
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
workflowsClient.setConfig({ baseUrl });
|
|
95
|
-
checkHealth();
|
|
96
|
-
syncWorkflows();
|
|
97
|
-
}, [baseUrl, workflowsClient, checkHealth, syncWorkflows]);
|
|
98
|
-
|
|
99
|
-
const handleUrlSave = () => {
|
|
100
|
-
const normalizedUrl = editingUrl.endsWith("/")
|
|
101
|
-
? editingUrl.slice(0, -1)
|
|
102
|
-
: editingUrl;
|
|
103
|
-
setBaseUrl(normalizedUrl);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const handleUrlReset = () => {
|
|
107
|
-
const defaultUrl = "http://localhost:8000";
|
|
108
|
-
setBaseUrl(defaultUrl);
|
|
109
|
-
setEditingUrl(defaultUrl);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const handleRunStart = (handlerId: string) => {
|
|
113
|
-
setActiveHandlerId(handlerId);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const handleMouseDown = (e: React.MouseEvent) => {
|
|
117
|
-
e.preventDefault();
|
|
118
|
-
setIsDragging(true);
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const handleMouseMove = useCallback((e: MouseEvent) => {
|
|
122
|
-
const container = document.querySelector(".main-content-area");
|
|
123
|
-
if (!container) return;
|
|
124
|
-
|
|
125
|
-
const containerRect = container.getBoundingClientRect();
|
|
126
|
-
const newLeftWidth =
|
|
127
|
-
((e.clientX - containerRect.left) / containerRect.width) * 100;
|
|
128
|
-
|
|
129
|
-
// Constrain between 20% and 80%
|
|
130
|
-
const clampedWidth = Math.max(20, Math.min(80, newLeftWidth));
|
|
131
|
-
setLeftPanelWidth(clampedWidth);
|
|
132
|
-
}, []);
|
|
133
|
-
|
|
134
|
-
const handleMouseUp = useCallback(() => {
|
|
135
|
-
setIsDragging(false);
|
|
136
|
-
}, []);
|
|
137
|
-
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
if (isDragging) {
|
|
140
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
141
|
-
document.addEventListener("mouseup", handleMouseUp);
|
|
142
|
-
document.body.style.cursor = "col-resize";
|
|
143
|
-
document.body.style.userSelect = "none";
|
|
144
|
-
|
|
145
|
-
return () => {
|
|
146
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
147
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
148
|
-
document.body.style.cursor = "";
|
|
149
|
-
document.body.style.userSelect = "";
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
}, [isDragging, handleMouseMove, handleMouseUp]);
|
|
153
|
-
|
|
154
|
-
// Keyboard shortcut: Ctrl+B to toggle config panel
|
|
155
|
-
useKeyboardShortcut(
|
|
156
|
-
"b",
|
|
157
|
-
useCallback(() => {
|
|
158
|
-
setConfigPanelCollapsed((prev) => !prev);
|
|
159
|
-
}, []),
|
|
160
|
-
true,
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
return (
|
|
164
|
-
<div
|
|
165
|
-
className={`h-screen flex flex-col bg-background ${
|
|
166
|
-
isDragging ? "resize-active" : ""
|
|
167
|
-
}`}
|
|
168
|
-
>
|
|
169
|
-
{/* Slim Titlebar */}
|
|
170
|
-
<div className="flex items-center justify-between h-12 px-4 bg-card border-b border-border">
|
|
171
|
-
<div className="flex items-center gap-4">
|
|
172
|
-
{/* Sidebar Toggle */}
|
|
173
|
-
<Button
|
|
174
|
-
variant="ghost"
|
|
175
|
-
size="sm"
|
|
176
|
-
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
|
|
177
|
-
title={sidebarCollapsed ? "Show sidebar" : "Hide sidebar"}
|
|
178
|
-
>
|
|
179
|
-
{sidebarCollapsed ? (
|
|
180
|
-
<PanelLeftOpen className="h-4 w-4" />
|
|
181
|
-
) : (
|
|
182
|
-
<PanelLeftClose className="h-4 w-4" />
|
|
183
|
-
)}
|
|
184
|
-
</Button>
|
|
185
|
-
|
|
186
|
-
<h1 className="text-md font-semibold">Workflow Debugger</h1>
|
|
187
|
-
</div>
|
|
188
|
-
|
|
189
|
-
{/* Centered Workflow Dropdown */}
|
|
190
|
-
<div className="flex items-center gap-2">
|
|
191
|
-
<label className="text-xs text-muted-foreground font-medium">
|
|
192
|
-
Workflow:
|
|
193
|
-
</label>
|
|
194
|
-
<Select
|
|
195
|
-
value={selectedWorkflow || ""}
|
|
196
|
-
onValueChange={setSelectedWorkflow}
|
|
197
|
-
>
|
|
198
|
-
<SelectTrigger className="w-48">
|
|
199
|
-
<SelectValue placeholder="Select workflow..." />
|
|
200
|
-
</SelectTrigger>
|
|
201
|
-
<SelectContent>
|
|
202
|
-
{Object.keys(workflows).map((workflow) => (
|
|
203
|
-
<SelectItem key={workflow} value={workflow}>
|
|
204
|
-
{workflow}
|
|
205
|
-
</SelectItem>
|
|
206
|
-
))}
|
|
207
|
-
</SelectContent>
|
|
208
|
-
</Select>
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
{/* Connection + Settings */}
|
|
212
|
-
<div className="flex items-center gap-2">
|
|
213
|
-
{isServerHealthy !== null && (
|
|
214
|
-
<span
|
|
215
|
-
className={`${
|
|
216
|
-
isServerHealthy ? "bg-green-500" : "bg-red-500"
|
|
217
|
-
} h-2 w-2 rounded-full`}
|
|
218
|
-
/>
|
|
219
|
-
)}
|
|
220
|
-
{isServerHealthy === false && (
|
|
221
|
-
<span className="text-destructive text-xs">
|
|
222
|
-
{connectionError || "Workflow server is unreachable"}
|
|
223
|
-
</span>
|
|
224
|
-
)}
|
|
225
|
-
<Popover>
|
|
226
|
-
<PopoverTrigger asChild>
|
|
227
|
-
<Button variant="ghost" size="sm">
|
|
228
|
-
<Settings className="h-4 w-4" />
|
|
229
|
-
</Button>
|
|
230
|
-
</PopoverTrigger>
|
|
231
|
-
<PopoverContent className="w-80">
|
|
232
|
-
<div className="space-y-4">
|
|
233
|
-
<div className="space-y-2">
|
|
234
|
-
<h4 className="font-medium text-sm">API Configuration</h4>
|
|
235
|
-
<Input
|
|
236
|
-
value={editingUrl}
|
|
237
|
-
onChange={(e) => setEditingUrl(e.target.value)}
|
|
238
|
-
placeholder="http://localhost:8000"
|
|
239
|
-
/>
|
|
240
|
-
<div className="flex gap-2">
|
|
241
|
-
<Button
|
|
242
|
-
onClick={handleUrlSave}
|
|
243
|
-
variant="outline"
|
|
244
|
-
size="sm"
|
|
245
|
-
disabled={editingUrl === baseUrl}
|
|
246
|
-
>
|
|
247
|
-
Save
|
|
248
|
-
</Button>
|
|
249
|
-
<Button
|
|
250
|
-
onClick={handleUrlReset}
|
|
251
|
-
variant="outline"
|
|
252
|
-
size="sm"
|
|
253
|
-
>
|
|
254
|
-
Reset
|
|
255
|
-
</Button>
|
|
256
|
-
</div>
|
|
257
|
-
<p className="text-xs text-muted-foreground">
|
|
258
|
-
Current: {baseUrl}
|
|
259
|
-
</p>
|
|
260
|
-
</div>
|
|
261
|
-
</div>
|
|
262
|
-
</PopoverContent>
|
|
263
|
-
</Popover>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
|
|
267
|
-
{/* Main Layout */}
|
|
268
|
-
<div className="flex flex-1 overflow-hidden">
|
|
269
|
-
{/* Sidebar - Recent Runs */}
|
|
270
|
-
{!sidebarCollapsed && (
|
|
271
|
-
<div className="w-48 border-r border-border bg-card overflow-hidden transition-all duration-200">
|
|
272
|
-
<RunListPanel
|
|
273
|
-
activeHandlerId={activeHandlerId}
|
|
274
|
-
onHandlerSelect={setActiveHandlerId}
|
|
275
|
-
/>
|
|
276
|
-
</div>
|
|
277
|
-
)}
|
|
278
|
-
|
|
279
|
-
{/* Main Content Area */}
|
|
280
|
-
<div className="flex-1 flex overflow-hidden main-content-area">
|
|
281
|
-
{/* Left Panel - Form */}
|
|
282
|
-
{!configPanelCollapsed ? (
|
|
283
|
-
<>
|
|
284
|
-
<div
|
|
285
|
-
className="border-r border-border overflow-auto"
|
|
286
|
-
style={{ width: `${leftPanelWidth}%` }}
|
|
287
|
-
>
|
|
288
|
-
{selectedWorkflow && (
|
|
289
|
-
<WorkflowConfigPanel
|
|
290
|
-
selectedWorkflow={selectedWorkflow}
|
|
291
|
-
onRunStart={handleRunStart}
|
|
292
|
-
activeHandlerId={activeHandlerId}
|
|
293
|
-
onCollapse={() => setConfigPanelCollapsed(true)}
|
|
294
|
-
/>
|
|
295
|
-
)}
|
|
296
|
-
</div>
|
|
297
|
-
|
|
298
|
-
{/* Resizable Gutter */}
|
|
299
|
-
<div
|
|
300
|
-
className={`w-2 hover:bg-gray-500/20 hover:shadow-lg cursor-col-resize flex-shrink-0 transition-all duration-200 relative group border-l border-r border-border ${
|
|
301
|
-
isDragging ? "shadow-xl" : ""
|
|
302
|
-
}`}
|
|
303
|
-
onMouseDown={handleMouseDown}
|
|
304
|
-
title="Drag to resize panels"
|
|
305
|
-
></div>
|
|
306
|
-
</>
|
|
307
|
-
) : (
|
|
308
|
-
/* Collapsed Config Panel Trigger */
|
|
309
|
-
<div className="w-10 border-r border-border bg-muted/30 flex flex-col items-center justify-center hover:bg-muted/50 transition-colors">
|
|
310
|
-
<Button
|
|
311
|
-
variant="ghost"
|
|
312
|
-
size="sm"
|
|
313
|
-
onClick={() => setConfigPanelCollapsed(false)}
|
|
314
|
-
title="Show configuration panel (Ctrl+B)"
|
|
315
|
-
className="h-10 w-10 p-0"
|
|
316
|
-
>
|
|
317
|
-
<PanelRightOpen className="h-5 w-5" />
|
|
318
|
-
</Button>
|
|
319
|
-
</div>
|
|
320
|
-
)}
|
|
321
|
-
|
|
322
|
-
{/* Right Panel - Results */}
|
|
323
|
-
<div
|
|
324
|
-
className="overflow-auto"
|
|
325
|
-
style={{
|
|
326
|
-
width: configPanelCollapsed
|
|
327
|
-
? "calc(100% - 40px)"
|
|
328
|
-
: `${100 - leftPanelWidth}%`,
|
|
329
|
-
}}
|
|
330
|
-
>
|
|
331
|
-
{activeHandlerId && (
|
|
332
|
-
<RunDetailsPanel
|
|
333
|
-
handlerId={activeHandlerId}
|
|
334
|
-
selectedWorkflow={selectedWorkflow}
|
|
335
|
-
/>
|
|
336
|
-
)}
|
|
337
|
-
</div>
|
|
338
|
-
</div>
|
|
339
|
-
</div>
|
|
340
|
-
</div>
|
|
341
|
-
);
|
|
342
|
-
}
|