Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from agent.pipeline import ( | |
| analyze_change, | |
| oob_troubleshoot, | |
| perform_recovery, | |
| load_synapse_log, | |
| reset_state, | |
| ) | |
| from agent import topology | |
| 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: #0f4d3f; | |
| --og-mint: #38c49d; | |
| --og-fern: #1c6b53; | |
| --og-cream: #f5f3ec; | |
| --og-shadow: 0 18px 40px rgba(10,48,38,0.18); | |
| } | |
| html, body { overflow-x: hidden; } | |
| body, .gradio-container { | |
| font-family: 'Inter', system-ui, -apple-system, sans-serif; | |
| background: | |
| url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyNDAnIGhlaWdodD0nMjQwJyB2aWV3Qm94PScwIDAgMjQwIDI0MCc+CjxwYXRoIGQ9J00xMCAyMDAgQyA2MCAxNTAsIDEyMCAxMjAsIDE0MCA3MCBDIDE1NSA0MCwgMTg1IDMwLCAyMjAgMjAnIHN0cm9rZT0nIzFjNmI1Mycgc3Ryb2tlLXdpZHRoPSc0JyBmaWxsPSdub25lJyBzdHJva2UtbGluZWNhcD0ncm91bmQnIHN0cm9rZS1vcGFjaXR5PScuMTYnLz4KPHBhdGggZD0nTTUwIDIyMCBDIDkwIDE5MCwgMTMwIDE0MCwgMTcwIDEwMCcgc3Ryb2tlPScjM2NiZjlhJyBzdHJva2Utd2lkdGg9JzMnIGZpbGw9J25vbmUnIHN0cm9rZS1saW5lY2FwPSdyb3VuZCcgc3Ryb2tlLW9wYWNpdHk9Jy4yMCcvPgo8Y2lyY2xlIGN4PSc5MCcgY3k9JzE1MCcgcj0nMTAnIGZpbGw9JyMzY2JmOWEnIGZpbGwtb3BhY2l0eT0nLjI1Jy8+CjxjaXJjbGUgY3g9JzE1MCcgY3k9JzkwJyByPSc5JyBmaWxsPScjMGY0ZDNmJyBmaWxsLW9wYWNpdHk9Jy4xOCcvPgo8Y2lyY2xlIGN4PScyMDAnIGN5PSc1MCcgcj0nNycgZmlsbD0nIzhiZDdiMScgZmlsbC1vcGFjaXR5PScuMjInLz4KPC9zdmc+") repeat, | |
| radial-gradient(circle at 12% 20%, rgba(60,191,154,0.10), transparent 32%), | |
| radial-gradient(circle at 82% 16%, rgba(15,77,63,0.10), transparent 30%), | |
| linear-gradient(135deg, #f8f7f2 0%, #ecf2e9 100%); | |
| color: #0f1b1a; | |
| position: relative; | |
| } | |
| .gradio-container > * { position: relative; z-index: 1; } | |
| .og-hero { | |
| background: | |
| linear-gradient(120deg, rgba(15,77,63,0.32), rgba(56,196,157,0.18)), | |
| linear-gradient(160deg, rgba(140,215,177,0.20), rgba(15,77,63,0.14)), | |
| url("assets/trellis.jpg?v=1"); | |
| background-size: cover; | |
| background-position: center; | |
| background-repeat: no-repeat; | |
| border: 1px solid rgba(15,77,63,0.16); | |
| box-shadow: var(--og-shadow); | |
| border-radius: 18px; | |
| padding: 22px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .og-hero::before, | |
| .og-hero::after { | |
| content: ""; | |
| position: absolute; | |
| inset: -80px -20px auto auto; | |
| width: 240px; | |
| height: 240px; | |
| background: radial-gradient(circle at 40% 40%, rgba(60,191,154,0.4), transparent 60%), | |
| radial-gradient(circle at 70% 70%, rgba(15,77,63,0.45), transparent 62%); | |
| filter: blur(6px); | |
| opacity: 0.35; | |
| } | |
| .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 { | |
| border: 1px solid rgba(15,77,63,0.16); | |
| border-radius: 16px; | |
| background: rgba(255,255,255,0.9); | |
| box-shadow: 0 12px 28px rgba(0,0,0,0.07); | |
| padding: 16px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .og-panel::before { | |
| content: ""; | |
| position: absolute; | |
| inset: -30px auto auto -20px; | |
| width: 160px; | |
| height: 160px; | |
| background: radial-gradient(circle at 60% 40%, rgba(60,191,154,0.22), transparent 60%); | |
| opacity: 0.55; | |
| pointer-events: none; | |
| } | |
| .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( | |
| """ | |
| <div class="og-hero"> | |
| <div class="og-pill">Living OOB Mesh</div> | |
| <div class="og-pill">MCP + Topology</div> | |
| <h2 style="font-family:'Playfair Display',serif; margin:6px 0 4px 0;">🌿 Overgrowth</h2> | |
| <p style="margin:0; font-size:15px; color:#0f4d3f;"> | |
| Autonomous network reliability agent — parses changes, simulates risk, diagnoses drift, and self-heals with MCP. | |
| </p> | |
| </div> | |
| """, | |
| ) | |
| device_choices = topology.list_devices() | |
| with gr.Row(): | |
| with gr.Column(scale=1, elem_classes=["og-panel"]): | |
| change_request = gr.Textbox( | |
| label="Describe your network change", | |
| placeholder=( | |
| "Example: Add VLAN 120 for tenant Blue on leaf-01 and " | |
| "uplink it to tor-03 (staging only)." | |
| ), | |
| lines=6, | |
| ) | |
| run_btn = gr.Button("Run Analysis", variant="primary") | |
| with gr.Column(scale=2, elem_classes=["og-panel", "og-tabs"]): | |
| with gr.Tab("Plan"): | |
| plan_md = gr.Markdown(label="Planned Steps (Hybrid NL + JSON)") | |
| with gr.Tab("Risk & Topology"): | |
| risk_md = gr.Markdown(label="Risk & Topology Summary") | |
| with gr.Tab("Diffs"): | |
| diffs_md = gr.Markdown(label="Config Diff Summary") | |
| with gr.Tab("Rollback"): | |
| rollback_md = gr.Markdown(label="Rollback Plan") | |
| with gr.Tab("Tool Calls"): | |
| tool_log_md = gr.Markdown(label="MCP Tool Call Log") | |
| with gr.Tab("OOB Troubleshooting"): | |
| troubleshoot_device = gr.Dropdown( | |
| label="Device", | |
| choices=device_choices or ["leaf-01", "leaf-02"], | |
| value=device_choices[0] if device_choices else "leaf-01", | |
| ) | |
| troubleshoot_text = gr.Textbox( | |
| label="Describe the issue", | |
| placeholder="Example: Link flaps on leaf-01, seeing packet loss.", | |
| lines=3, | |
| ) | |
| troubleshoot_btn = gr.Button("Run Troubleshooting") | |
| troubleshoot_md = gr.Markdown(label="Troubleshooting Summary") | |
| troubleshoot_log = gr.Markdown(label="Troubleshooting Log") | |
| troubleshoot_actions = gr.Markdown(label="Recommended Actions") | |
| troubleshoot_conf = gr.Markdown(label="Root Cause Confidence") | |
| troubleshoot_state = gr.Markdown(label="Device State Snapshot") | |
| execute_btn = gr.Button("Execute Recovery Plan") | |
| execute_summary = gr.Markdown(label="Recovery Execution Summary") | |
| execute_log = gr.Markdown(label="Recovery Execution Log") | |
| reset_btn = gr.Button("Reset Overgrowth State (dangerous)", variant="secondary") | |
| reset_md = gr.Markdown(label="State Reset Log") | |
| with gr.Tab("Synapse Log"): | |
| synapse_btn = gr.Button("Refresh Log") | |
| synapse_md = gr.Markdown(label="Overgrowth Synapse Log") | |
| def on_run(change_text: str): | |
| """ | |
| Wraps the agent pipeline for Gradio. | |
| Returns: | |
| plan_markdown, risk_markdown, diffs_markdown, | |
| rollback_markdown, tool_log_markdown | |
| """ | |
| return analyze_change(change_text) | |
| def run_troubleshoot(dev, text): | |
| return oob_troubleshoot(dev, text) | |
| def execute_recovery_plan_wrapper(dev, text): | |
| return perform_recovery(dev, text) | |
| run_btn.click( | |
| fn=on_run, | |
| inputs=[change_request], | |
| outputs=[plan_md, risk_md, diffs_md, rollback_md, tool_log_md], | |
| ) | |
| troubleshoot_btn.click( | |
| fn=run_troubleshoot, | |
| inputs=[troubleshoot_device, troubleshoot_text], | |
| outputs=[ | |
| troubleshoot_md, | |
| troubleshoot_log, | |
| troubleshoot_actions, | |
| troubleshoot_conf, | |
| troubleshoot_state, | |
| ], | |
| ) | |
| execute_btn.click( | |
| fn=execute_recovery_plan_wrapper, | |
| inputs=[troubleshoot_device, troubleshoot_text], | |
| outputs=[execute_summary, execute_log], | |
| ) | |
| reset_btn.click( | |
| fn=reset_state, | |
| inputs=[], | |
| outputs=[reset_md], | |
| ) | |
| synapse_btn.click(fn=load_synapse_log, inputs=None, outputs=synapse_md) | |
| 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) | |