flowno


Nameflowno JSON
Version 0.1.5 PyPI version JSON
download
home_pageNone
SummaryA Python DSL for writing dataflow programs.
upload_time2025-10-21 23:15:19
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseMIT
keywords dataflow dsl python
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Flowno

Flowno is a Python DSL for building concurrent, cyclic, and streaming
[dataflow](https://en.wikipedia.org/wiki/Dataflow_programming) programs—ideal
for designing modular LLM agents. Inspired by no-code flowchart tools like
[ComfyUI](https://github.com/comfyanonymous/ComfyUI), Flowno lets you describe
workflows in code while handling complex dependency resolution (including cycles
and streaming) behind the scenes.

## Key Features

- **Node-Based Design**  
  Define processing steps as nodes with a simple decorator. Nodes can be
  stateless or stateful - consume, yield or transform streams - single or
  multiple outputs. 

- **Cycles & Streaming**  
  Unlike many workflow/flowchart/visual dataflow tools, Flowno supports cyclic
  graphs and streaming outputs. Use default values to bootstrap cycles.

- **Concurrent By Default**  
  The Flowno runtime schedules nodes to run as soon as their inputs are ready.
  Flowno provides a set of basic non-blocking concurrency primitives.

- **Type-Checked & Autocompleted**
  Flowno is designed to work well with type checkers and IDEs to catch
  incompatible connections between nodes.
  


## Quickstart

Set up a virtual environment and install Flowno:

```bash
python -m venv .venv  # Requires Python 3.10+
source .venv/bin/activate  # On Windows use: .venv\Scripts\activate
pip install flowno
```

Create a minimal flow (`hello.py`):

```python
from flowno import node, FlowHDL

@node
async def Add(x, y):
    return x + y

@node
async def Print(value):
    print(f"Value: {value}")

with FlowHDL() as f:
    f.sum = Add(1, 2)
    f.print = Print(f.sum)

f.run_until_complete()
```
```bash
(.venv) $ python hello.py
Value: 3
```

## How It Works

Flowno has three key components that work together to create dataflow programs:

### 1. Node Creation
The `@node` decorator transforms `async` functions (or classes) into a
constructor for a new subclass of `DraftNode`. The transformed function
takes takes connections to other nodes or constant values as arguments and
returns a `DraftNode` instance. 

```python
@node
async def Add(x, y):
    return x + y

draft_node_1 = Add(1, 2)  # Creates a node that will add 1 and 2
draft_node_2 = Add(draft_node_1, 3)  # Connects to the output of draft_node_1 to first input of draft_node_2
```

### 2. Flow Description
The `FlowHDL` context manager provides a declarative way to define node 
connections. Inside this context, nodes are assigned as attributes and can reference 
other nodes' outputs:

```python
with FlowHDL() as f:
    f.node_a = Add(1, 2)          # No references
    f.node_b = Add(f.node_a, 1)   # Backward reference

    f.node_c = Add(f.node_d, 1)   # Forward reference
    f.node_d = Add(f.node_c, 1)   # Backward reference creating a cycle
```

The context uses Python's `__getattr__` method to return placeholder objects when 
accessing undefined attributes like `f.node_d`. These forward references are a core 
feature of Flowno—they're not errors. However, each forward-referenced node must 
eventually be defined in the flow (as shown in the last line for `f.node_d`). During 
`__exit__`, the placeholders are resolved into proper node connections, and all 
`DraftNode` instances are finalized into full `Node` objects.

This mechanism allows you to define nodes in any order, which simplifies the 
construction of cyclic graphs.

### 3. Execution
When you call `f.run_until_complete()`, Flowno executes your flow:

1. Identifies nodes ready to run (those with all inputs available)
2. Executes ready nodes concurrently
3. Propagates outputs to dependent nodes
4. For cyclic flows, uses default values to bootstrap the cycle

The runtime is asynchronous - nodes only wait for their direct dependencies and
run concurrently with other nodes.  Execution continues until all nodes complete
or, for cyclic flows, until an uncaught exception occurs.

## Features TODO

- **Conditional Nodes**  
  Currently all nodes are executed unconditionally. The only way to skip a node is to add an immediate return statement to bypass the node's logic. I plan to add a way to conditionally execute nodes based on the output of other nodes.

## Non-Goals

- **Visual Editor**  
  Flowno is a DSL for defining dataflows in code. It does not include a visual
  editor for creating flows. I have a crude prototype of a visual editor, but I
  just don't see the value in dragging and dropping nodes. A visual editor can't
  be used by a LLM agent, for example.

- **Backpropagation**  
  Flowno is not a machine learning framework. I considered adding some sort of
  automatic differentiation, but I could never do it as well as PyTorch, and
  cycles would be difficult.


## About The Naming Conventions

> A Foolish Consistency is the Hobgoblin of Little Minds
> 
> -- <cite>Style Guide for Python Code (PEP-8)</cite>

My naming conventions deviate from standard Python style. I capitalize node names 
(like `Add` instead of `add`) because they are factory functions that behave like 
classes - when you call a decorated node function, it returns a `DraftNode` 
instance.

The `FlowHDL` context with its `f.node_name = Node()` syntax is an abuse of 
Python's `__getattr__` method. However, this approach enables:
- LSP type checking support
- Slightly better IDE autocomplete functionality
- Forward references for natural cycle definitions

## License

Flowno is released under the MIT License.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "flowno",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "dataflow, dsl, python",
    "author": null,
    "author_email": "Tyler Parker <tparker@computemachines.com>",
    "download_url": "https://files.pythonhosted.org/packages/c7/d9/e37cd53b05698b3ce7ac6d03390decc7a835f060411ed967da6a6d746348/flowno-0.1.5.tar.gz",
    "platform": null,
    "description": "# Flowno\n\nFlowno is a Python DSL for building concurrent, cyclic, and streaming\n[dataflow](https://en.wikipedia.org/wiki/Dataflow_programming) programs\u2014ideal\nfor designing modular LLM agents. Inspired by no-code flowchart tools like\n[ComfyUI](https://github.com/comfyanonymous/ComfyUI), Flowno lets you describe\nworkflows in code while handling complex dependency resolution (including cycles\nand streaming) behind the scenes.\n\n## Key Features\n\n- **Node-Based Design**  \n  Define processing steps as nodes with a simple decorator. Nodes can be\n  stateless or stateful - consume, yield or transform streams - single or\n  multiple outputs. \n\n- **Cycles & Streaming**  \n  Unlike many workflow/flowchart/visual dataflow tools, Flowno supports cyclic\n  graphs and streaming outputs. Use default values to bootstrap cycles.\n\n- **Concurrent By Default**  \n  The Flowno runtime schedules nodes to run as soon as their inputs are ready.\n  Flowno provides a set of basic non-blocking concurrency primitives.\n\n- **Type-Checked & Autocompleted**\n  Flowno is designed to work well with type checkers and IDEs to catch\n  incompatible connections between nodes.\n  \n\n\n## Quickstart\n\nSet up a virtual environment and install Flowno:\n\n```bash\npython -m venv .venv  # Requires Python 3.10+\nsource .venv/bin/activate  # On Windows use: .venv\\Scripts\\activate\npip install flowno\n```\n\nCreate a minimal flow (`hello.py`):\n\n```python\nfrom flowno import node, FlowHDL\n\n@node\nasync def Add(x, y):\n    return x + y\n\n@node\nasync def Print(value):\n    print(f\"Value: {value}\")\n\nwith FlowHDL() as f:\n    f.sum = Add(1, 2)\n    f.print = Print(f.sum)\n\nf.run_until_complete()\n```\n```bash\n(.venv) $ python hello.py\nValue: 3\n```\n\n## How It Works\n\nFlowno has three key components that work together to create dataflow programs:\n\n### 1. Node Creation\nThe `@node` decorator transforms `async` functions (or classes) into a\nconstructor for a new subclass of `DraftNode`. The transformed function\ntakes takes connections to other nodes or constant values as arguments and\nreturns a `DraftNode` instance. \n\n```python\n@node\nasync def Add(x, y):\n    return x + y\n\ndraft_node_1 = Add(1, 2)  # Creates a node that will add 1 and 2\ndraft_node_2 = Add(draft_node_1, 3)  # Connects to the output of draft_node_1 to first input of draft_node_2\n```\n\n### 2. Flow Description\nThe `FlowHDL` context manager provides a declarative way to define node \nconnections. Inside this context, nodes are assigned as attributes and can reference \nother nodes' outputs:\n\n```python\nwith FlowHDL() as f:\n    f.node_a = Add(1, 2)          # No references\n    f.node_b = Add(f.node_a, 1)   # Backward reference\n\n    f.node_c = Add(f.node_d, 1)   # Forward reference\n    f.node_d = Add(f.node_c, 1)   # Backward reference creating a cycle\n```\n\nThe context uses Python's `__getattr__` method to return placeholder objects when \naccessing undefined attributes like `f.node_d`. These forward references are a core \nfeature of Flowno\u2014they're not errors. However, each forward-referenced node must \neventually be defined in the flow (as shown in the last line for `f.node_d`). During \n`__exit__`, the placeholders are resolved into proper node connections, and all \n`DraftNode` instances are finalized into full `Node` objects.\n\nThis mechanism allows you to define nodes in any order, which simplifies the \nconstruction of cyclic graphs.\n\n### 3. Execution\nWhen you call `f.run_until_complete()`, Flowno executes your flow:\n\n1. Identifies nodes ready to run (those with all inputs available)\n2. Executes ready nodes concurrently\n3. Propagates outputs to dependent nodes\n4. For cyclic flows, uses default values to bootstrap the cycle\n\nThe runtime is asynchronous - nodes only wait for their direct dependencies and\nrun concurrently with other nodes.  Execution continues until all nodes complete\nor, for cyclic flows, until an uncaught exception occurs.\n\n## Features TODO\n\n- **Conditional Nodes**  \n  Currently all nodes are executed unconditionally. The only way to skip a node is to add an immediate return statement to bypass the node's logic. I plan to add a way to conditionally execute nodes based on the output of other nodes.\n\n## Non-Goals\n\n- **Visual Editor**  \n  Flowno is a DSL for defining dataflows in code. It does not include a visual\n  editor for creating flows. I have a crude prototype of a visual editor, but I\n  just don't see the value in dragging and dropping nodes. A visual editor can't\n  be used by a LLM agent, for example.\n\n- **Backpropagation**  \n  Flowno is not a machine learning framework. I considered adding some sort of\n  automatic differentiation, but I could never do it as well as PyTorch, and\n  cycles would be difficult.\n\n\n## About The Naming Conventions\n\n> A Foolish Consistency is the Hobgoblin of Little Minds\n> \n> -- <cite>Style Guide for Python Code (PEP-8)</cite>\n\nMy naming conventions deviate from standard Python style. I capitalize node names \n(like `Add` instead of `add`) because they are factory functions that behave like \nclasses - when you call a decorated node function, it returns a `DraftNode` \ninstance.\n\nThe `FlowHDL` context with its `f.node_name = Node()` syntax is an abuse of \nPython's `__getattr__` method. However, this approach enables:\n- LSP type checking support\n- Slightly better IDE autocomplete functionality\n- Forward references for natural cycle definitions\n\n## License\n\nFlowno is released under the MIT License.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A Python DSL for writing dataflow programs.",
    "version": "0.1.5",
    "project_urls": {
        "Documentation": "https://flowno.net/docs",
        "Homepage": "https://flowno.net",
        "Issues": "https://github.com/computemachines/flowno/issues",
        "Repository": "https://github.com/computemachines/flowno"
    },
    "split_keywords": [
        "dataflow",
        " dsl",
        " python"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "3b0b143963c195b6f4606e4b20b62938df04f9c197c5a208be233773ef5ecc56",
                "md5": "3dc2081a4c1a8f4a2f6cd00002d86b16",
                "sha256": "d70dd8b91e32cb029c78c537bd5e4c6f9c4633bb0057a405d405af8b882e7e77"
            },
            "downloads": -1,
            "filename": "flowno-0.1.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "3dc2081a4c1a8f4a2f6cd00002d86b16",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 95956,
            "upload_time": "2025-10-21T23:15:18",
            "upload_time_iso_8601": "2025-10-21T23:15:18.168933Z",
            "url": "https://files.pythonhosted.org/packages/3b/0b/143963c195b6f4606e4b20b62938df04f9c197c5a208be233773ef5ecc56/flowno-0.1.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c7d9e37cd53b05698b3ce7ac6d03390decc7a835f060411ed967da6a6d746348",
                "md5": "3882caca12f78541f1d3a8f0d9d932cc",
                "sha256": "539a7f9a7ea2aed3b6f151e7373f997648faa65b717b7f6a0b8ea2eeeab3265c"
            },
            "downloads": -1,
            "filename": "flowno-0.1.5.tar.gz",
            "has_sig": false,
            "md5_digest": "3882caca12f78541f1d3a8f0d9d932cc",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 102298,
            "upload_time": "2025-10-21T23:15:19",
            "upload_time_iso_8601": "2025-10-21T23:15:19.617790Z",
            "url": "https://files.pythonhosted.org/packages/c7/d9/e37cd53b05698b3ce7ac6d03390decc7a835f060411ed967da6a6d746348/flowno-0.1.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-21 23:15:19",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "computemachines",
    "github_project": "flowno",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "flowno"
}
        
Elapsed time: 2.89848s