import streamlit as st import json import re import networkx as nx import matplotlib.pyplot as plt from transformers import pipeline # Initialize two text2text-generation pipelines using Flan-T5-large. model_request_to_graph = pipeline('text2text-generation', model='google/flan-t5-large') model_reply_with_graph = pipeline('text2text-generation', model='google/flan-t5-large') def generate_input_graph(request_text): """ Model 1: Convert the customer request into a structured graph. The prompt now includes an explicit example to guide the model. """ prompt = ( "You are an assistant that converts a natural language customer request into a structured graph. " "Output only valid JSON with exactly two keys: 'nodes' and 'edges'.\n" "Example output:\n" '{"nodes": ["City A", "City B", "City C", "City D", "City E"], ' '"edges": [["City A", "City B"], ["City B", "City C"], ["City C", "City D"], ["City D", "City E"]]} \n\n' "Do not include any extra text, explanation, or commentary.\n\n" f"Customer Request: \"{request_text}\"\n\n" "Structured Graph JSON:" ) output = model_request_to_graph(prompt, max_new_tokens=150, do_sample=True, temperature=0.7)[0]['generated_text'] return output def generate_reply_and_graph(request_text): """ Model 2: Generate a detailed response along with a structured graph. The prompt now includes an explicit example of the expected JSON format. """ prompt = ( "You are an assistant that responds to a customer request with a detailed reply and a structured graph. " "First, provide a helpful textual reply to the request. Then output a valid JSON object with exactly two keys: 'nodes' and 'edges'.\n" "Example JSON output:\n" '{"nodes": ["City A", "City B", "City C", "City D", "City E"], ' '"edges": [["City A", "City B"], ["City B", "City C"], ["City C", "City D"], ["City D", "City E"]]} \n\n' "Do not include any extra text or commentary outside the JSON.\n\n" f"Customer Request: \"{request_text}\"\n\n" "Detailed Response and Structured Graph JSON:" ) output = model_reply_with_graph(prompt, max_new_tokens=200, do_sample=True, temperature=0.7)[0]['generated_text'] return output def try_parse_json(text): """ Attempt to extract a valid JSON substring from the model output using a non-greedy match. Returns the parsed JSON if valid, otherwise logs the error and returns None. """ st.write("Raw model output:", text) # Non-greedy match for JSON object json_pattern = re.compile(r'\{.*?\}', re.DOTALL) match = json_pattern.search(text) if match: json_str = match.group() try: parsed = json.loads(json_str) # Check for required keys and non-empty lists if 'nodes' in parsed and 'edges' in parsed and parsed['nodes'] and parsed['edges']: return parsed else: st.error("Parsed JSON is missing required keys or contains empty lists.") st.write("Extracted JSON:", json_str) return None except Exception as e: st.error(f"JSON parsing error: {e}") st.write("Extracted text:", json_str) return None else: st.error("No JSON object found in the model output.") return None def build_and_visualize_graph(graph_data, title="Graph Visualization"): """ Build a NetworkX directed graph from a dictionary with 'nodes' and 'edges' and visualize it using Matplotlib. """ if not graph_data or 'nodes' not in graph_data or 'edges' not in graph_data: st.error("Invalid graph data format.") return G = nx.DiGraph() for node in graph_data['nodes']: G.add_node(node) for edge in graph_data['edges']: if isinstance(edge, list) and len(edge) == 2: G.add_edge(edge[0], edge[1]) else: st.warning(f"Skipping invalid edge format: {edge}") plt.figure(figsize=(8, 6)) pos = nx.spring_layout(G, seed=42) nx.draw(G, pos, with_labels=True, node_color='skyblue', edge_color='gray', node_size=2000, font_size=10) plt.title(title) st.pyplot(plt) def main(): st.title("Two-LLM Pipeline: Request & Reply with Graph Visualization") st.write( "Enter a customer request to see it transformed into an input graph and then receive a detailed reply " "with its own structured graph. Both graphs are built using non-empty 'nodes' and 'edges'." ) customer_request = st.text_area("Enter Customer Request:", height=150) if st.button("Process Request"): if not customer_request.strip(): st.error("Please enter a valid customer request.") return # --- Stage 1: Request to Input Graph --- st.subheader("Stage 1: Customer Request → Input Graph") model1_output = generate_input_graph(customer_request) st.code(model1_output, language="json") input_graph_data = try_parse_json(model1_output) if input_graph_data: st.write("Input Graph Data:") st.json(input_graph_data) build_and_visualize_graph(input_graph_data, title="Input Graph (Customer Request)") else: st.warning("Could not parse a valid graph structure from Model 1 output.") # --- Stage 2: Detailed Reply with Structured Graph --- st.subheader("Stage 2: Detailed Response with Structured Graph") model2_output = generate_reply_and_graph(customer_request) st.code(model2_output, language="json") output_graph_data = try_parse_json(model2_output) if output_graph_data: st.write("Output Graph Data:") st.json(output_graph_data) build_and_visualize_graph(output_graph_data, title="Output Graph (Structured Response)") else: st.warning("Could not parse a valid graph structure from Model 2 output.") if __name__ == "__main__": main()