|
319 | 319 | let selected = { type: null, id: null }; |
320 | 320 |
|
321 | 321 | // build graph & interaction |
322 | | - let cy, wf; |
| 322 | + let cy, wf, assistant_json; |
323 | 323 | (async () => { |
324 | 324 | assistant_json = await (await fetch("/workflow")).json(); |
325 | 325 | renderYAML(panes.Info, assistant_json); |
|
331 | 331 | ); |
332 | 332 | Object.values(wf.nodes).forEach((n) => { |
333 | 333 | elems.push({ data: { id: n.name, label: n.name, type: "node" } }); |
334 | | - if (n.command) { |
335 | | - const commandName = Object.keys(n.command)[0]; |
336 | | - const toolName = n.command[commandName].name || commandName; |
| 334 | + if (n.tool) { |
| 335 | + const toolName = n.tool.name; |
337 | 336 | const toolId = `${n.name}#tool#${toolName}`; |
338 | 337 |
|
339 | 338 | elems.push({ data: { id: toolId, label: toolName, type: "tool" } }); |
|
352 | 351 | }, |
353 | 352 | }); |
354 | 353 | } |
355 | | - n.publish_to.forEach((t) => |
| 354 | + n.publish_to.forEach((topicName) => |
356 | 355 | elems.push({ |
357 | 356 | data: { |
358 | | - id: `${n.name}->${t.name}`, |
| 357 | + id: `${n.name}->${topicName}`, |
359 | 358 | source: n.name, |
360 | | - target: t.name, |
| 359 | + target: topicName, |
361 | 360 | }, |
362 | 361 | }) |
363 | 362 | ); |
364 | 363 | n.subscribed_expressions.forEach((expr) => { |
365 | 364 | // Recursively extract all topics from the expression tree |
366 | 365 | function extractTopics(expression) { |
367 | 366 | const topics = []; |
368 | | - |
| 367 | + |
| 368 | + // If this expression has a topic field, it's the topic name (string) |
369 | 369 | if (expression.topic) { |
370 | 370 | topics.push(expression.topic); |
371 | 371 | } |
372 | | - |
| 372 | + |
| 373 | + // Check left side of expression (for AND/OR operations) |
373 | 374 | if (expression.left) { |
374 | | - if (expression.left.topic) { |
375 | | - topics.push(expression.left.topic); |
376 | | - } else { |
377 | | - // Recursively extract from left expression |
378 | | - topics.push(...extractTopics(expression.left)); |
379 | | - } |
| 375 | + topics.push(...extractTopics(expression.left)); |
380 | 376 | } |
381 | | - |
| 377 | + |
| 378 | + // Check right side of expression (for AND/OR operations) |
382 | 379 | if (expression.right) { |
383 | | - if (expression.right.topic) { |
384 | | - topics.push(expression.right.topic); |
385 | | - } else { |
386 | | - // Recursively extract from right expression |
387 | | - topics.push(...extractTopics(expression.right)); |
388 | | - } |
| 380 | + topics.push(...extractTopics(expression.right)); |
389 | 381 | } |
390 | | - |
| 382 | + |
391 | 383 | return topics; |
392 | 384 | } |
393 | | - |
| 385 | + |
394 | 386 | const allTopics = extractTopics(expr); |
395 | | - allTopics.forEach((topic) => { |
| 387 | + allTopics.forEach((topicName) => { |
396 | 388 | elems.push({ |
397 | 389 | data: { |
398 | | - id: `${topic.name}->${n.name}`, |
399 | | - source: topic.name, |
| 390 | + id: `${topicName}->${n.name}`, |
| 391 | + source: topicName, |
400 | 392 | target: n.name, |
401 | 393 | }, |
402 | 394 | }); |
|
625 | 617 | }); |
626 | 618 |
|
627 | 619 | // node/tool/topic click |
628 | | - cy.on("tap", "node", async (evt) => { |
| 620 | + cy.on("tap", "node", (evt) => { |
629 | 621 | // remove prior highlights |
630 | 622 | cy.elements().removeClass("highlighted"); |
631 | 623 | // highlight this element |
|
636 | 628 | if (currentTab === "Info") { |
637 | 629 | if (typ === "tool") { |
638 | 630 | const node_name = raw.split("#tool#")[0]; // Extract node name from node_name#tool#tool_name |
639 | | - renderYAML(panes.Info, wf.nodes[node_name].command); |
| 631 | + renderYAML(panes.Info, wf.nodes[node_name].tool); |
640 | 632 | } else if (typ === "node") { |
641 | 633 | renderYAML(panes.Info, wf.nodes[raw]); |
642 | 634 | } else { |
643 | 635 | renderYAML(panes.Info, wf.topics[raw]); |
644 | 636 | } |
645 | 637 | } else if (currentTab === "Event") { |
646 | | - showTab("Event"); |
647 | | - await document.querySelector('[data-tab="Event"]').onclick(); |
| 638 | + // Manually trigger the event filtering and rendering |
| 639 | + let evs = eventsCache[convSelect.value] || []; |
| 640 | + |
| 641 | + // apply element filters |
| 642 | + if (selected.type === "node") { |
| 643 | + evs = evs.filter( |
| 644 | + (e) => |
| 645 | + ["NodeInvoke", "NodeRespond"].includes(e.event_type) && |
| 646 | + e.name === selected.id |
| 647 | + ); |
| 648 | + } |
| 649 | + if (selected.type === "tool") { |
| 650 | + const toolName = selected.id.split("#tool#")[1]; |
| 651 | + evs = evs.filter( |
| 652 | + (e) => |
| 653 | + ["ToolInvoke", "ToolRespond"].includes(e.event_type) && |
| 654 | + e.name === toolName |
| 655 | + ); |
| 656 | + } |
| 657 | + if (selected.type === "topic") { |
| 658 | + evs = evs.filter( |
| 659 | + (e) => |
| 660 | + ["PublishToTopic", "ConsumeFromTopic"].includes(e.event_type) && |
| 661 | + e.name === selected.id |
| 662 | + ); |
| 663 | + } |
| 664 | + if (reqSelect.value !== "All") { |
| 665 | + evs = evs.filter( |
| 666 | + (e) => e.invoke_context.assistant_request_id === reqSelect.value |
| 667 | + ); |
| 668 | + } |
| 669 | + |
| 670 | + // render according to drop‑down choice |
| 671 | + switch (eventView.value) { |
| 672 | + case "formed": |
| 673 | + renderFormedEvents(evs); |
| 674 | + break; |
| 675 | + default: |
| 676 | + renderRawEvents(evs); |
| 677 | + } |
648 | 678 | } |
649 | 679 | }); |
650 | 680 | })(); |
|
691 | 721 | return ""; |
692 | 722 | }; |
693 | 723 |
|
694 | | - const collect = (arr) => |
695 | | - (arr || []).map(msgText).filter(Boolean).join(" | "); |
696 | | - const collectNested = (items) => |
697 | | - (items || []) |
698 | | - .flatMap((it) => collect(it.data ?? []).split(" | ")) |
| 724 | + const collect = (arr) => { |
| 725 | + if (!Array.isArray(arr)) return ""; |
| 726 | + return arr.map(msgText).filter(Boolean).join(" | "); |
| 727 | + }; |
| 728 | + const collectNested = (items) => { |
| 729 | + if (!Array.isArray(items)) return ""; |
| 730 | + return items |
| 731 | + .flatMap((it) => { |
| 732 | + const data = it.data ?? it; |
| 733 | + if (!Array.isArray(data)) return []; |
| 734 | + return collect(data).split(" | "); |
| 735 | + }) |
699 | 736 | .filter(Boolean) |
700 | 737 | .join(" | "); |
| 738 | + }; |
701 | 739 |
|
702 | 740 | const level = (e) => { |
703 | 741 | if (e.event_type.startsWith("Assistant")) return 0; |
|
712 | 750 | 0: "bg-sky-50", |
713 | 751 | 1: "bg-indigo-50", |
714 | 752 | 2: "bg-emerald-50", |
| 753 | + 2.5: "bg-teal-50", |
715 | 754 | 3: "bg-amber-50", |
716 | 755 | }; |
717 | 756 |
|
|
884 | 923 | .filter((e) => e.event_type === "AssistantRespond") |
885 | 924 | .map((e) => { |
886 | 925 | const inp = e.input_data |
887 | | - ? e.input_data.map((msg) => ({ content: msg.content })) |
888 | | - : []; |
889 | | - const out = e.output_data |
890 | | - ? e.output_data.map((msg) => ({ content: msg.content })) |
| 926 | + ? e.input_data.data.map((msg) => ({ content: msg.content })) |
891 | 927 | : []; |
| 928 | + const out = Array.isArray(e.output_data) |
| 929 | + ? e.output_data.flatMap(ev => |
| 930 | + Array.isArray(ev.data) |
| 931 | + ? ev.data.map(msg => ({ content: msg.content })) |
| 932 | + : [] |
| 933 | + ) |
| 934 | + : []; |
892 | 935 | return { input_data: inp, output_data: out }; |
893 | 936 | }); |
894 | 937 | renderHistory(panes.History, hist); |
|
0 commit comments