dkapur17 / streamlit-flow

Streamlit Component to quickly create Interactive Flow Diagrams using React Flow
https://stflow.streamlit.app
MIT License
111 stars 11 forks source link

Flow not honouring new starting values #28

Open ben-gineer opened 4 days ago

ben-gineer commented 4 days ago

Many thanks for this component. Much more suitable for my needs than the GraphViz one! I have an issue however.

I'm using version 1.5.0.

I have an app that renders a tree structure, based upon a starting node. When it first runs, it renders an entire tree from the root.

Each time the app runs, it recalculates the flow correctly based upon the provided starting node.

The user can then click on a child node, and use that as the new starting node (i.e. I want to render a subtree).

My problem is that when the flow is rendered, it's remembering the nodes from the first call, even though the input flow is correctly only including the subtree. i.e. this code is always rendering the same after the first call:

def create_stack_flow(stack_index):
    # This contains all the AWS CFN stacks as a dataframe
    stacks = st.session_state.stacks

    # Create dictionary of parent-child relationships
    parent_to_children = {}
    for index, row in stacks[stacks['ParentId'].notna()].iterrows():
        parent = extract_stack_name(row['ParentId'])
        if parent not in parent_to_children:
            parent_to_children[parent] = []
        parent_to_children[parent].append(row)

    nodes = []
    edges = []
    visited = set()

    def recursive_add_nodes(parent_name):
        if parent_name in visited:
            return
        visited.add(parent_name)

        # Process children
        children = parent_to_children.get(parent_name, [])
        for child in children:
            child_name = child.StackName
            service_name = get_service_name(child_name)

            # Add child node
            child_node = StreamlitFlowNode(
                id=child_name,
                pos=(0, 0),  # Initial position will be adjusted by TreeLayout
                data={'content': service_name},
                draggable=False,
                node_type='default',
                source_position='bottom',
                target_position='top',
                style={'color': 'white', 'backgroundColor': '#FF4B4B'}
            )
            nodes.append(child_node)

            # Add edge from parent to child
            edge = StreamlitFlowEdge(
                id=f'{parent_name}-{child_name}',
                source=parent_name,
                target=child_name,
                animated=True,
                edge_type='smoothstep'
            )
            edges.append(edge)

            # Recursively process child's children
            recursive_add_nodes(child_name)

    root_name = extract_stack_name(stack_index[0])
    root_node = StreamlitFlowNode(
        id=root_name,
        pos=(0, 0),
        data={'content': root_name},
        draggable=False,
        node_type='input',
        source_position='bottom',
        style={'color': 'white', 'backgroundColor': '#FF4B4B'}
    )
    nodes.append(root_node)

    # Add the children recursively
    recursive_add_nodes(root_name)

    # Create the flow state
    state = StreamlitFlowState(nodes, edges)

    return state

...

    descendant_event = streamlit_flow(
        'stack_layout',
        flow_state,
        layout=TreeLayout(direction='down'),
        fit_view=True,
        hide_watermark=True,
        height=700,
        get_node_on_click=True
    )

    if descendant_event.selected_id:
        stacks = st.session_state.stacks
        descendant_stack = stacks[stacks['StackName'] == descendant_event.selected_id]
        if descendant_stack is not None and not descendant_stack.empty:
            del st.session_state["stack_layout"]
            st.session_state.selected_stack = descendant_stack
            st.switch_page("cloud/stack.py")

I've tried clearing the stack_layout from session state - which appears to work OK.

I'm assuming the front-end code is caching something. What's the best way to fix this? I could programatically remove all the existing nodes prior to reloading from a new descendant node - but this seems like a hack.

Many thanks.

ben-gineer commented 4 days ago

I see if I name the streamlit_flow IDs with the name of the node, then this appears to work, i.e.

 descendant_event = streamlit_flow(
        "flow_" + stack.iloc[0].StackName,
        flow_state,
        layout=TreeLayout(direction='down'),
        fit_view=True,
        hide_watermark=True,
        height=700,
        get_node_on_click=True
    )

How can I ensure I'm not caching the different trees for all the nodes between reloads?