import gradio as gr import asyncio from typing import List, Dict, Any, Optional from loguru import logger import json import os # Import core components from ui.backend import DebugBackend, LocalBackend from core.models import RankedSolution from visualization.blaxel_generator import ErrorFlowVisualizer from voice.elevenlabs_tts import VoiceExplainer from config.api_keys import api_config from theme import debuggenie_theme class DebugGenieUI: def __init__(self, backend: DebugBackend): self.backend = backend self.visualizer = ErrorFlowVisualizer() # Initialize voice explainer if API key is available try: self.voice_explainer = VoiceExplainer(api_key=api_config.elevenlabs_api_key) except: logger.warning("ElevenLabs API key not found - voice features disabled") self.voice_explainer = None async def handle_analyze( self, error_text: str, screenshot, codebase_files, progress=gr.Progress() ): """Main analysis handler with progressive updates.""" try: # Validate inputs if not error_text and screenshot is None: return ( "
⚠️ Please provide an error message or screenshot
", "
Solutions will appear here after analysis
", "
Visualization will appear here
", None, None ) progress(0.2, desc="Analyzing error...") # Build context context = { 'error_text': error_text, 'image': screenshot, 'code_context': "" } if screenshot is not None: context['type'] = 'ide' progress(0.5, desc="Running AI analysis...") # Run backend analysis result = await self.backend.analyze(context) progress(0.8, desc="Generating results...") # Build root cause display root_cause_html = f"""

🎯 Root Cause

{result.root_cause}

""" # Generate solutions HTML solutions_html = self._generate_solutions_html(result.solutions) # Generate visualization mock_trace = self.visualizer.generate_mock_trace() viz_html = self.visualizer.generate_flow(mock_trace) # Generate voice explanation voice_audio = None if self.voice_explainer and result.solutions: try: top_solution = result.solutions[0] ranked_sol = RankedSolution( rank=1, title=top_solution.get('title', 'Solution'), description=top_solution.get('description', ''), steps=[], confidence=top_solution.get('probability', 0.5), sources=[], why_ranked_here=f"Top solution with {top_solution.get('probability', 0)*100:.0f}% confidence", trade_offs=[] ) audio_bytes = self.voice_explainer.generate_explanation(ranked_sol, mode="walkthrough") if audio_bytes: voice_path = self.voice_explainer.save_audio( audio_bytes, f"explanation_{hash(error_text[:100])}.mp3" ) voice_audio = voice_path except Exception as e: logger.warning(f"Voice generation failed: {e}") progress(1.0, desc="Complete!") return ( root_cause_html, solutions_html, viz_html, voice_audio, { "execution_time": f"{result.execution_time:.2f}s", "confidence": f"{result.confidence_score:.1%}", "solutions_found": len(result.solutions) } ) except Exception as e: logger.error(f"Analysis failed: {e}") return ( f"
❌ Analysis failed: {str(e)}
", "", "", None, {"error": str(e)} ) def _generate_solutions_html(self, solutions: List[Dict]) -> str: """Generate clean, readable solutions display.""" if not solutions: return "
No solutions found
" html = "
" for idx, sol in enumerate(solutions[:5], 1): title = sol.get('title', f'Solution {idx}') desc = sol.get('description', 'No description available') prob = sol.get('probability', 0.5) # Color based on confidence if prob > 0.7: bg_color = "#10b981" badge_text = "High Confidence" elif prob > 0.4: bg_color = "#f59e0b" badge_text = "Medium Confidence" else: bg_color = "#6366f1" badge_text = "Low Confidence" html += f"""
{idx}

{title}

{prob:.0%} • {badge_text}

{desc}

""" html += "
" return html def create_interface(backend: DebugBackend): """Create the main Gradio interface with clean, simple design.""" ui = DebugGenieUI(backend) with gr.Blocks(title="DebugGenie - AI Debugging Assistant") as demo: # Header gr.Markdown( """ # 🧞 DebugGenie ### AI-Powered Debugging Assistant Paste your error message below and get instant solutions from multiple AI agents. """, elem_classes="header" ) # Main input section gr.Markdown("## 📋 Enter Your Error") error_input = gr.Code( label="Error Message or Stack Trace", language="python", lines=10, placeholder="Paste your error message, stack trace, or exception here...", show_label=False ) # Optional inputs in accordion with gr.Accordion("➕ Add Screenshot or Files (Optional)", open=False): with gr.Row(): screenshot_input = gr.Image( label="Screenshot", type="pil", sources=["upload", "clipboard"] ) codebase_files = gr.File( label="Related Code Files", file_count="multiple" ) # Analyze button analyze_btn = gr.Button( "🔍 Analyze Error", variant="primary", size="lg" ) # Results section gr.Markdown("## 🎯 Analysis Results") root_cause_display = gr.HTML( label="Root Cause", value="
Results will appear here after analysis
" ) # Solutions in accordion with gr.Accordion("💡 Recommended Solutions", open=True): solutions_display = gr.HTML( value="
Solutions will appear here
" ) # Additional details in tabs with gr.Tabs(): with gr.Tab("📊 Error Visualization"): viz_display = gr.HTML() with gr.Tab("🎙️ Voice Explanation"): voice_output = gr.Audio( label="AI-Generated Explanation", autoplay=False ) with gr.Tab("📈 Technical Details"): analysis_json = gr.JSON(label="Analysis Metrics") # Examples section gr.Markdown("### 📚 Try These Examples") gr.Examples( examples=[ [ """Traceback (most recent call last): File "app.py", line 42, in process_data result = json.loads(data) json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)""", None, None ], [ """TypeError: 'NoneType' object is not subscriptable File "main.py", line 15, in get_user return users[user_id]['name']""", None, None ], [ """AttributeError: 'list' object has no attribute 'keys' File "data_processor.py", line 28 for key in config.keys():""", None, None ] ], inputs=[error_input, screenshot_input, codebase_files], label=None ) # Event handler analyze_btn.click( fn=ui.handle_analyze, inputs=[error_input, screenshot_input, codebase_files], outputs=[ root_cause_display, solutions_display, viz_display, voice_output, analysis_json ] ) return demo if __name__ == "__main__": backend = LocalBackend() demo = create_interface(backend) demo.launch( server_name="127.0.0.1", server_port=7860, share=False, show_error=True, theme=gr.themes.Soft( primary_hue="indigo", radius_size="lg" ), css=""" /* Clean, minimal styling */ .header { text-align: center; padding: 24px 0; margin-bottom: 32px; } .header h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 8px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .header h3 { color: #64748b; font-weight: 400; } /* Container spacing */ .gradio-container { max-width: 1200px; margin: 0 auto; } /* Button styling */ button.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; font-weight: 600; } button.primary:hover { transform: translateY(-2px); box-shadow: 0 8px 16px rgba(102, 126, 234, 0.3); } /* Smooth transitions */ * { transition: all 0.2s ease; } """ )