import gradio as gr import os from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() from agent.pipeline import ( analyze_change, oob_troubleshoot, perform_recovery, load_synapse_log, reset_state, ) from agent import topology from agent.network_ops import ( get_lab_topology, get_lab_projects, manage_device, get_device_configuration, configure_device, backup_device_config, build_network_from_description, ) import json VINE_CSS = """ @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600;700&family=Inter:wght@400;500;600&display=swap'); :root { --og-green: #2d5f4f; --og-mint: #5fc9a0; --og-fern: #3d8169; --og-cream: #fafaf8; --og-shadow: 0 8px 24px rgba(45,95,79,0.12); } html, body { overflow-x: hidden; } body, .gradio-container { font-family: 'Inter', system-ui, -apple-system, sans-serif; background: repeating-linear-gradient(45deg, transparent, transparent 35px, rgba(95,201,160,0.03) 35px, rgba(95,201,160,0.03) 70px), repeating-linear-gradient(-45deg, transparent, transparent 35px, rgba(61,129,105,0.03) 35px, rgba(61,129,105,0.03) 70px), linear-gradient(to bottom, #fafaf8, #f5f7f5); color: #1a2f26; position: relative; min-height: 100vh; } .gradio-container > * { position: relative; z-index: 1; } .og-hero { background: linear-gradient(135deg, rgba(95,201,160,0.08), rgba(61,129,105,0.12)), repeating-linear-gradient(45deg, transparent, transparent 20px, rgba(95,201,160,0.04) 20px, rgba(95,201,160,0.04) 40px), repeating-linear-gradient(-45deg, transparent, transparent 20px, rgba(61,129,105,0.04) 20px, rgba(61,129,105,0.04) 40px), linear-gradient(to bottom right, #f8faf9, #f0f5f2); border: 1px solid rgba(95,201,160,0.2); box-shadow: var(--og-shadow); border-radius: 16px; padding: 28px; position: relative; overflow: hidden; } .og-hero::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, transparent, var(--og-mint), transparent); opacity: 0.6; } .og-hero::after { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at 20% 30%, rgba(95,201,160,0.08), transparent 40%), radial-gradient(circle at 80% 70%, rgba(61,129,105,0.08), transparent 40%); pointer-events: none; } .og-hero::after { inset: auto auto -120px -20px; transform: rotate(8deg); } .og-hero h2 { font-family: 'Playfair Display', serif; font-size: 28px; color: var(--og-green); letter-spacing: 0.02em; } .og-hero p { margin: 0; font-size: 15px; color: #0f4d3f; max-width: 720px; } .og-pill { display: inline-block; padding: 7px 12px; border-radius: 999px; background: rgba(60,191,154,0.22); color: var(--og-green); font-weight: 700; margin: 0 10px 8px 0; letter-spacing: 0.01em; box-shadow: 0 8px 18px rgba(0,0,0,0.06); } .og-panel { background: rgba(255,255,255,0.92); backdrop-filter: blur(16px); border: 1px solid rgba(95,201,160,0.15); border-radius: 12px; padding: 22px; box-shadow: 0 4px 16px rgba(45,95,79,0.08); position: relative; overflow: hidden; } /* Hide transparent boxes on empty rows/columns */ .og-main-row > div:empty, .og-main-row > div > div:empty { display: none !important; } /* Ensure main row doesn't create unwanted backgrounds */ .og-main-row { background: transparent !important; gap: 16px; } .og-main-row > div { background: transparent !important; } .og-panel::before { .og-panel::after { content: ""; position: absolute; inset: auto -20px -40px auto; width: 150px; height: 150px; background: radial-gradient(circle at 30% 30%, rgba(15,77,63,0.14), transparent 65%); opacity: 0.55; pointer-events: none; } .og-tab-title { font-family: 'Playfair Display', serif; color: var(--og-green); } .og-tabs .tab-nav button { font-family: 'Playfair Display', serif; color: var(--og-fern); } .og-tabs .tab-nav button.selected { background: rgba(60,191,154,0.16); border-color: rgba(60,191,154,0.4); color: var(--og-green); } .og-divider { border-bottom: 1px solid rgba(15,77,63,0.12); margin: 10px 0 14px 0; } .og-label { color: var(--og-green); font-weight: 700; letter-spacing: 0.02em; } textarea, input, select { border-radius: 12px !important; border: 1px solid rgba(15,77,63,0.16) !important; background: rgba(255,255,255,0.95) !important; } .gr-button { border-radius: 12px !important; box-shadow: 0 12px 22px rgba(15,77,63,0.18) !important; background: linear-gradient(120deg, var(--og-green), #128162) !important; color: #f5f3ec !important; border: none !important; } .gr-button.secondary { background: rgba(15,77,63,0.08) !important; color: var(--og-green) !important; box-shadow: none !important; } """ def build_ui(): with gr.Blocks( title="Overgrowth – a living digital environment", css=VINE_CSS, theme=gr.themes.Soft(primary_hue="green", neutral_hue="slate"), ) as demo: gr.HTML( """
Living OOB Mesh
MCP + Topology

🌿 Overgrowth

A living digital environment

""", ) device_choices = topology.list_devices() # Main Pipeline UI gr.Markdown("""### 🌿 Network Automation Pipeline **From Consultation → Production** Describe your network in plain English. Overgrowth handles the rest: 1. 💬 **Consultation** - Capture requirements 2. 📋 **Source of Truth** - Generate network data model 3. 📊 **Diagram** - Visualize topology 4. 🛒 **Bill of Materials** - Hardware shopping list 5. 🔧 **Setup Guide** - Human deployment steps + OOB network 6. 🤖 **Autonomous Deploy** - AI configures devices (Ansible/Netmiko) 7. 👁️ **Observability** - Monitoring, SNMP, topology discovery 8. ✅ **Validation** - pytest-based network testing & compliance """) with gr.Row(elem_classes=["og-main-row"]): with gr.Column(scale=1, elem_classes=["og-panel"]): pipeline_input = gr.Textbox( label="Describe Your Network", placeholder=( "Example: We're a coffee shop chain with 3 locations. " "We need WiFi for customers, POS systems with payment processing, " "security cameras, and secure VPN to HQ for centralized management. " "Each location has ~50 customers at peak time." ), lines=10, ) run_pipeline_btn = gr.Button("🚀 Run Full Pipeline", variant="primary", size="lg") gr.Markdown("#### 🔄 Tool Calls & Logs") tool_log_md = gr.Markdown(label="MCP Activity") with gr.Column(scale=2, elem_classes=["og-panel", "og-tabs"]): gr.Markdown("#### 📦 Pipeline Outputs") pipeline_status = gr.Markdown(label="Status") with gr.Tabs(): with gr.Tab("📋 Source of Truth"): sot_output = gr.Code(label="Network Model (YAML)", language="yaml", lines=20) with gr.Tab("🛒 Bill of Materials"): bom_output = gr.Markdown(label="Shopping List") with gr.Tab("🔧 Setup Guide"): setup_output = gr.Markdown(label="Deployment Guide") with gr.Tab("📊 Diagram"): diagram_output = gr.Textbox(label="Network Diagram", lines=25, max_lines=50) # Pipeline handler def run_pipeline(user_input): """Execute the full automation pipeline""" from agent.pipeline_engine import OvergrowthPipeline pipeline = OvergrowthPipeline() try: # Run the pipeline results = pipeline.run_full_pipeline(user_input) # Check pre-flight validation preflight = results.get('preflight', {}) ready_to_deploy = preflight.get('ready_to_deploy', False) # Format status status = "## 🚀 Pipeline Execution Complete!\n\n" # Pre-flight validation section if ready_to_deploy: status += "### ✅ Pre-flight Validation PASSED\n" status += f"- Schema validation: ✓ Passed\n" status += f"- Policy checks: ✓ Passed\n" if preflight.get('warnings'): status += f"- Warnings: {len(preflight['warnings'])}\n" if preflight.get('info'): status += f"- Info: {len(preflight['info'])}\n" status += "\n" else: status += "### ❌ Pre-flight Validation FAILED\n" status += "**Deployment blocked until errors are fixed:**\n\n" for error in preflight.get('errors', []): status += f"- ❌ {error}\n" status += "\n" if preflight.get('warnings'): status += "**Warnings:**\n" for warning in preflight.get('warnings', []): status += f"- ⚠️ {warning}\n" status += "\n" # Completed stages status += "### Completed Stages:\n" status += "0. " + ("✅" if ready_to_deploy else "❌") + " Pre-flight Validation\n" status += "1. ✅ Consultation - Intent captured\n" status += "2. ✅ Source of Truth - Network model generated\n" status += "3. ✅ Diagrams - Visualizations created\n" status += "4. ✅ Bill of Materials - Shopping list ready\n" status += "5. ✅ Setup Guide - Deployment instructions generated\n" if ready_to_deploy: status += "6. ⏳ Autonomous Deploy - Ready for execution\n" status += "7. ⏳ Observability - Ready for setup\n" status += "8. ⏳ Validation - Ready for verification\n\n" else: status += "6. 🚫 Autonomous Deploy - BLOCKED (fix validation errors)\n" status += "7. 🚫 Observability - BLOCKED\n" status += "8. 🚫 Validation - BLOCKED\n\n" status += "### 📁 Files Created:\n" status += "- `infra/network_model.yaml` - Source of Truth\n" status += "- `infra/bill_of_materials.json` - BOM data\n" status += "- `infra/setup_guide.md` - Deployment guide\n" # Extract outputs sot_yaml = results.get('model', {}) import yaml sot_str = yaml.dump(sot_yaml, default_flow_style=False) bom_md = results.get('shopping_list', 'No BOM generated') setup_md = results.get('setup_guide', 'No setup guide generated') diagram = results.get('diagrams', {}).get('ascii', 'No diagram available') return status, sot_str, bom_md, setup_md, diagram except Exception as e: error = f"## ❌ Pipeline Error\n\n{str(e)}" import traceback error += f"\n\n```\n{traceback.format_exc()}\n```" return error, "", "", "", "" # Event Handlers run_pipeline_btn.click( fn=run_pipeline, inputs=[pipeline_input], outputs=[pipeline_status, sot_output, bom_output, setup_output, diagram_output], ) return demo demo = build_ui().queue() # enable queue by default for Spaces app = demo # Hugging Face picks this up automatically if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860, share=False, show_api=False)