File size: 6,119 Bytes
d129cb9
de77814
18794f1
d129cb9
 
 
 
8d894c1
 
 
d129cb9
8d894c1
d129cb9
8d894c1
400b83f
d129cb9
de77814
68b8ef0
400b83f
 
 
 
 
8d894c1
de77814
 
8d894c1
de77814
d129cb9
8d894c1
d129cb9
59e94a6
400b83f
d129cb9
de77814
400b83f
 
 
 
 
59e94a6
8d894c1
 
de77814
8d894c1
de77814
d129cb9
de77814
d129cb9
59e94a6
 
d129cb9
400b83f
59e94a6
 
18794f1
 
 
 
 
59e94a6
18794f1
 
 
59e94a6
 
18794f1
 
59e94a6
 
18794f1
59e94a6
 
18794f1
d129cb9
63eadc6
d129cb9
59e94a6
f7ea9ee
d129cb9
63eadc6
 
de77814
d129cb9
63eadc6
de77814
63eadc6
de77814
 
59e94a6
 
d129cb9
 
 
de77814
d129cb9
 
 
8d894c1
de77814
59e94a6
400b83f
de77814
 
 
 
d129cb9
de77814
 
 
 
68b8ef0
f7ea9ee
8d894c1
de77814
f7ea9ee
 
68b8ef0
 
f7ea9ee
d129cb9
63eadc6
de77814
68b8ef0
8d894c1
 
 
 
f7ea9ee
68b8ef0
 
f7ea9ee
de77814
8d894c1
63eadc6
d129cb9
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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()