A workflow engine for VS Code developers — build, run, and automate workflows with Python nodes.
Project description
Choola
A workflow automation engine where you build multi-step pipelines from self-contained Python nodes. Includes a visual editor (React + React Flow), a Flask API, and a CLI runner.
Prerequisites
- Python 3.12+
- Node.js 18+ and npm
Setup
# Clone the repo
git clone <repo-url>
cd choola
# Create a Python virtual environment and install dependencies
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Build the frontend
cd frontend
npm install
npm run build
cd ..
Running
Web UI + API
python server.py
Opens at http://localhost:5000. The visual editor lets you create workflows, add nodes, wire them together, and run them with live status streaming (SSE).
CLI
# List available workflows
python cli.py list
# Run a workflow with a JSON payload
python cli.py run <workflow_name> --payload '{"key": "value"}'
# Create a new empty workflow
python cli.py create <workflow_name>
# List nodes in a workflow
python cli.py nodes <workflow_name>
Project Structure
choola/
├── server.py # Flask API + serves the React frontend
├── cli.py # Headless CLI runner
├── database.py # SQLite store (globals + run logs)
├── requirements.txt
├── core/
│ └── base_node.py # Abstract base class for all nodes
├── frontend/ # React + React Flow visual editor (Vite)
└── workflows/
└── <workflow_name>/
├── topology.json # DAG definition (nodes + edges)
└── nodes/
├── __init__.py
└── <node>.py # One file per node
How Workflows Work
A workflow is a folder under workflows/ containing:
topology.json— defines the DAG: which nodes exist and how they connect.nodes/*.py— one Python file per node. Each node is a self-contained class that inherits fromBaseNode, receives a JSON payload, does one thing, and returns the (possibly modified) payload to the next node.
The engine topologically sorts the nodes and executes them in order, passing the payload through the chain.
Writing a Node
Every node is a single .py file in workflows/<name>/nodes/. Here's the template:
"""
@choola-node: MyNodeName
@category: processing
@description: Does one specific thing to the payload.
@input-payload:
- some_key (str): What this node expects
@output-payload:
- some_key (str): Same or transformed
- new_key (int): Something this node adds
@config-fields:
- threshold (int, default=10): Controls the threshold
@example-input: {"some_key": "hello"}
@example-output: {"some_key": "hello", "new_key": 42}
@side-effects: none
@errors: Raises ValueError if some_key is missing
"""
from typing import Any
from core.base_node import BaseNode
class MyNodeName(BaseNode):
name = "My Node Name"
category = "processing"
description = "Does one specific thing to the payload."
fields = [
{"name": "threshold", "type": "number", "default": 10},
]
async def execute(self, payload: dict[str, Any], context: dict[str, Any]) -> dict[str, Any]:
# Your logic here
return payload
Node rules
- Self-contained — all logic in one file, no cross-node imports.
- JSON in, JSON out — communicate only through the
payloaddict. - Docstring required — the
@choola-nodemarkers make nodes discoverable by grep.
Categories
| Category | Use for |
|---|---|
input |
Data ingestion, entry points |
processing |
Transformation, enrichment |
routing |
Conditional branching |
output |
Sending results, notifications |
validation |
Data checks, guards |
integration |
External API calls |
Global variables
Nodes can persist data across runs using SQLite-backed helpers:
value = await self.get_global("my_key")
await self.set_global("my_key", "new_value")
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/nodes |
List all registered node types |
| GET | /api/workflows |
List all workflows |
| POST | /api/workflows |
Create a new workflow |
| GET | /api/workflows/<name>/topology |
Get workflow topology |
| PUT | /api/workflows/<name>/topology |
Update workflow topology |
| POST | /api/workflows/<name>/run |
Execute a workflow |
| GET | /api/workflows/<name>/stream/<run_id> |
SSE stream for live run status |
Using with Claude Code
This project includes slash commands for building workflows with AI assistance:
/choola— describe what you want and Claude builds the full workflow (nodes + topology)./node— add a single node to an existing workflow.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file choola-0.1.0.tar.gz.
File metadata
- Download URL: choola-0.1.0.tar.gz
- Upload date:
- Size: 304.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49bcf952648f3d01e632861865a5e004df1f7e1b2e15b8d7a078fdd0abd0e42d
|
|
| MD5 |
47b62673059de5e8f20ae9b522485aab
|
|
| BLAKE2b-256 |
204e026287162bab84a423d04c75c984ba8434bc5bc57dda9d41e5cb8bfce46a
|
File details
Details for the file choola-0.1.0-py3-none-any.whl.
File metadata
- Download URL: choola-0.1.0-py3-none-any.whl
- Upload date:
- Size: 315.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b78a0a3103f51401788b33dc2c7444f531464ef57feb1cf5909ff905058469c
|
|
| MD5 |
9f5db378e96867d0fe1aaf84ac3bdfe1
|
|
| BLAKE2b-256 |
fa13e272b835c45ec88a2e3cf61639e7415794fc71b2a4ab418ce6af857eebd0
|