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)