You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A visualization for the graph would help the users to know what would happen. And it would help the designers to detect the weakness in their deigns, So I built this, a new method for the class Flow to visualize the whole graph. More explanation and details and below.
If you just want to use this feature. you should download the mermaid.py , put it into the sourcecode portfolio and add a new method in class Flow like below. See my repository.
classFlow(BaseNode):
def__init__(self,start=None): super().__init__(); self.start_node=startdefstart(self,start): self.start_node=start; returnstartdefget_next_node(self,curr,action):
nxt=curr.successors.get(actionor"default")
ifnotnxtandcurr.successors: warnings.warn(f"Flow ends: '{action}' not found in {list(curr.successors)}")
returnnxtdef_orch(self,shared,params=None):
curr,p,last_action=copy.copy(self.start_node),(paramsor {**self.params}),Nonewhilecurr: curr.set_params(p); last_action=curr._run(shared); curr=copy.copy(self.get_next_node(curr,last_action))
returnlast_actiondef_run(self,shared): p=self.prep(shared); o=self._orch(shared); returnself.post(shared,p,o)
defpost(self,shared,prep_res,exec_res): returnexec_resdefvisualize(self,namespace: dict,*,direction: str="LR",show_default: bool=False,highlight_starts: bool=True,max_nodes: int=1000)->str:
from .mermaidimportvisualizeas_visualizereturn_visualize(self,namespace,direction=direction,show_default=show_default,highlight_starts=highlight_starts,max_nodes=max_nodes)
And then I will talk about how this feature work. This is still an experimental idea, and I would appreciate more feedback. Whether you are unsatisfied with the generated results or have thoughts on the design philosophy, please leave a comment on this issue.
Just use the new method visualize to generate the mermaid code ,and then you could copy it into .md file to see the result. The method visualize use the extra function to do its work.
We want to use the variable name in the mermaid, but because of Python objects themselves do not know which variable name references them; therefore, the user must explicitly pass in the namespace:namespace=locals() . Do not assign multiple variable names to the same instance. This will trigger a warning and fall back to using the class name as the label for the Mermaid diagram.
visualize Skips node logic execution and exports the Mermaid diagram based purely on the connected graph structure (successors + start_node). But the pocketflow class Flow is dynamic. We define a flow’s subgraph as the nodes and edges reachable from start_node (searching only via successors). However, this inevitably leads to situations where the same object is covered by multiple Flow bodies (especially in cases of complex nesting or cross-layer connections). We adopt a ‘first win’ strategy: an object is placed only in the first Flow container it is encountered in.
Then about the flow, regarding the start, any connection pointing to a Flow is redirected to that Flow’s actual start node. Conversely, the successors of the Flow , which represent action-based transitions after the Flow ends, will ideally originate from the Flow’s internal terminal node (a node with empty successors). If the terminal node cannot be identified (e.g., in cases of a pure loop), we default to treating the flow’s initial node as its decision node and proceed to the next step based on that decision.”
In the end, Here are design philosophy for pocketflow.
Regarding flow termination, you should route it to a Terminal node, which has no successors. This ensures no errors occur in the existing framework, and our drawing tool will naturally treat it as an endpoint.
For autonomous decision scenarios involving internal loops, we still recommend having the decision node determine whether to enter a Terminal and end the flow. In cases involving loops, we fallback to starting from the actual starting point. We assume the flow’s initial node is its decision node, so the next step proceeds based on its decision.
Every flow is an independent entity, and ‘start’ is its unique entry point. Arbitrarily interrupting the flow to enter directly into its internals makes it difficult for automation tools to interpret.
Mermaid Examples, the red is pointing the flow entry node.
A visualization for the graph would help the users to know what would happen. And it would help the designers to detect the weakness in their deigns, So I built this, a new method for the class
Flowto visualize the whole graph. More explanation and details and below.If you just want to use this feature. you should download the mermaid.py , put it into the sourcecode portfolio and add a new method in class
Flowlike below. See my repository.And then I will talk about how this feature work. This is still an experimental idea, and I would appreciate more feedback. Whether you are unsatisfied with the generated results or have thoughts on the design philosophy, please leave a comment on this issue.
Just use the new method
visualizeto generate the mermaid code ,and then you could copy it into.mdfile to see the result. The methodvisualizeuse the extra function to do its work.We want to use the variable name in the mermaid, but because of Python objects themselves do not know which variable name references them; therefore, the user must explicitly pass in the namespace:
namespace=locals(). Do not assign multiple variable names to the same instance. This will trigger a warning and fall back to using the class name as the label for the Mermaid diagram.visualizeSkips node logic execution and exports the Mermaid diagram based purely on the connected graph structure (successors + start_node). But the pocketflow classFlowis dynamic. We define a flow’s subgraph as the nodes and edges reachable from start_node (searching only via successors). However, this inevitably leads to situations where the same object is covered by multiple Flow bodies (especially in cases of complex nesting or cross-layer connections). We adopt a ‘first win’ strategy: an object is placed only in the first Flow container it is encountered in.Then about the flow, regarding the start, any connection pointing to a Flow is redirected to that Flow’s actual start node. Conversely, the successors of the Flow , which represent action-based transitions after the Flow ends, will ideally originate from the Flow’s internal terminal node (a node with empty successors). If the terminal node cannot be identified (e.g., in cases of a pure loop), we default to treating the flow’s initial node as its decision node and proceed to the next step based on that decision.”
In the end, Here are design philosophy for pocketflow.
Mermaid Examples, the red is pointing the flow entry node.
flowchart LR start((Start)) --> n2 subgraph subflow_n0[pipeline] subgraph subflow_n1[stage1] n2["stage1_start"] n4["s1_clean"] n7["s1_reject"] n8["s1_validate"] n12["s1_end"] n2 --> n4 n4 -->|invalid| n7 n4 -->|valid| n8 n7 --> n12 n8 --> n12 end subgraph subflow_n3[stage2] n5["s2_route"] n9["s2a1"] n10["s2b1"] n13["s2a2"] n14["s2b2"] n16["s2_merge"] n17["s2b3"] n19["s2_end"] n5 -->|typeA| n9 n5 -->|typeB| n10 n9 --> n13 n10 --> n14 n13 --> n16 n14 --> n17 n16 --> n19 n17 --> n16 end subgraph subflow_n6[stage3] n11["s3_format"] n15["s3_save"] n18["s3_end"] n11 --> n15 n15 --> n18 end n12 -->|done| n5 n19 -->|done| n11 end classDef pf_true_start stroke:#d33,stroke-width:3px,fill:#fff5f5; class n2 pf_true_start class n5 pf_true_start class n11 pf_true_startflowchart LR start((Start)) --> n1 subgraph subflow_n0[outer_flow] n1["outer_main"] n4["outer_final"] subgraph subflow_n2[mid_flow] n3["mid_entry1"] n7["mid_other"] subgraph subflow_n5[core_flow] n6["core_a"] n8["core_b"] n9["core_c"] n6 --> n8 n8 --> n9 end n3 --> n6 n9 --> n7 end n1 --> n3 n7 --> n4 end classDef pf_true_start stroke:#d33,stroke-width:3px,fill:#fff5f5; class n1 pf_true_start class n3 pf_true_start class n6 pf_true_startflowchart LR start((Start)) --> n1 subgraph subflow_n0[outer] n1["main_router"] n7["join_node"] n12["final_node"] subgraph subflow_n2[flowA] n6["a1"] n11["a2"] n16["a3"] n6 --> n11 n11 --> n16 end subgraph subflow_n3[flowB] n8["b1"] n13["b2"] n17["b3"] n18["b4"] n8 --> n13 n13 -->|left| n17 n13 -->|right| n18 end subgraph subflow_n4[flowC] n9["c1"] n14["c2"] n19["c3"] n9 --> n14 n14 --> n19 n19 --> n14 end subgraph subflow_n5[flowD] n10["d1"] n15["d2"] subgraph subflow_n20[d_sub_flow] n21["d_sub1"] n22["d_sub2"] n23["d_sub3"] n21 --> n22 n22 --> n23 end n10 --> n15 n15 --> n21 end n1 -->|A| n6 n1 -->|B| n8 n1 -->|C| n9 n1 -->|D| n10 n7 --> n12 n9 -->|exit| n7 n16 -->|done| n7 n17 -->|done| n7 n18 -->|done| n7 n23 -->|done| n7 end classDef pf_true_start stroke:#d33,stroke-width:3px,fill:#fff5f5; class n1 pf_true_start class n6 pf_true_start class n8 pf_true_start class n9 pf_true_start class n10 pf_true_start class n21 pf_true_startflowchart LR start((Start)) --> n2 subgraph subflow_n0[outer] n3["after"] subgraph subflow_n1[inner] n2["a1"] n4["a2"] n2 --> n4 n4 --> n2 end n2 -->|done| n3 end classDef pf_true_start stroke:#d33,stroke-width:3px,fill:#fff5f5; class n2 pf_true_startflowchart LR start((Start)) --> n1 subgraph subflow_n0[main_flow] n1["top_decision"] n2["a_entry"] n3["b1"] n4["c_entry"] n5["a_decision"] n6["b2"] n8["a_action1"] n9["a_action2"] n10["b3"] n12["merge_point"] n14["post_process"] n16["final_decision"] n17["success"] n18["warning"] subgraph subflow_n7[c_sub_flow] n11["c_sub1"] n13["c_sub2"] n15["c_sub3"] n11 --> n13 n13 --> n15 end n1 -->|A| n2 n1 -->|B| n3 n1 -->|C| n4 n2 --> n5 n3 --> n6 n4 --> n11 n5 -->|A1| n8 n5 -->|A2| n9 n6 --> n10 n8 --> n12 n9 --> n12 n10 --> n12 n12 --> n14 n14 --> n16 n15 --> n12 n16 -->|success| n17 n16 -->|warning| n18 end classDef pf_true_start stroke:#d33,stroke-width:3px,fill:#fff5f5; class n1 pf_true_start class n11 pf_true_start