Skip to content

feat: add visual builder + workspace mode to Burr UI#687

Open
andreahlert wants to merge 12 commits intoapache:mainfrom
andreahlert:feat/workspace-builder-lowcode
Open

feat: add visual builder + workspace mode to Burr UI#687
andreahlert wants to merge 12 commits intoapache:mainfrom
andreahlert:feat/workspace-builder-lowcode

Conversation

@andreahlert
Copy link
Copy Markdown
Collaborator

@andreahlert andreahlert commented Mar 23, 2026

Building on #667 (Graph Builder by @skrawcz), this adds a workspace mode and significantly enhanced builder that turns Burr's UI into a development environment.

What's new

Visual Builder (enhanced)

  • 9 node types: Action, Input, Result, LLM Call, API Call, Code, Streaming, Loop, Router
  • Each type has specialized property editors (LLM: provider/model, API: url/method, Code: inline editor, Router: branch conditions)
  • Inline + button on edges (Activepieces-style) with searchable type picker
  • Recursive layout algorithm (replaces dagre) supporting loop arcs and router fan-out/fan-in
  • Node type picker with search bar and categories (I/O, Logic, Integrations)

Monaco Editor (split view)

  • Builder canvas on the left, VS Code-style editor on the right
  • Multi-file code generation: actions.py, app.py, run.py, requirements.txt
  • File explorer sidebar with tabs (open/close files)
  • Bidirectional sync: edit code and the graph updates, edit graph and the code updates
  • Full Ctrl+C/V/Z support in both canvas and editor

Project save/load

  • File menu (New, Save, Open, Download .py)
  • Projects persisted server-side in ~/.burr/builder_projects/
  • Ctrl+S to save

Workspace integration

  • Backend workspace router with file browsing, code viewing, and script execution (SSE streaming)
  • Link workspace directories to projects
  • Path traversal protection with workspace validation

Other improvements

  • Undo/redo with 50-state history
  • Keyboard shortcuts (Delete to remove nodes, Ctrl+C/V to copy/paste nodes)
  • Code generation includes proper imports, sanitized identifiers, and with_tracker()

Screenshots

Builder with nodes and generated code
02-builder-with-nodes

Node editor with LLM Call configuration
03-builder-node-editor

Searchable node type picker
04-builder-picker

Technical details

Backend (Python)

  • burr/tracking/server/workspace.py: new APIRouter with endpoints for file tree, file content, script execution (SSE), process management, workspace links, and builder project persistence
  • Modified run.py to mount workspace router, schema.py to add supports_workspace

Frontend (~6,700 new lines)

  • utils/: builderTypes, codeGenerator (9 node types + multi-file), codeParser (bidirectional sync), flowLayout (recursive layout), treeUtils (immutable tree ops), stateFlow
  • hooks/useBuilderState.ts: tree-based state with undo/redo, keyboard shortcuts, save/load
  • builder/: BuilderView (split layout), BuilderGraph (ReactFlow with custom node/edge types), NodeEditor, NodeTypePicker (portal-based), 4 node components, 5 edge components with inline + buttons
  • workspace/: FileExplorer, CodeViewer, RunTerminal, WorkspaceSelector
  • project/: ProjectWorkspaceView, ActivityBar, SidePanel, TabBar, TrackingSidePanel

Dependencies added

  • @monaco-editor/react (MIT)
  • @tanstack/react-query (MIT)
  • @xyflow/react (MIT)
  • react-joyride (MIT)

All MIT license, ASF Category A compatible.

How I tested

  • npx tsc --noEmit zero errors
  • Backend endpoints tested with curl
  • Manual E2E: create flow, add nodes via toolbar and inline +, edit properties, see code update, save/load projects

Checklist

  • PR has an informative and human-readable title
  • Changes are limited to a single goal
  • Code passed the pre-commit check
  • New functions are documented
  • Placeholder code is flagged / future TODOs are captured in comments

@skrawcz
Copy link
Copy Markdown
Contributor

skrawcz commented Mar 28, 2026

@andreahlert I can't get this to run locally -- did you commit everything? otherwise see the CI failures.

@skrawcz
Copy link
Copy Markdown
Contributor

skrawcz commented Mar 28, 2026

Also need some example to load to help someone get started.

@andreahlert
Copy link
Copy Markdown
Collaborator Author

Thanks for reviewing. Didn't have the time to check it properly, I'll do it as soon as possible. Target: 03-30-2026

@andreahlert andreahlert self-assigned this Mar 28, 2026
@andreahlert
Copy link
Copy Markdown
Collaborator Author

Thanks for trying it out. Two things:

  1. Missing pieces: This PR is meant to be applied on top of feat: add visual Graph Builder tool to Burr UI #667 (your graph builder). On main alone, the builder route works but some of the other UI improvements (HomeView, SearchView, etc.) won't be there. The builder itself at /builder should load fine since all its deps are self-contained. I'll add a note about this in the PR description.

  2. Example graphs: Good call. I'll add a few pre-loaded examples (chatbot flow, data pipeline, agent with tools) that load from the File > Open menu so someone can see the builder in action without building from scratch.

Working on both now.

Copy link
Copy Markdown

@JiwaniZakir JiwaniZakir left a comment

Choose a reason for hiding this comment

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

