nFlow β€” Visual Deep Learning Model Creator

A node-based, native desktop app for designing deep-learning models visually. Wire nodes on a canvas β†’ see live tensor shapes β†’ run CPU inference β†’ export to PyTorch, Keras, or ONNX. Zero Python required.

Stack: Tauri v2 (Rust backend + WebView frontend) Β· React 19 Β· xyflow (React Flow) Β· Tailwind v4 Β· Zustand

Architecture spec & SRS: krystv/nflow-architecture-spec


What works right now

Feature Status
Node-graph canvas (pan, zoom, drag, multi-select) βœ…
90+ built-in operators (IO, Layer, Linalg, Activation, Norm, Pooling, Movement, Loss, Composite) βœ…
Live symbolic tensor shape inference (every wire shows shape + dtype) βœ…
Shape error diagnostics per node βœ…
Custom node creator (composite editor tab) βœ…
Custom node io.input / io.output β€” inline rename, dtype picker, rank badge βœ…
Composite validity bar (port count, error count, Rust diagnostics) βœ…
Snapshot-based undo / redo (all edit types) βœ…
Code export β€” PyTorch, Keras, ONNX text βœ…
ONNX binary protobuf export (passes onnx.checker, runs in onnxruntime) βœ…
CPU numeric forward pass (real tensor evaluation β€” Simulate button) βœ…
.nfl project file β€” save / open / recent files βœ…
Global composite library (reusable nodes across projects, persisted in redb) βœ…
Group-to-composite (select nodes β†’ right-click β†’ Create Block) βœ…
Double-click block node β†’ inline to primitives βœ…
Double-click user composite β†’ open editor tab βœ…
Rhai custom op scripting (define shape inference in-place, no recompile) βœ…
Multi-tab canvas (main project + one tab per open composite editor) βœ…
Training config (AdamW / SGD / Adam + loss β€” emits PyTorch training loop) βœ…

Rust crate status

Crate What it does Tests
nflow-ir SSA graph IR, SlotMap nodes/values, Dim algebra, UserComposite 6 βœ…
nflow-infer DimSolver (union-find), broadcast, cost model (params + FLOPs) 7 βœ…
nflow-ops 90+ operator registry β€” OpSpec + shape infer + Rhai custom ops 14 βœ…
nflow-events Session (event-sourced undo/redo + live inference + view) + .nfl format + redb 6 βœ…
nflow-codegen PyTorch / Keras / ONNX text + binary protobuf exporters 5 βœ…
nflow-exec Backend trait, CPU evaluator (90/90 ops covered), GPU stub (burn+wgpu, --features wgpu) 4 βœ…

42 tests, 0 failures, 0 warnings (cargo test at workspace root, no GPU needed).


Quick start

# Prerequisites: Rust stable, Node 20+, Tauri v2 system deps
# See: https://tauri.app/start/prerequisites/

npm install

# Desktop app (Rust backend + hot-reload frontend):
npm run tauri dev

# Frontend-only preview β€” uses in-browser mock engine, no Rust needed:
npm run dev

# Production bundle:
npm run tauri build

# Rust unit tests only (no webkit/GPU needed):
cargo test

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  React/xyflow frontend  (src/)                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚LeftPaletteβ”‚  β”‚   Editor   β”‚  β”‚  RightInspector      β”‚    β”‚
β”‚  β”‚Ops/Custom β”‚  β”‚ (canvas)   β”‚  β”‚  (attrs + ports)     β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚       β”‚               β”‚                    β”‚                β”‚
β”‚       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚
β”‚                       β”‚ invoke(Intent)                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚  Tauri IPC
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Rust backend  (src-tauri/src/lib.rs)                       β”‚
β”‚                       β”‚                                     β”‚
β”‚          Session::apply(intent)                             β”‚
β”‚               ↓              ↓                              β”‚
β”‚        Graph mutation   run_inference()                     β”‚
β”‚                              ↓                              β”‚
β”‚                    GraphView (nodes+edges+shapes)           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Rust core is the single source of truth. The frontend is a read-only projection β€” it sends Intent messages (add_node, connect, disconnect, move, delete, set_attr) and receives back a GraphView. The frontend never owns graph state; all mutation logic, shape inference, validation, and undo/redo live in Rust.


Frontend file map

