Skip to content

feat: add visual workflow builder MVP for automation pipelines (#447)#456

Open
BekkamMallishwari wants to merge 1 commit into
sreerevanth:mainfrom
BekkamMallishwari:feature/visual-workflow-builder-447
Open

feat: add visual workflow builder MVP for automation pipelines (#447)#456
BekkamMallishwari wants to merge 1 commit into
sreerevanth:mainfrom
BekkamMallishwari:feature/visual-workflow-builder-447

Conversation

@BekkamMallishwari

@BekkamMallishwari BekkamMallishwari commented Jun 22, 2026

Copy link
Copy Markdown

Summary

Minimal Viable Product (MVP) of the Visual Workflow Builder for Agent Automation Pipelines. Users can visualize, add, move, and connect nodes (Agent, LLM, Memory, HTTP, etc.) on a responsive interactive canvas, and configure node-specific properties. A simulated execution pipeline can be run, showing real-time execution steps and logs.

Changes

  • Frontend: A minimal and interactive workflow builder UI at /workflow-builder route, implementing basic node system, drag-and-drop mechanics, interactive connection rendering (SVG-based bezier curves), and viewport navigation (zoom/pan).
  • Backend: Minimal in-memory/JSON-file workflows APIs supporting GET /api/workflows, POST /api/workflows, GET /api/workflows/{id}, and DELETE /api/workflows/{id}, alongside duplicate/alias routes for compatibility.
  • Navigation: Unified header-level layout linking from / (index page) directly to the visual builder.

Testing

  • Unit tests implemented in tests/test_workflows.py verifying all REST API endpoints.
  • npm run type-check passed.
  • npm run lint passed.
  • npm run build passed.

Limitations

  • No advanced canvas engine (simple SVG-based drawing only)
  • No production execution system (simulation runner only)
  • No database integration (uses in-memory JSON file storage)
  • MVP only implementation

Issue

Closes #447

Summary by CodeRabbit

  • New Features
    • Introduced a visual Workflow Builder with an interactive canvas for designing and managing workflows
    • Added drag-and-drop node creation from a palette, node-to-node connection support, and per-node configuration forms
    • Created ability to save, load, and delete workflows with persistent storage
    • Integrated workflow simulation runner with step-through inspection and execution logs
    • Added validation warnings for workflow structure issues

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Visual Workflow Builder feature: Pydantic models (WorkflowNode, WorkflowEdge, Workflow) with disk-backed JSON persistence, CRUD and simulation REST endpoints on the FastAPI server, a full 1264-line React canvas page with drag-drop node palette, SVG edge routing, node config forms, and step simulation, plus integration tests and dashboard navigation wiring.

Changes

Visual Workflow Builder

Layer / File(s) Summary
Workflow data models and disk persistence
agentwatch/api/server.py
Adds WorkflowNode, WorkflowEdge, Workflow Pydantic models and DEFAULT_TEMPLATES, then implements load_workflows_from_disk() with default-merging fallback and save_workflows_to_disk() with error logging.
Workflow CRUD REST endpoints
agentwatch/api/server.py
List, get, save, and delete handlers under /api/workflows and /api/v1/workflows, all gated by API key and rate limiting; save manages created_at/updated_at; delete raises 404 on missing id.
Workflow simulation endpoint
agentwatch/api/server.py
POST /api/workflows/{id}/run: builds a topological execution queue, generates per-node simulated logs/outputs/errors/duration by node.type, propagates failure including a hard-coded trigger for "node-failed-test", and returns overall status plus step traces.
Workflow lifecycle integration tests
tests/test_workflows.py
Pytest client fixture and an end-to-end test verifying list, create, get, confirm-in-list, delete, and 404-after-delete on the CRUD endpoints.
Frontend types, state, and workflow lifecycle handlers
frontend/pages/workflow-builder.tsx
TypeScript interfaces for graph/simulation payloads, NODE_TYPES palette config, component state, SWR workflow loading, first-load selection, and create/load/save/delete handlers calling the backend API.
Canvas viewport controls and interaction handlers
frontend/pages/workflow-builder.tsx
Zoom, pan, node drag with click-offset, palette drag-drop with coordinate conversion, edge connection with duplicate prevention, node deletion, node-data updater, and SVG edge-endpoint coordinate math.
Simulation control logic and node status helpers
frontend/pages/workflow-builder.tsx
Simulation trigger POST, step-forward/backward state updates, simulation reset, and per-node simulation state computation for visual indicators.
Canvas, SVG edges, nodes, and minimap rendering
frontend/pages/workflow-builder.tsx
Main canvas render: header, templates panel, left palette, SVG bezier edge paths with dragging edge, absolute-positioned HTML nodes with handles and simulation indicators, and a minimap overlay.
Node config sidebar and simulation/logs bottom panel
frontend/pages/workflow-builder.tsx
Right sidebar with per-node-type forms (agent, LLM, memory, HTTP, file, scheduler, conditional, custom-tool); bottom panel with step inspector, color-coded log stream, graph validation badge, and warnings list.
Dashboard navigation link and ESLint config
frontend/pages/index.tsx, frontend/.eslintrc.json
Adds a "Workflow Builder" link in the dashboard header and creates frontend/.eslintrc.json extending next/core-web-vitals with three rules disabled.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant WorkflowBuilderPage
  participant BackendAPI
  participant DiskPersistence

  User->>WorkflowBuilderPage: Open /workflow-builder
  WorkflowBuilderPage->>BackendAPI: GET /api/workflows (SWR)
  BackendAPI->>DiskPersistence: load_workflows_from_disk()
  DiskPersistence-->>BackendAPI: [workflows + defaults]
  BackendAPI-->>WorkflowBuilderPage: workflow list

  User->>WorkflowBuilderPage: Drag nodes, connect edges, configure node data
  User->>WorkflowBuilderPage: Click "Save"
  WorkflowBuilderPage->>BackendAPI: POST /api/workflows (workflow payload)
  BackendAPI->>DiskPersistence: save_workflows_to_disk()
  BackendAPI-->>WorkflowBuilderPage: {status: "saved", id}

  User->>WorkflowBuilderPage: Click "Run Simulation"
  WorkflowBuilderPage->>BackendAPI: POST /api/workflows/{id}/run
  BackendAPI->>DiskPersistence: load_workflows_from_disk()
  BackendAPI->>BackendAPI: Build topological queue, simulate steps
  BackendAPI-->>WorkflowBuilderPage: {status, steps, totals}
  WorkflowBuilderPage->>User: Animate step-by-step logs and node status indicators
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Hoppity-hop, I built a graph today,
Nodes and edges lined up in a row,
Drag a box here, connect it there,
Watch the simulation put on a show!
The rabbit saves workflows to disk with care,
And runs them step by step through the air. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add visual workflow builder MVP for automation pipelines' directly and clearly summarizes the main change in the pull request.
Linked Issues check ✅ Passed The PR implements the core features from issue #447: drag-and-drop canvas, node-based architecture, visual connections, multiple node types, workflow persistence, validation, and simulation with execution logs.
Out of Scope Changes check ✅ Passed All changes align with issue #447 objectives. Minor ESLint configuration updates and header navigation changes are in-scope supporting infrastructure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@BekkamMallishwari

Copy link
Copy Markdown
Author

Hi @sreerevanth
Just wanted to kindly check if you had a chance to review this PR yet.

This implements the MVP for the Visual Workflow Builder for Issue #447. All checks are passing and it’s ready for review whenever convenient.

Thanks!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (5)
frontend/pages/index.tsx (1)

334-337: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use Next.js <Link> component instead of plain <a> for client-side navigation.

The link to /workflow-builder uses a standard HTML <a> tag, which causes a full page reload in a Next.js SPA. The <Link> component provides efficient client-side navigation without reloads.

import Link from 'next/link'

Then update the link:

-            <a href="/workflow-builder" className="inline-flex items-center gap-2 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-2 text-sm font-medium text-blue-300 transition hover:bg-blue-500/20 hover:text-blue-200">
+            <Link href="/workflow-builder" className="inline-flex items-center gap-2 rounded-xl border border-blue-500/30 bg-blue-500/10 px-4 py-2 text-sm font-medium text-blue-300 transition hover:bg-blue-500/20 hover:text-blue-200">
               <Zap size={14} />
               Workflow Builder
-            </a>
+            </Link>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/index.tsx` around lines 334 - 337, The navigation link to
"/workflow-builder" uses a plain HTML `<a>` tag instead of Next.js `<Link>`
component, which causes unnecessary full page reloads. Replace the `<a>` tag
with the `<Link>` component imported from 'next/link' at the top of the file,
and move the className and other styling attributes to the appropriate elements
within the Link component structure. This ensures efficient client-side
navigation without page reloads in the Next.js SPA.
frontend/.eslintrc.json (1)

1-8: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Consider scoping ESLint rule disables to specific locations rather than disabling at the project level.

Disabling react-hooks/exhaustive-deps, @next/next/no-html-link-for-pages, and react/no-unescaped-entities at the .eslintrc.json root applies these exceptions to the entire frontend codebase. This masks potential issues beyond the workflow builder:

  • react-hooks/exhaustive-deps: Disabling this rule project-wide risks stale-closure bugs in any useEffect across the frontend. The pattern in workflow-builder.tsx (lines 135–140 in that file) may have a valid reason to omit selectedWorkflowId from deps, but this intent should be documented with an inline ESLint comment at that specific location, not hidden by a project-wide disable.

  • @next/next/no-html-link-for-pages: This combined with the <a> tag in index.tsx suggests a pattern-wide choice to avoid <Link>, which is suboptimal for SPA navigation.

  • react/no-unescaped-entities: Unclear why this needs to be disabled project-wide unless multiple files contain unescaped entities.

Recommended approach: Move targeted disables (e.g., // eslint-disable-next-line react-hooks/exhaustive-deps) to the specific lines in workflow-builder.tsx that require exceptions, with explanatory comments. Keep the root .eslintrc.json strict to catch issues elsewhere.

Please verify the scope and necessity of these disables by checking:

  1. Does this .eslintrc.json apply to all frontend pages or only the workflow-builder?
  2. Which patterns in the workflow-builder necessitate each disable, and are there equivalent patterns elsewhere in the frontend?
  3. Is the selectedWorkflowId omission from the useEffect dependency array in workflow-builder.tsx intentional, and does the loadWorkflow function safely handle the closure?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/.eslintrc.json` around lines 1 - 8, Remove the three rules
`@next/next/no-html-link-for-pages`, `react/no-unescaped-entities`, and
`react-hooks/exhaustive-deps` from the rules section in the root .eslintrc.json
file. Then, for each rule that is truly necessary to disable, locate the
specific files and lines where exceptions are needed (particularly in
workflow-builder.tsx around the useEffect block with the selectedWorkflowId
dependency array omission) and add targeted inline ESLint disable comments using
the format `// eslint-disable-next-line rule-name` directly above those lines,
followed by explanatory comments documenting why the exception is necessary at
that specific location. Only re-add rules to .eslintrc.json if they are
genuinely needed across multiple unrelated parts of the codebase; otherwise,
keep exceptions scoped to their specific locations.
tests/test_workflows.py (1)

15-63: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add coverage for /api/v1/workflows alias routes to protect compatibility guarantees.

This lifecycle test only exercises /api/workflows, but the backend exposes /api/v1/workflows aliases as part of the contract. A small parametrization over base path would prevent silent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_workflows.py` around lines 15 - 63, The test_workflows_lifecycle
function only exercises the /api/workflows endpoint path, but the backend also
provides /api/v1/workflows as an alias route that needs coverage. Add a pytest
parametrize decorator to the test_workflows_lifecycle function to accept a
base_path parameter with two values: "/api/workflows" and "/api/v1/workflows".
Then replace all hardcoded endpoint paths in the test (such as "/api/workflows",
"/api/workflows/wf-test-1234") with dynamically constructed paths using the
base_path parameter to ensure both alias routes are tested.
frontend/pages/workflow-builder.tsx (2)

834-840: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Minimap uses hardcoded canvas dimensions that may not match actual content.

The minimap positions nodes based on fixed 1400×800 dimensions. Nodes positioned beyond these coordinates will be clamped to the minimap edges, potentially showing inaccurate positions for large workflows.

Consider computing bounds dynamically from actual node positions, or documenting the assumed canvas size.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/workflow-builder.tsx` around lines 834 - 840, The minimap
style calculation uses hardcoded canvas dimensions (1400 for width and 800 for
height) when computing node positions, which causes nodes to be clamped
inaccurately for workflows larger than these dimensions. Replace the hardcoded
1400 and 800 values in the left and top style calculations with dynamically
computed bounds. Calculate these bounds by finding the maximum x and y
coordinates across all nodes in the workflow, or retrieve the actual canvas
dimensions from the component state or props if available. This ensures the
minimap accurately represents node positions regardless of workflow size.

180-180: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Replace deprecated substr() with substring() or slice().

String.prototype.substr() is deprecated. The pattern Math.random().toString(36).substr(2, 9) appears in multiple places for ID generation.

Proposed fix using substring
-const newId = `wf-${Math.random().toString(36).substr(2, 9)}`
+const newId = `wf-${Math.random().toString(36).substring(2, 11)}`

Apply similar changes at lines 197, 327, and 355.

Also applies to: 197-197, 327-327, 355-355

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/workflow-builder.tsx` at line 180, The code uses the
deprecated String.prototype.substr() method in the ID generation pattern.
Replace all occurrences of .substr(2, 9) with .substring(2, 11) or .slice(2, 11)
to use the non-deprecated alternatives. This needs to be applied in four
locations: the newId variable assignment and at the three other locations where
this same pattern appears for ID generation (referenced at lines 197, 327, and
355). Remember that substring and slice use start and end indices, not start and
length like substr does, so adjust accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@agentwatch/api/server.py`:
- Around line 1276-1297: The save_workflow function has a race condition where
concurrent requests can lose updates because the load-modify-save sequence is
not atomic. Multiple requests can read the workflows file simultaneously, each
modify it independently, and the last write will overwrite previous changes.
Wrap the entire sequence between load_workflows_from_disk() and
save_workflows_to_disk() with file locking using fcntl.flock to ensure only one
request can modify workflows at a time. Acquire the lock before loading and
release it after saving to make the operation atomic and prevent concurrent
modifications from overwriting each other.
- Around line 1336-1350: The incoming_counts dictionary is initialized based on
edge counts but is never decremented during the execution loop, causing nodes to
execute as soon as they're queued rather than waiting for all dependencies to
complete. After each node is executed (processed from the queue), iterate
through its outgoing edges using the outgoing_edges dictionary, decrement the
incoming_counts for each target node, and add the target to the queue only when
its incoming_counts reaches zero. This ensures all predecessors complete before
a node executes, implementing proper DAG traversal semantics.
- Around line 1253-1259: The save_workflows_to_disk function silently catches
exceptions and only logs a warning, but the calling save_workflow endpoint
proceeds to return a success status regardless of whether the write actually
succeeded. To fix this, modify save_workflows_to_disk to either re-raise the
caught exception after logging or return a boolean indicating success/failure.
Then update the save_workflow endpoint to check the result from
save_workflows_to_disk before returning the success response to the caller,
ensuring the user is only told their workflow was saved when it actually was
persisted.

In `@frontend/pages/workflow-builder.tsx`:
- Around line 226-243: In the handleDeleteWorkflow function, when the currently
selected workflow is deleted (when selectedWorkflowId === id), only the
selectedWorkflowId is being cleared to null. To prevent stale canvas data from
remaining visible, also clear the nodes and edges state at the same location by
calling the appropriate state setters (likely setNodes and setEdges) to reset
them to empty arrays alongside the setSelectedWorkflowId(null) call.
- Line 38: The fetcher function is returning null for non-ok HTTP responses,
which prevents SWR from detecting and handling errors properly. Instead of
returning null when r.ok is false, throw an error that includes the response
status and details so that SWR's error state and retry logic can be properly
triggered for 4xx and 5xx responses.
- Around line 640-642: The instructional text "Mouse wheel to zoom" in the div
element at line 640-642 advertises functionality that isn't implemented. To fix
this, implement a handleWheel event handler function that prevents default
behavior and adjusts the zoom state by calculating a delta based on the wheel
direction (negative deltaY increases zoom, positive decreases it), clamping the
result between 0.5 and 1.8 using the setZoom function. Then add the onWheel
event listener with this handler to the main canvas element to actually enable
the wheel zoom functionality.
- Around line 210-223: The fetch request to the workflows API endpoint is
missing the required X-Api-Key header. First, determine where the API key is
stored or accessed on the frontend (check context, local storage, or component
props). Then, in the fetch call that uses `${API_BASE}/workflows`, add the
X-Api-Key header to the headers object alongside the existing Content-Type
header, passing the API key value. Apply this same header addition to all other
workflow API calls mentioned for delete and list operations to ensure all
requests include proper authentication and avoid 401 errors.

In `@tests/test_workflows.py`:
- Around line 10-13: The client fixture is using the real app without isolating
disk state, causing tests to mutate shared files and creating test dependencies.
Modify the client fixture to override the WORKFLOWS_FILE environment variable to
use a temporary or isolated test-specific file path before creating the
TestClient. Additionally, ensure the fixture properly cleans up the temporary
file after the test completes using a yield statement or finally block to
guarantee cleanup even when tests fail. Apply the same isolation pattern to
other test functions referenced at lines 23-25 and 57-63 that also interact with
the workflows file.

---

Nitpick comments:
In `@frontend/.eslintrc.json`:
- Around line 1-8: Remove the three rules `@next/next/no-html-link-for-pages`,
`react/no-unescaped-entities`, and `react-hooks/exhaustive-deps` from the rules
section in the root .eslintrc.json file. Then, for each rule that is truly
necessary to disable, locate the specific files and lines where exceptions are
needed (particularly in workflow-builder.tsx around the useEffect block with the
selectedWorkflowId dependency array omission) and add targeted inline ESLint
disable comments using the format `// eslint-disable-next-line rule-name`
directly above those lines, followed by explanatory comments documenting why the
exception is necessary at that specific location. Only re-add rules to
.eslintrc.json if they are genuinely needed across multiple unrelated parts of
the codebase; otherwise, keep exceptions scoped to their specific locations.

In `@frontend/pages/index.tsx`:
- Around line 334-337: The navigation link to "/workflow-builder" uses a plain
HTML `<a>` tag instead of Next.js `<Link>` component, which causes unnecessary
full page reloads. Replace the `<a>` tag with the `<Link>` component imported
from 'next/link' at the top of the file, and move the className and other
styling attributes to the appropriate elements within the Link component
structure. This ensures efficient client-side navigation without page reloads in
the Next.js SPA.

In `@frontend/pages/workflow-builder.tsx`:
- Around line 834-840: The minimap style calculation uses hardcoded canvas
dimensions (1400 for width and 800 for height) when computing node positions,
which causes nodes to be clamped inaccurately for workflows larger than these
dimensions. Replace the hardcoded 1400 and 800 values in the left and top style
calculations with dynamically computed bounds. Calculate these bounds by finding
the maximum x and y coordinates across all nodes in the workflow, or retrieve
the actual canvas dimensions from the component state or props if available.
This ensures the minimap accurately represents node positions regardless of
workflow size.
- Line 180: The code uses the deprecated String.prototype.substr() method in the
ID generation pattern. Replace all occurrences of .substr(2, 9) with
.substring(2, 11) or .slice(2, 11) to use the non-deprecated alternatives. This
needs to be applied in four locations: the newId variable assignment and at the
three other locations where this same pattern appears for ID generation
(referenced at lines 197, 327, and 355). Remember that substring and slice use
start and end indices, not start and length like substr does, so adjust
accordingly.

In `@tests/test_workflows.py`:
- Around line 15-63: The test_workflows_lifecycle function only exercises the
/api/workflows endpoint path, but the backend also provides /api/v1/workflows as
an alias route that needs coverage. Add a pytest parametrize decorator to the
test_workflows_lifecycle function to accept a base_path parameter with two
values: "/api/workflows" and "/api/v1/workflows". Then replace all hardcoded
endpoint paths in the test (such as "/api/workflows",
"/api/workflows/wf-test-1234") with dynamically constructed paths using the
base_path parameter to ensure both alias routes are tested.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94767b74-1459-4b6f-befe-66838dcea61e

📥 Commits

Reviewing files that changed from the base of the PR and between 66eb9d7 and 55e413c.

📒 Files selected for processing (5)
  • agentwatch/api/server.py
  • frontend/.eslintrc.json
  • frontend/pages/index.tsx
  • frontend/pages/workflow-builder.tsx
  • tests/test_workflows.py

Comment thread agentwatch/api/server.py
Comment thread agentwatch/api/server.py
Comment thread agentwatch/api/server.py
Comment thread frontend/pages/workflow-builder.tsx
Comment thread frontend/pages/workflow-builder.tsx
Comment thread frontend/pages/workflow-builder.tsx
Comment thread frontend/pages/workflow-builder.tsx
Comment thread tests/test_workflows.py
@github-actions

Copy link
Copy Markdown
Contributor

🧪 PR Test Results

Check Result
Tests (pytest tests/) ✅ success
Lint (ruff check .) ❌ failure
Coverage (agentwatch) 73.15%

Python 3.12 · commit 55e413c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] ✨ Feature Request: Visual Workflow Builder for Agent Automation Pipelines

1 participant