In run.py, the shutdown sequence in lifespan calls await workspace.cleanup_processes() directly before await backend.lifespan(app).__anext__(), but there's no try/finally wrapping these calls. If cleanup_processes() raises an exception, the backend's own cleanup will be silently skipped, potentially leaking database connections or other resources. The backend shutdown should be guarded with a try/finally block regardless of what happens in workspace cleanup.

Additionally, supports_workspace=True is hardcoded unconditionally in _get_app_spec() — this means the UI will always advertise workspace support even if the feature is disabled or broken at runtime. This should be derived from a config flag or at least check that the workspace module initialized successfully, similar to how is_snapshotting_backend and is_annotations_backend are computed from the actual backend capabilities.

The _validate_workspace allowlist approach in workspace.py is a reasonable security boundary, but _read_links() is called on every file-browsing request with no caching. If the links file is read from disk on each API call under load, this could become a bottleneck — a short-lived in-process cache or memoization with invalidation on write would be worth considering.

@skrawcz
Copy link
Copy Markdown
Contributor

skrawcz commented Mar 29, 2026

Screenshot 2026-03-29 at 09 25 22 Screenshot 2026-03-29 at 09 25 26

validated things load locally at least.

@andreahlert andreahlert added kind/feature Net-new functionality area/ui Burr UI (telemetry frontend) labels Mar 30, 2026
Copy link
Copy Markdown
Contributor

@elijahbenizzy elijahbenizzy left a comment

Choose a reason for hiding this comment

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

Played around some -- looking great! Node types should probably have specific implementations but I think this is a good start to put out there.

andreahlert and others added 11 commits April 9, 2026 10:23
Adds a low-code visual builder with split-view Monaco editor,
workspace file browsing, and project persistence.

Builder features:
- 9 node types (Action, Input, Result, LLM Call, API Call, Code,
  Streaming, Loop, Router) with specialized property editors
- Inline + buttons on edges for inserting nodes (Activepieces-style)
- Recursive layout algorithm supporting loop arcs and router fan-out
- Monaco Editor with multi-file code generation (actions.py, app.py,
  run.py, requirements.txt)
- Bidirectional sync between graph and code
- Searchable node type picker with categories
- File menu with save/load projects, Ctrl+S support
- Undo/redo (50-state history), copy/paste, delete shortcuts
- Code generation with sanitized identifiers and proper imports

Workspace features:
- Backend router for file browsing, code viewing, script execution
  (SSE streaming), process management, and workspace-project linking
- Builder project persistence in ~/.burr/builder_projects/
- Path traversal protection with workspace validation

New dependencies (all MIT, ASF Category A):
- @monaco-editor/react
- @tanstack/react-query
- @xyflow/react
- react-joyride
- Remove unused imports/variables flagged by ESLint
- Replace regex literal with RegExp constructor to avoid no-control-regex
- Format workspace.py with black and isort
- Replace `as any` with proper types where possible
- Add eslint-disable comments for icon type maps (heroicons ForwardRef)
- Remove unused `id` from edge component destructures
- Remove unused `langMap` variable from BuilderView
This takes work from @jaeyow and apache#572.

Adds a drag-and-drop graph editor for designing Burr application graphs
visually and exporting as Python code or JSON.

Key changes:
- New /graph-builder route with full visual editor (ReactFlow v12)
- Migrate existing GraphView from reactflow v11 to @xyflow/react v12
- Remove reactflow and @tisoap/react-flow-smart-edge dependencies
- Per-node async/streaming toggles matching Burr's 4 action variants
- Python code generation with correct decorators and signatures
- 3 pre-built example graphs (MultiModal Chatbot, CRAG, Streaming)
- localStorage auto-save/restore of graph state
- Empty-state onboarding overlay and structured help sidebar
- Fix appcontainer layout for full-height content
Python (workspace.py):
- Module-level docstring describing all capabilities
- Docstrings on all Pydantic models, endpoints, and helper functions
- Documented security functions with Args/Returns/Raises

TypeScript:
- JSDoc on all exported types in builderTypes, codeGenerator, codeParser
- JSDoc on all tree operation functions in treeUtils
- JSDoc on layout algorithm functions in flowLayout
- JSDoc on useBuilderState hook
Remove the legacy react-query v3 package and standardize on
@tanstack/react-query v5 across all components. This fixes the
"No QueryClient set" error in BuilderView which was caused by
two separate QueryClient providers from different packages.
Wrap workspace.cleanup_processes() in try/finally so backend shutdown
always runs even if process cleanup fails. Derive supports_workspace
from workspace.is_available() instead of hardcoding True. Add in-process
cache to _read_links() to avoid disk reads on every file-browsing request.
@andreahlert andreahlert force-pushed the feat/workspace-builder-lowcode branch from 898f6be to 0fff68f Compare April 9, 2026 13:34
@andreahlert
Copy link
Copy Markdown
Collaborator Author

All review feedback addressed. Rebased clean on main, no conflicts.

@JiwaniZakir your three points: cleanup_processes() is wrapped in try/finally now so backend shutdown always runs, supports_workspace is derived from workspace.is_available() instead of hardcoded, and _read_links() has an in-process cache with invalidation on write.

@skrawcz docstrings on every function/class in workspace.py, including the inner ones. Examples were already in from the graph-builder merge.

@elijahbenizzy opened #735 to track the node type implementations as a follow-up. Agreed it's fine for the initial merge.

Ready for re-review whenever you get a chance.

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

Labels

area/ui Burr UI (telemetry frontend) kind/feature Net-new functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants