Flowfile 0.23: Groups in Code and Room for Your Data
Flowfile 0.23 adds Data and Logs windows, visual groups in Python, and code-based Simple build, alongside notebook improvements and everyday editing fixes.
A flow, its Python code, and the resulting table are three things I want to see at the same time. Add the run log and the screen starts negotiating with you.
Flowfile 0.23 gives Data and Logs their own windows, brings visual groups into Python, and lets Simple build describe a flow using the same code the notebook shows. This post covers the October 10 release and the notebook improvements since the 0.22 canvas notebook post.
Put the data beside the canvas
The notebook could already move into its own window. Data and Logs can now do the same: use the pop-out button in the dock header. On desktop that opens a native window; in the web app it opens a browser window.
The Data window follows the canvas selection. Click a node and its rows appear beside the flow. Deselect it and the table stays, which is useful when you want to look at the numbers while moving things around. A separate Logs window keeps the run output visible while you inspect a node’s settings.
Each flow gets one window of each kind. Return to designer puts a panel back in the dock, and a panel you moved out stays out when you select another node or start a run. Desktop windows stay attached to the flow after Save As.
Behind this is a change feed for each open flow. Multiple tabs or windows showing that flow can pick up edits without a manual reload. A notebook push also checks whether the canvas changed since its cells were rendered. If it did, the notebook refreshes, preserves your edits, and asks you to run or push again. That check matters when the two views are on different screens.
Visual groups now have a Python form
A labelled box around the cleaning steps is part of how I read a flow. It should survive moving between the canvas and code.
ff.FlowGroup creates that box, and .add_to_group(...) puts a node inside it. In a new flow, open Code → Notebook, select No kernel, paste this example and choose Run all:
import flowfile as ff
cleaning = ff.FlowGroup("Cleaning", color="blue")
reporting = ff.FlowGroup("Reporting", parent_group=cleaning)
orders = ff.DataFrame({
"order_id": [1, 2, 3],
"amount": [120.0, 40.0, 900.0],
}).add_to_group(cleaning)
kept = orders.filter(ff.col("amount") > 50).add_to_group(cleaning)
ranked = kept.sort("amount", descending=True).add_to_group(reporting)
ff.explore(ranked, description="Orders to inspect", group=reporting)
There are two nested groups here. The input and filter belong to Cleaning; the sort and Explore data node belong to Reporting. Groups organize the canvas and leave the calculation unchanged. They have nothing to do with group_by.
One detail worth knowing: .add_to_group(...) assigns the node it is called on. To put every step of a chain in a group, add it to each step.
The FlowFrame export and canvas notebook include these declarations and assignments. Change a group’s name, colour or parent in the notebook and push it; the box updates on the canvas. Nodes whose cells you did not change keep their existing membership. Removing color= restores the default tint, and a group left empty is removed.
ff.explore(...) is new too. It places an Explore data node, where you build charts in the designer. The call returns None, so continue a pipeline from ranked, not from the Explore call. Pushing its notebook cell preserves charts already saved on the node; the chart configuration itself stays in the designer.
The visual groups reference covers nesting and how groups follow their nodes when two graphs are joined.
The notebook keeps more of the code you wrote
Polars Code nodes now show their function directly in the notebook, with its name and parameter names preserved. A custom transformation can look like this:
import polars as pl
def add_vat(orders: pl.LazyFrame) -> pl.LazyFrame:
return orders.with_columns(
(pl.col("amount") * 1.21).alias("amount_with_vat")
)
with_vat = kept.polars_code(add_vat)
The function is the code stored on the node. Keep any helpers inside it: the node cannot carry arbitrary variables from the surrounding notebook along with it. Snippet-style Polars nodes still work and appear inside a generated function whose body you can edit.
The editor also has improved type hints and signature help. From a flow’s catalog page, Modify in notebook opens the designer with the notebook already showing.
For cells that need ordinary Python execution, Create notebook kernel now creates, starts and selects a matching kernel in one step. If its image is missing, the button offers to download it first. Docker is required for that kernel. After upgrading Flowfile, Update notebook kernel switches to the image for the new app version and clears the running session’s memory.
The distinction between execution modes still matters. With No kernel, Flowfile interprets supported flow declarations; Run syncs edits and runs the corresponding canvas node. With a notebook kernel selected, Run executes Python in the kernel and Push applies the flow it builds to the canvas. Notebook kernels are available for local desktop and default pip installations; Docker deployments retain the notebook without a kernel, with syncing restricted to admins.
The canvas notebook documentation describes those modes, including where unsynced edits live. For a script or project to run elsewhere, there is also the Python project export.
Simple build writes the same FlowFrame code
The AI assistant’s Simple build mode now asks the model for FlowFrame code. Expand Generated code in the reply to read it, then choose Add to canvas to insert the steps as one undo operation. When a flow already exists, the generated steps can connect to it.
Flowfile parses that description into nodes. Simple build has a deliberately small set of allowed operations: it rejects writer nodes, database, cloud, catalog and Kafka sources, code nodes, collect(), function definitions and loops. A rejected response gets one rewrite attempt. You attach the destination yourself after the flow is on the canvas.
That makes the generated code useful to inspect. If a request for paid orders becomes a filter on the wrong column, the expression is right there before you add it.
The previous format remains available under Settings → AI → Assistant → Simple build → node settings. Provider defaults also changed in this release. One upgrade detail to check if you use OpenRouter: the staged agent now defaults to a paid Qwen model in place of the discontinued free Llama tier. The release notes list the model changes.
Fixes that show up in everyday flows
Several smaller changes remove interruptions from that same editing loop:
- Blank rename fields preserve the original name. Join and Select no longer turn an empty rename into a duplicate-column failure. Real name collisions produce an error identifying the columns.
- Local joins accept eager right-hand inputs. This fixes failures involving readers such as Excel, Avro and IPC streams.
- CSV lists read every file.
ff.read_csvnow handles a list of paths correctly. Passing a cloud-storage path to a local reader points you towards the appropriate cloud reader. - Catalog writers match their notebook cells. A writer configured in the designer no longer appears edited just because the notebook renders it differently.
- Separate log windows follow the full run. Quiet periods no longer end the stream before the run finishes. Windows desktop also gets fixes for blank notebook windows and backend processes left behind after the app exits unexpectedly.
There is also an access fix for shared installations: open flows are now scoped to the current user’s session. Two users opening the same file get separate in-memory flows, IDs and logs. Requests for another user’s open flow return 404.
The CSV-combining walkthrough is a small flow to try with the table beside the canvas. Install or upgrade with pip install --upgrade flowfile, or get the desktop release.