Spaces:
Sleeping
Sleeping
| """ | |
| DebugGenie - Hackathon Edition π | |
| Modern, Multi-Agent AI Debugging Assistant | |
| """ | |
| import os | |
| import time | |
| import gradio as gr | |
| import plotly.graph_objects as go | |
| import networkx as nx | |
| # --- CONFIGURATION --- | |
| MODAL_API_URL = os.environ.get("MODAL_API_URL") | |
| # --- CUSTOM CSS (Moved to launch() in Gradio 6) --- | |
| HACKATHON_CSS = """ | |
| /* π¨ Global Styles */ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&family=JetBrains+Mono:wght@400;700&display=swap'); | |
| .gradio-container { | |
| max-width: 1400px !important; | |
| } | |
| /* π¦Έ Hero Section */ | |
| .hero-section { | |
| background: linear-gradient(135deg, #1e40af 0%, #7c3aed 100%); | |
| padding: 3rem 2rem; | |
| border-radius: 1rem; | |
| color: white; | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| box-shadow: 0 10px 25px rgba(30, 64, 175, 0.2); | |
| } | |
| .hero-title { | |
| font-size: 3.5rem; | |
| font-weight: 800; | |
| margin-bottom: 0.5rem; | |
| background: linear-gradient(to right, #ffffff, #e0e7ff); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .hero-subtitle { | |
| font-size: 1.25rem; | |
| color: #e0e7ff; | |
| margin-bottom: 2rem; | |
| font-weight: 500; | |
| } | |
| .feature-badges { | |
| display: flex; | |
| justify-content: center; | |
| gap: 1rem; | |
| flex-wrap: wrap; | |
| } | |
| .badge { | |
| background: rgba(255, 255, 255, 0.15); | |
| backdrop-filter: blur(10px); | |
| padding: 0.5rem 1rem; | |
| border-radius: 2rem; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| transition: transform 0.2s; | |
| } | |
| .badge:hover { | |
| transform: translateY(-2px); | |
| background: rgba(255, 255, 255, 0.25); | |
| } | |
| /* π Stats Bar */ | |
| .stats-container { | |
| display: flex; | |
| justify-content: center; | |
| gap: 3rem; | |
| margin: 2rem 0; | |
| padding: 1.5rem; | |
| background: white; | |
| border-radius: 0.75rem; | |
| border: 1px solid #e5e7eb; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); | |
| } | |
| .stat-item { | |
| font-weight: 600; | |
| color: #4b5563; | |
| } | |
| /* π€ Agent Activity Monitor */ | |
| .agent-card { | |
| background: white; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 0.75rem; | |
| padding: 1rem; | |
| margin-bottom: 0.75rem; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| gap: 1rem; | |
| } | |
| .agent-card:hover { | |
| transform: translateX(4px); | |
| border-color: #6366f1; | |
| box-shadow: 0 4px 12px rgba(99, 102, 241, 0.1); | |
| } | |
| .agent-avatar { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.25rem; | |
| background: #f3f4f6; | |
| } | |
| .agent-info { | |
| flex: 1; | |
| } | |
| .agent-name { | |
| font-weight: 700; | |
| font-size: 0.9rem; | |
| color: #1f2937; | |
| margin-bottom: 0.25rem; | |
| } | |
| .agent-status { | |
| font-size: 0.8rem; | |
| color: #6b7280; | |
| } | |
| /* π’ Status Indicators */ | |
| .status-dot { | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| display: inline-block; | |
| } | |
| .status-dot.waiting { | |
| background-color: #ef4444; | |
| box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2); | |
| } | |
| .status-dot.processing { | |
| background-color: #f59e0b; | |
| animation: pulse 1.5s infinite; | |
| } | |
| .status-dot.complete { | |
| background-color: #10b981; | |
| } | |
| @keyframes pulse { | |
| 0% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4); } | |
| 70% { box-shadow: 0 0 0 6px rgba(245, 158, 11, 0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); } | |
| } | |
| /* π‘ Solution Cards */ | |
| .solution-card { | |
| background: white; | |
| border: 1px solid #e5e7eb; | |
| border-left: 4px solid #10b981; | |
| border-radius: 0.75rem; | |
| padding: 1.5rem; | |
| margin-bottom: 1.5rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); | |
| } | |
| .rank-badge { | |
| background: linear-gradient(135deg, #10b981 0%, #059669 100%); | |
| color: white; | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 1rem; | |
| font-size: 0.75rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| display: inline-block; | |
| margin-bottom: 0.75rem; | |
| } | |
| .confidence-bar-bg { | |
| height: 6px; | |
| background: #e5e7eb; | |
| border-radius: 3px; | |
| margin-top: 0.5rem; | |
| overflow: hidden; | |
| } | |
| .confidence-bar-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, #10b981 0%, #3b82f6 100%); | |
| border-radius: 3px; | |
| transition: width 1s ease-out; | |
| } | |
| /* π± Mobile Tweaks */ | |
| @media (max-width: 768px) { | |
| .hero-title { font-size: 2rem; } | |
| .stats-container { flex-direction: column; gap: 1rem; } | |
| } | |
| """ | |
| # --- HELPER FUNCTIONS --- | |
| def generate_agent_html(agent_name, status, message, progress): | |
| """Generates HTML for a single agent card.""" | |
| status_class = "waiting" | |
| if status == "processing": | |
| status_class = "processing" | |
| elif status == "complete": | |
| status_class = "complete" | |
| icon = "π€" | |
| if "Claude" in agent_name: | |
| icon = "π§ " | |
| elif "Gemini" in agent_name: | |
| icon = "β¨" | |
| elif "GPT-4" in agent_name: | |
| icon = "β‘" | |
| return f""" | |
| <div class="agent-card"> | |
| <div class="agent-avatar">{icon}</div> | |
| <div class="agent-info"> | |
| <div class="agent-name"> | |
| {agent_name} | |
| <span class="status-dot {status_class}" style="float: right;"></span> | |
| </div> | |
| <div class="agent-status">{message}</div> | |
| <div class="confidence-bar-bg"> | |
| <div class="confidence-bar-fill" style="width: {progress}%;"></div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| def create_3d_viz(): | |
| """Creates a 3D network graph using Plotly.""" | |
| G = nx.random_geometric_graph(20, 0.125) | |
| edge_x = [] | |
| edge_y = [] | |
| edge_z = [] | |
| for edge in G.edges(): | |
| x0, y0 = G.nodes[edge[0]]['pos'] | |
| x1, y1 = G.nodes[edge[1]]['pos'] | |
| edge_x.extend([x0, x1, None]) | |
| edge_y.extend([y0, y1, None]) | |
| edge_z.extend([0, 0, None]) | |
| edge_trace = go.Scatter3d( | |
| x=edge_x, y=edge_y, z=edge_z, | |
| line=dict(width=0.5, color='#888'), | |
| hoverinfo='none', | |
| mode='lines' | |
| ) | |
| node_x = [] | |
| node_y = [] | |
| node_z = [] | |
| for node in G.nodes(): | |
| x, y = G.nodes[node]['pos'] | |
| node_x.append(x) | |
| node_y.append(y) | |
| node_z.append(0) | |
| node_trace = go.Scatter3d( | |
| x=node_x, y=node_y, z=node_z, | |
| mode='markers', | |
| hoverinfo='text', | |
| marker=dict( | |
| showscale=True, | |
| colorscale='YlGnBu', | |
| reversescale=True, | |
| color=[], | |
| size=10, | |
| colorbar=dict( | |
| thickness=15, | |
| title='Node Connections', | |
| xanchor='left', | |
| titleside='right' | |
| ), | |
| line_width=2 | |
| ) | |
| ) | |
| fig = go.Figure( | |
| data=[edge_trace, node_trace], | |
| layout=go.Layout( | |
| title='Error Dependency Graph', | |
| titlefont_size=16, | |
| showlegend=False, | |
| hovermode='closest', | |
| margin=dict(b=20, l=5, r=5, t=40), | |
| scene=dict( | |
| xaxis=dict(showgrid=False, zeroline=False, showticklabels=False), | |
| yaxis=dict(showgrid=False, zeroline=False, showticklabels=False), | |
| zaxis=dict(showgrid=False, zeroline=False, showticklabels=False), | |
| ) | |
| ) | |
| ) | |
| return fig | |
| # --- MAIN APP --- | |
| def create_app(): | |
| # Create Blocks with theme and css (they should be here in Gradio 6) | |
| with gr.Blocks( | |
| title="DebugGenie π§", | |
| theme=gr.themes.Soft(primary_hue="indigo", neutral_hue="slate"), | |
| css=HACKATHON_CSS | |
| ) as demo: | |
| # 1. HERO SECTION | |
| with gr.Row(): | |
| gr.HTML(""" | |
| <div class="hero-section"> | |
| <h1 class="hero-title">DebugGenie π§</h1> | |
| <p class="hero-subtitle">Multi-Agent AI Debugging Assistant</p> | |
| <div class="feature-badges"> | |
| <span class="badge">π€ Powered by Claude β’ Gemini β’ GPT-4</span> | |
| <span class="badge">π¨ 3D Visualization</span> | |
| <span class="badge">π Voice Explanations</span> | |
| </div> | |
| </div> | |
| """) | |
| # 2. STATS BAR | |
| gr.HTML(""" | |
| <div class="stats-container"> | |
| <div class="stat-item">π€ 3 AI Agents Active</div> | |
| <div class="stat-item">π§ 5 MCP Tools Connected</div> | |
| <div class="stat-item">β‘ Real-time Analysis Ready</div> | |
| </div> | |
| """) | |
| # 3. MAIN LAYOUT (Two Columns) | |
| with gr.Row(): | |
| # LEFT COLUMN: INPUT (40%) | |
| with gr.Column(scale=2): | |
| with gr.Group(): | |
| gr.Markdown("### π Describe the Issue") | |
| error_input = gr.Code( | |
| label="Error Trace / Code Snippet", | |
| language="python", | |
| lines=10 | |
| ) | |
| with gr.Accordion("πΈ Add Context (Optional)", open=False): | |
| screenshot_input = gr.Image( | |
| label="Upload Screenshot", | |
| type="pil", | |
| height=200 | |
| ) | |
| analyze_btn = gr.Button( | |
| "π Analyze Error", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| with gr.Row(): | |
| example_btn = gr.Button("π² Try Example", size="sm", variant="secondary") | |
| clear_btn = gr.Button("ποΈ Clear", size="sm", variant="secondary") | |
| # RIGHT COLUMN: RESULTS (60%) | |
| with gr.Column(scale=3): | |
| # Agent Activity Monitor | |
| gr.Markdown("### π‘ Agent Activity Monitor") | |
| agent_monitor = gr.HTML( | |
| value=generate_agent_html("System", "waiting", "Ready to analyze...", 0) + | |
| generate_agent_html("Claude", "waiting", "Standby", 0) + | |
| generate_agent_html("Gemini", "waiting", "Standby", 0) | |
| ) | |
| # Results Tabs | |
| results_tabs = gr.Tabs(visible=False) | |
| with results_tabs: | |
| # Tab 1: Solutions | |
| with gr.Tab("π― Solutions"): | |
| solutions_display = gr.HTML() | |
| # Tab 2: 3D Viz | |
| with gr.Tab("π¨ 3D Visualization"): | |
| viz_plot = gr.Plot(label="Error Graph") | |
| # Tab 3: Raw Logs | |
| with gr.Tab("π Agent Logs"): | |
| chatbot = gr.Chatbot( | |
| label="Conversation", | |
| height=400, | |
| type="messages" | |
| ) | |
| # Voice Player (Bottom) | |
| voice_player = gr.Group(visible=False) | |
| with voice_player: | |
| gr.Markdown("### π Audio Briefing") | |
| audio_out = gr.Audio(interactive=False) | |
| # --- LOGIC --- | |
| def analyze_error(error_code, img): | |
| # 1. Initial State | |
| yield { | |
| agent_monitor: generate_agent_html("System", "processing", "Initializing swarm...", 10) + | |
| generate_agent_html("Claude", "waiting", "Queued", 0) + | |
| generate_agent_html("Gemini", "waiting", "Queued", 0), | |
| results_tabs: gr.update(visible=True), | |
| voice_player: gr.update(visible=False) | |
| } | |
| time.sleep(1) | |
| # 2. Claude Analysis | |
| yield { | |
| agent_monitor: generate_agent_html("System", "complete", "Swarm Active", 100) + | |
| generate_agent_html("Claude", "processing", "Analyzing syntax tree...", 45) + | |
| generate_agent_html("Gemini", "waiting", "Queued", 0) | |
| } | |
| time.sleep(1.5) | |
| # 3. Gemini Analysis | |
| yield { | |
| agent_monitor: generate_agent_html("System", "complete", "Swarm Active", 100) + | |
| generate_agent_html("Claude", "complete", "Analysis complete", 100) + | |
| generate_agent_html("Gemini", "processing", "Checking documentation...", 60) | |
| } | |
| time.sleep(1.5) | |
| # 4. Finalizing | |
| yield { | |
| agent_monitor: generate_agent_html("System", "complete", "Swarm Active", 100) + | |
| generate_agent_html("Claude", "complete", "Analysis complete", 100) + | |
| generate_agent_html("Gemini", "complete", "Solutions found", 100) | |
| } | |
| # 5. Show Results | |
| solution_html = """ | |
| <div class="solution-card"> | |
| <div class="rank-badge">π₯ Top Recommendation</div> | |
| <h3>Install Missing Dependency</h3> | |
| <p>The error <code>ModuleNotFoundError</code> indicates that <code>gradio</code> is not installed.</p> | |
| <div class="confidence-bar-bg"><div class="confidence-bar-fill" style="width: 98%;"></div></div> | |
| <pre style="background: #f3f4f6; padding: 10px; border-radius: 5px; margin-top: 10px;">pip install gradio</pre> | |
| </div> | |
| <div class="solution-card" style="border-left-color: #f59e0b;"> | |
| <div class="rank-badge" style="background: #f59e0b;">π₯ Alternative</div> | |
| <h3>Check Virtual Environment</h3> | |
| <p>Ensure you are running in the correct venv.</p> | |
| <div class="confidence-bar-bg"><div class="confidence-bar-fill" style="width: 75%; background: #f59e0b;"></div></div> | |
| </div> | |
| """ | |
| fig = create_3d_viz() | |
| yield { | |
| solutions_display: solution_html, | |
| viz_plot: fig, | |
| voice_player: gr.update(visible=True), | |
| chatbot: [ | |
| {"role": "user", "content": "Analyze this error."}, | |
| {"role": "assistant", "content": "I've analyzed the stack trace. The root cause is a missing library."} | |
| ] | |
| } | |
| def clear_inputs(): | |
| return "", None | |
| def load_example(): | |
| return """Traceback (most recent call last): | |
| File "app.py", line 10, in <module> | |
| import gradio as gr | |
| ModuleNotFoundError: No module named 'gradio'""", None | |
| # Event handlers | |
| analyze_btn.click( | |
| analyze_error, | |
| inputs=[error_input, screenshot_input], | |
| outputs=[agent_monitor, results_tabs, voice_player, solutions_display, viz_plot, chatbot] | |
| ) | |
| clear_btn.click( | |
| clear_inputs, | |
| outputs=[error_input, screenshot_input] | |
| ) | |
| example_btn.click( | |
| load_example, | |
| outputs=[error_input, screenshot_input] | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_app() | |
| # Simple launch without theme/css (already in Blocks constructor) | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860 | |
| ) |