Text2Diagram / app.py
Anupam007's picture
Update app.py
37e5a6d verified
import gradio as gr
import requests
import spacy
import re
import time
import os # Import the 'os' module
import base64 # Import base64
# Try to load the spaCy model, download if it's not available
try:
nlp = spacy.load("en_core_web_sm")
except OSError:
print("Downloading en_core_web_sm model...")
import spacy.cli
spacy.cli.download("en_core_web_sm")
nlp = spacy.load("en_core_web_sm")
def text_to_diagram(text_description):
"""Convert process description into Mermaid.js flowchart."""
doc = nlp(text_description.lower())
steps = []
conditions = []
loops = []
complex_flow = []
sentences = [sent.text.strip() for sent in doc.sents]
for sentence in sentences:
sentence = sentence.replace("2, conditional branch", "").replace("3 loop start", "").replace("4 complex flow", "").strip()
if "then" in sentence:
tokens = sentence.split("then")
for token in tokens:
token = token.strip()
if token:
step = token.strip().replace(",", "").replace(".", "")
if step and step not in ["conditional branch", "loop start", "complex flow"]:
if "flowchart that shows" in step:
continue
steps.append(step)
cond_match = re.search(r"if ([a-z\s]+), (?:go to|proceed to) ([a-z\s]+); otherwise, (?:go to|proceed to) ([a-z\s]+)", sentence)
if not cond_match:
cond_match = re.search(r"if ([a-z\s]+), ([a-z\s]+); otherwise, ([a-z\s]+)", sentence)
if cond_match:
condition, true_action, false_action = cond_match.groups()
conditions.append((condition.strip(), true_action.strip(), false_action.strip()))
loop_match = re.search(r"(?:do|perform) (?:action )?([a-z]), repeat ([a-z]) until ([a-z]) is true", sentence)
if not loop_match:
loop_match = re.search(r"loop: ([a-z\s]+), repeat ([a-z\s]+) until ([a-z\s]+)", sentence)
if loop_match:
action, repeat_action, condition = loop_match.groups()
loops.append((action.strip(), condition.strip()))
if "flowchart that shows" in sentence:
flow_part = sentence.split("flowchart that shows")[1]
flow_steps = flow_part.split("then")
for step in flow_steps:
step = step.replace("and finally", "").replace(".", "").replace(",", "").strip()
if step:
complex_flow.append(step)
elif "then" in sentence and not steps:
tokens = sentence.split("then")
for token in tokens:
token = token.strip()
if token and token not in steps:
step = token.replace(",", "").replace(".", "").strip()
if step:
complex_flow.append(step)
if not complex_flow:
complex_flow = steps[1:] if steps else []
steps = steps[:1] if steps else []
diagram_code = "graph TD\n"
node_counter = 0
node_map = {}
def get_node_id(label):
nonlocal node_counter
if label not in node_map:
node_map[label] = chr(65 + node_counter)
node_counter += 1
return node_map[label]
if steps:
for i in range(len(steps)):
step = steps[i]
node_id = get_node_id(step)
diagram_code += f"{node_id}[{step.capitalize()}]\n"
if i > 0:
prev_node_id = get_node_id(steps[i - 1])
diagram_code += f"{prev_node_id} --> {node_id}\n"
else:
start_node = get_node_id("start")
diagram_code += f"{start_node}[Start]\n"
last_node = steps[-1] if steps else "start"
last_node_id = get_node_id(last_node)
for condition, true_action, false_action in conditions:
cond_node = get_node_id(f"cond_{condition}")
true_node = get_node_id(true_action)
false_node = get_node_id(false_action)
diagram_code += f"{last_node_id} --> {cond_node}{{{condition.capitalize()}?}}\n"
diagram_code += f"{cond_node} -->|Yes| {true_node}[{true_action.capitalize()}]\n"
diagram_code += f"{cond_node} -->|No| {false_node}[{false_action.capitalize()}]\n"
last_node = true_action
last_node_id = true_node
false_node_id = false_node
for action, condition in loops:
loop_node = get_node_id(f"loop_{action}")
cond_node = get_node_id(f"cond_{condition}")
finish_node = get_node_id(f"finish_{action}")
diagram_code += f"{last_node_id} --> {loop_node}[Loop: Perform {action.capitalize()}]\n"
diagram_code += f"{loop_node} --> {cond_node}{{{condition.capitalize()} is True?}}\n"
diagram_code += f"{cond_node} -->|No| {loop_node}\n"
diagram_code += f"{cond_node} -->|Yes| {finish_node}[Finish Loop]\n"
last_node_id = finish_node
for i in range(len(complex_flow)):
step = complex_flow[i]
node_id = get_node_id(f"flow_{i}_{step}")
diagram_code += f"{node_id}[{step.capitalize()}]\n"
if i == 0:
diagram_code += f"{last_node_id} --> {node_id}\n"
else:
prev_node_id = get_node_id(f"flow_{i - 1}_{complex_flow[i - 1]}")
diagram_code += f"{prev_node_id} --> {node_id}\n"
last_node_id = node_id
end_node = get_node_id("end")
diagram_code += f"{end_node}[End Process]\n"
diagram_code += f"{last_node_id} --> {end_node}\n"
if conditions:
diagram_code += f"{false_node_id} --> {end_node}\n"
try:
img_url = render_mermaid_to_url(diagram_code)
response = requests.get(img_url, timeout=10)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
image_data = response.content
temp_img_path = f"temp_diagram_{int(time.time())}.png"
with open(temp_img_path, "wb") as f:
f.write(image_data)
return diagram_code, temp_img_path
except requests.exceptions.RequestException as e:
print(f"Error rendering diagram (network): {e}")
return diagram_code, None
except Exception as e:
print(f"Error rendering diagram: {e}")
return diagram_code, None
def render_mermaid_to_url(mermaid_code):
"""Render Mermaid code to an image URL using Mermaid.ink."""
try:
encoded_code = base64.urlsafe_b64encode(mermaid_code.encode()).decode()
return f"https://mermaid.ink/img/{encoded_code}"
except Exception as e:
print(f"Error encoding Mermaid code: {e}")
return None
def gradio_interface(text_input):
"""Process user input and return diagram output for Gradio."""
try:
diagram_code, img_path = text_to_diagram(text_input)
print("Generated Mermaid Code:")
print(diagram_code)
return diagram_code, img_path if img_path else None
except Exception as e:
print(f"Error in Gradio interface: {e}")
return f"Error: {str(e)}", None
iface = gr.Interface(
fn=gradio_interface,
inputs=gr.Textbox(lines=5, placeholder="Describe your process (e.g., 'User logs in, then enters details...')"),
outputs=[
gr.Textbox(label="Generated Mermaid Code"),
gr.Image(label="Diagram Visualization", type="filepath"),
],
title="Text-to-Diagram Converter (Napkin AI Style)",
description="Convert process descriptions into structured flowcharts like Napkin AI.",
examples=[
["Developer writes code, then tests code. If tests pass, proceed to deploy; otherwise, debug code. Then release software to production."],
["Customer browses products, then adds to cart, then proceeds to checkout. If payment is verified, confirm order; otherwise, request payment again. Loop: Validate payment, repeat until successful. Then ship order and notify customer."],
["HR posts job opening, then reviews applications, then conducts interviews. If candidate is selected, offer job; otherwise, reject candidate. Then onboard new employee."],
["Student registers for exam, then studies material. If prepared, proceed to take exam; otherwise, study more. If exam is passed, receive certificate; otherwise, retake exam. Then celebrate success."],
["Customer places order, then kitchen prepares food. Loop: Check food quality, repeat until perfect. Then serve food and collect payment."],
["Traveler searches for flights, then selects flight, then books ticket. If booking is confirmed, proceed to payment; otherwise, search again. If payment is successful, receive itinerary; otherwise, cancel booking. Then plan trip."],
["User signs up, then verifies email, then logs in."],
],
allow_flagging="never",
)
if __name__ == "__main__":
iface.launch(share=True)