From 3f91b0adbd09905e33cbf38d6bbfbf0b45f8d821 Mon Sep 17 00:00:00 2001 From: zachary62 Date: Sun, 16 Feb 2025 13:40:05 -0500 Subject: [PATCH] update flow doc --- docs/flow.md | 52 +++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/docs/flow.md b/docs/flow.md index d8f9959..8112303 100644 --- a/docs/flow.md +++ b/docs/flow.md @@ -7,30 +7,30 @@ nav_order: 2 # Flow -A **Flow** orchestrates how Nodes connect and run, based on **Actions** returned from each Node’s `post()` method. You can chain Nodes in a sequence or create branching logic depending on the **Action** string. +A **Flow** orchestrates a graph of Nodes. You can chain Nodes in a sequence or create branching depending on the **Actions** returned from each Node's `post()`. ## 1. Action-based Transitions -Each Node's `post(shared, prep_res, exec_res)` method returns an **Action** string. By default, if `post()` doesn't explicitly return anything, we treat that as `"default"`. +Each Node's `post()` returns an **Action** string. By default, if `post()` doesn't return anything, we treat that as `"default"`. You define transitions with the syntax: -1. Basic default transition: `node_a >> node_b` - This means if `node_a.post()` returns `"default"` (or `None`), go to `node_b`. +1. **Basic default transition**: `node_a >> node_b` + This means if `node_a.post()` returns `"default"`, go to `node_b`. (Equivalent to `node_a - "default" >> node_b`) -2. Named action transition: `node_a - "action_name" >> node_b` +2. **Named action transition**: `node_a - "action_name" >> node_b` This means if `node_a.post()` returns `"action_name"`, go to `node_b`. -It’s possible to create loops, branching, or multi-step flows. +It's possible to create loops, branching, or multi-step flows. ## 2. Creating a Flow -A **Flow** begins with a **start** node (or flow). You call `Flow(start=some_node)` to specify the entry point. When you call `flow.run(shared)`, it executes the first node, looks at its `post()` return Action, follows the corresponding transition, and continues until there’s no next node or you explicitly stop. +A **Flow** begins with a **start** node. You call `Flow(start=some_node)` to specify the entry point. When you call `flow.run(shared)`, it executes the start node, looks at its returned Action from `post()`, follows the transition, and continues until there's no next node. ### Example: Simple Sequence -Here’s a minimal flow of two nodes in a chain: +Here's a minimal flow of two nodes in a chain: ```python node_a >> node_b @@ -41,7 +41,7 @@ flow.run(shared) - When you run the flow, it executes `node_a`. - Suppose `node_a.post()` returns `"default"`. - The flow then sees `"default"` Action is linked to `node_b` and runs `node_b`. -- If `node_b.post()` returns `"default"` but we didn’t define `node_b >> something_else`, the flow ends there. +- `node_b.post()` returns `"default"` but we didn't define `node_b >> something_else`. So the flow ends there. ### Example: Branching & Looping @@ -57,19 +57,19 @@ We can wire them like this: # Define the flow connections review - "approved" >> payment # If approved, process payment review - "needs_revision" >> revise # If needs changes, go to revision -review - "rejected" >> finish # If rejected, finish the process +review - "rejected" >> finish # If rejected, finish the process revise >> review # After revision, go back for another review -payment >> finish # After payment, finish the process +payment >> finish # After payment, finish the process flow = Flow(start=review) ``` Let's see how it flows: -1. If `review.post()` returns `"approved"`, the expense moves to `payment` node -2. If `review.post()` returns `"needs_revision"`, it goes to `revise` node, which then loops back to `review` -3. If `review.post()` returns `"rejected"`, it moves to `finish` node and stops +1. If `review.post()` returns `"approved"`, the expense moves to the `payment` node +2. If `review.post()` returns `"needs_revision"`, it goes to the `revise` node, which then loops back to `review` +3. If `review.post()` returns `"rejected"`, it moves to the `finish` node and stops ```mermaid flowchart TD @@ -83,12 +83,13 @@ flowchart TD ### Running Individual Nodes vs. Running a Flow -- `node.run(shared)`: Just runs that node alone (calls `prep()`, `exec()`, `post()`), returns an Action. -- `flow.run(shared)`: Executes from the start node, follows Actions to the next node, and so on until the flow can’t continue (no next node or no next Action). +- `node.run(shared)`: Just runs that node alone (calls `prep->exec->post()`), returns an Action. +- `flow.run(shared)`: Executes from the start node, follows Actions to the next node, and so on until the flow can't continue. -> node.run(shared) **does not** proceed automatically to the successor and may use incorrect parameters. +> `node.run(shared)` **does not** proceed to the successor. > This is mainly for debugging or testing a single node. +> > Always use `flow.run(...)` in production to ensure the full pipeline runs correctly. {: .warning } @@ -100,13 +101,6 @@ A **Flow** can act like a Node, which enables powerful composition patterns. Thi 2. Combine multiple smaller Flows into a larger Flow for reuse. 3. Node `params` will be a merging of **all** parents' `params`. -> While **Flow** is also a **Node**, it won't run `exec()`. -> -> It will run `prep()` and `post()`, before and after calling the nodes within the flow. -> -> However, `post()` always receives None for exec_res, and should instead get the flow execution results from the shared store. -{: .warning } - ### Basic Flow Nesting Here's how to connect a flow to another node: @@ -125,7 +119,7 @@ parent_flow = Flow(start=subflow) When `parent_flow.run()` executes: 1. It starts `subflow` -2. `subflow` runs through its nodes (`node_a` then `node_b`) +2. `subflow` runs through its nodes (`node_a->node_b`) 3. After `subflow` completes, execution continues to `node_c` ### Example: Order Processing Pipeline @@ -176,3 +170,11 @@ flowchart LR inventoryFlow --> shippingFlow end ``` + +### Flow's Node Methods + +A **Flow** is also a **Node**, so it will run `prep()` and `post()`. However: + +- It **won't** run `exec()`, as its main logic is to orchestrate other nodes. +- `post()` always receives None for exec_res and should instead get the flow execution results from the shared store. +