src/
β”œβ”€β”€ main.tsx                  ← bootstrap, global contextmenu suppression
β”œβ”€β”€ App.tsx                   ← layout shell, multi-tab canvas mount
β”œβ”€β”€ ErrorBoundary.tsx         ← catches render crashes, shows readable error
β”œβ”€β”€ index.css                 ← Tailwind v4, react-flow overrides, scrollbar, font
β”œβ”€β”€ lib/utils.ts              ← cn(), fmtShape, dtypeClass, IS_MAC, fmtShortcut
β”‚
β”œβ”€β”€ bridge/
β”‚   β”œβ”€β”€ types.ts              ← all TS interfaces mirroring Rust types
β”‚   β”œβ”€β”€ invoke.ts             ← api.* β€” the ONLY place that calls invoke()
β”‚   └── mock.ts               ← in-browser mock engine for npm run dev
β”‚
β”œβ”€β”€ store/
β”‚   β”œβ”€β”€ graph.ts              ← main session state (view, catalog, file, undo/redo)
β”‚   β”œβ”€β”€ tabs.ts               ← tab system (main + composite editor tabs)
β”‚   └── composites.ts         ← project + global composite library
β”‚
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ layout/
β”‚   β”‚   β”œβ”€β”€ TopBar.tsx        ← file menu, undo/redo, export, simulate, settings
β”‚   β”‚   β”œβ”€β”€ TabBar.tsx        ← tab strip (main + editor tabs)
β”‚   β”‚   β”œβ”€β”€ LeftPalette.tsx   ← 28px strip + Ops panel + Custom panel + create modal
β”‚   β”‚   β”œβ”€β”€ RightInspector.tsx← node inspector (attrs, ports, weights, cost)
β”‚   β”‚   └── BottomMetrics.tsx ← collapsible metrics bar (params, FLOPs, event log)
β”‚   └── editor/
β”‚       β”œβ”€β”€ Editor.tsx        ← ReactFlow canvas, all callbacks useCallback-wrapped
β”‚       β”œβ”€β”€ ContextMenu.tsx   ← right-click menus (node / edge / canvas)
β”‚       └── nodes/
β”‚           β”œβ”€β”€ PrimitiveNode.tsx   ← leaf op node (read-only display)
β”‚           β”œβ”€β”€ BlockNode.tsx       ← composite/block node (double-click to expand)
β”‚           └── IoNode.tsx          ← io.input / io.output (editable label + dtype)
β”‚
└── panels/
    β”œβ”€β”€ CompositeEditorToolbar.tsx  ← toolbar for editor tabs (save/discard/undo/promote)
    β”œβ”€β”€ CompositeValidityBar.tsx    ← validity strip above each editor canvas
    β”œβ”€β”€ ExportModal.tsx             ← code export (PyTorch / Keras / ONNX)
    β”œβ”€β”€ RunPanel.tsx                ← forward pass result + coverage report
    β”œβ”€β”€ TrainPanel.tsx              ← training config (optimizer, loss, metrics)
    β”œβ”€β”€ SettingsPanel.tsx           ← app settings (shortcuts, canvas, general)
    β”œβ”€β”€ NodeLibraryPanel.tsx        ← full node library panel
    └── Toast.tsx                   ← error/info toasts

Custom node creation flow

LeftPalette β†’ Custom tab β†’ "New" button
    β”‚
    β”œβ”€ QuickCreate (inline)
    β”‚     Type name β†’ Enter β†’ createComposite() β†’ loadProjectComposites() β†’ openEditorTab()
    β”‚
    └─ Advanced… link β†’ AdvancedCreateModal
          Name, uid, category, colour, port counts, doc, paper URL
          β†’ createComposite() β†’ loadProjectComposites() β†’ openEditorTab()

openEditorTab(uc):
    1. api.openCompositeEditor(uid)  β†’  Rust creates sub-Session with inner_graph
    2. tabs store: push new tab, set editorViews[uid] = GraphView
    3. App.tsx: mounts <Editor editorUid={uid} /> (display:flex, others display:none)
    4. CompositeValidityBar renders above canvas (port count, valid/invalid, errors)
    5. io.input / io.output nodes render as IoNode (click label to rename, dtype picker)

Critical bugs fixed (session history)

Bug Symptom Fix
onSelectionChange inline arrow Maximum update depth (infinite loop) Wrapped in useCallback with [] deps
nodeTypes inside component All nodes remount every render Moved to module-level const
onClick={void go} Create button does nothing Changed to onClick={() => void go()}
App.tsx single <Editor> Editor tab canvas never appeared All tab canvases mounted simultaneously, display:none for inactive
mockCreateUserComposite always returned Mamba New composite had wrong uid Mock now inserts real entry into store
open_composite_editor ignored uid Editor always showed Mamba graph mockEditorViewForUid(uid) generates correct io nodes
Graph unused import in lib.rs Rust compiler warning Removed; use Definition directly
import { useEffect } after usage Module ordering issue All imports hoisted to top of file
CreateCompositeModal imported but never rendered in App.tsx Advanced modal inaccessible Inlined as AdvancedCreateModal in LeftPalette.tsx

Design system

  • Canvas bg: #0a0c10 Β· Panel bg: #14161b Β· Strip bg: #0e1016 Β· Border: #2d313a
  • Fonts: Inter (body) Β· JetBrains Mono (code/values) β€” defined in @theme CSS vars
  • 28px strip pattern: LeftPalette and RightInspector use 28px permanent strips with rotated text
  • Port hover: box-shadow glow only β€” never transform: scale() (causes positional jump)
  • Wire z-index: .react-flow__connection-line { z-index: 1001 } β€” above port handles

Repo layout

crates/             Rust workspace (pure, no GUI deps β€” cargo test works anywhere)
  nflow-ir/         SSA graph IR + UserComposite + Dim algebra
  nflow-infer/      DimSolver + shape broadcast + cost model
  nflow-ops/        Operator registry (90+ ops) + Rhai custom op scripting
  nflow-events/     Session (undo/redo + inference) + .nfl format + redb DB
  nflow-codegen/    PyTorch + Keras + ONNX exporters
  nflow-exec/       CPU evaluator + GPU Backend trait (wgpu feature)
src-tauri/          Tauri v2 IPC command layer (~30 commands)
src/                React 19 + xyflow + Tailwind v4 + Zustand frontend
docs/               Architecture docs, SRS, operator reference

License

Apache-2.0

Generated by ML Intern

This repository was built and maintained by ML Intern.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support