#!/usr/bin/env python3 import os import re import glob import json import base64 import zipfile import random import requests import openai from PIL import Image from urllib.parse import quote import streamlit as st import streamlit.components.v1 as components # Example base snippet: your normal Mermaid code DEFAULT_MERMAID = r""" flowchart LR U((User 😎)) -- "Talk 🗣️" --> LLM[LLM Agent 🤖\nExtract Info] click U "/?q=User%20😎" "Open 'User 😎'" "_blank" click LLM "/?q=LLM%20Agent%20Extract%20Info" "Open LLM Agent" "_blank" LLM -- "Query 🔍" --> HS[Hybrid Search 🔎\nVector+NER+Lexical] click HS "/?q=Hybrid%20Search%20Vector+NER+Lexical" "Open Hybrid Search" "_blank" HS -- "Reason 🤔" --> RE[Reasoning Engine 🛠️\nNeuralNetwork+Medical] click RE "/?q=Reasoning%20Engine%20NeuralNetwork+Medical" "Open Reasoning" "_blank" RE -- "Link 📡" --> KG((Knowledge Graph 📚\nOntology+GAR+RAG)) click KG "/?q=Knowledge%20Graph%20Ontology+GAR+RAG" "Open Knowledge Graph" "_blank" """ def parse_mermaid_edges(mermaid_text: str): """ 🍿 parse_mermaid_edges: - Find lines like `A -- "Label" --> B`. - Return adjacency dict: edges[A] = [(label, B), ...]. """ adjacency = {} # Regex to match lines like: A -- "Label" --> B edge_pattern = re.compile(r'(\S+)\s*--\s*"([^"]*)"\s*-->\s*(\S+)') # We split the text into lines and search for edges for line in mermaid_text.split('\n'): match = edge_pattern.search(line.strip()) if match: nodeA, label, nodeB = match.groups() if nodeA not in adjacency: adjacency[nodeA] = [] adjacency[nodeA].append((label, nodeB)) return adjacency def build_subgraph(adjacency, start_node): """ 🍎 build_subgraph: - BFS or DFS from start_node to gather edges. - For simplicity, we only gather direct edges from this node. - If you want a multi-level downstream search, do a BFS/DFS deeper. """ sub_edges = [] # If start_node has no adjacency, return empty if start_node not in adjacency: return sub_edges # For each edge out of start_node, store it in sub_edges for label, child in adjacency[start_node]: sub_edges.append((start_node, label, child)) return sub_edges def create_subgraph_mermaid(sub_edges, start_node): """ 🍄 create_subgraph_mermaid: - Given a list of edges in form (A, label, B), - Return a smaller flowchart snippet that includes them. """ # Start with the flowchart directive sub_mermaid = "flowchart LR\n" sub_mermaid += f" %% Subgraph for {start_node}\n" # For each edge, build a line: NodeA -- "Label" --> NodeB for (A, label, B) in sub_edges: # Potentially you can keep the original styles or shapes (like U((User 😎))). # If your original code has shapes, you can store them in a dict so A-> "U((User 😎))" etc. sub_mermaid += f' {A} -- "{label}" --> {B}\n' # Optionally add a comment to show the subgraph ends sub_mermaid += f" %% End of subgraph for {start_node}\n" return sub_mermaid def generate_mermaid_html(mermaid_code: str) -> str: """Tiny function to embed Mermaid code in HTML with a CDN.""" return f"""
{mermaid_code}
""" def main(): st.set_page_config(page_title="Partial Subgraph Demo", layout="wide") st.title("Partial Mermaid Subgraph from a Clicked Node") # 1) Show the main diagram st.subheader("Full Diagram:") full_html = generate_mermaid_html(DEFAULT_MERMAID) components.html(full_html, height=400, scrolling=True) # 2) Build adjacency from the original code adjacency = parse_mermaid_edges(DEFAULT_MERMAID) # 3) See if user clicked a shape, e.g. ?q=LLM%20Agent%20Extract%20Info query_params = st.query_params clicked = (query_params.get('q') or [""])[0] # If present if clicked: # The "clicked" node might contain spaces or special chars. # We typically match the "nodeId" from the code. # But your code might have the real node name "LLM" or "RE", etc. # If your node is "LLM" and the label is "LLM Agent Extract Info", # you might need to map them. # For simplicity, let's assume your node ID is the same as the label # after removing spaces. # Or if your original code uses node IDs like 'LLM' or 'U'. # We'll show an example: # We can guess your node ID might be "LLM" if the text includes "LLM". # Let's try a simpler approach: you store an internal mapping # from node ID -> label. We'll do a brute force approach: st.info(f"User clicked shape: {clicked}") # Suppose we want to find the adjacency key that best matches the clicked string: # This is a naive approach: # We'll see if 'clicked' is a substring of the adjacency's node key. possible_keys = [] for nodeKey in adjacency.keys(): if clicked.lower().replace("%20", " ").replace("extract info", "") in nodeKey.lower(): possible_keys.append(nodeKey) # If we found one or more possible matches, take the first if possible_keys: chosen_node = possible_keys[0] # Build subgraph from adjacency sub_edges = build_subgraph(adjacency, chosen_node) if sub_edges: sub_mermaid = create_subgraph_mermaid(sub_edges, chosen_node) # Display top-centered subgraph st.subheader(f"SearchResult Subgraph for Node: {chosen_node}") partial_html = generate_mermaid_html(sub_mermaid) components.html(partial_html, height=300, scrolling=False) else: st.warning(f"No outgoing edges from node '{chosen_node}'.") else: st.warning("No matching node found in adjacency for that query param.") else: st.info("No shape clicked, or no ?q= in the query parameters.") if __name__ == "__main__": main()