Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import time | |
| from deepengineer.backend.gradio_tools import run_agent_stream | |
| from deepengineer.common_path import DATA_DIR | |
| # Custom CSS for hardware engineering theme | |
| custom_css = """ | |
| .gradio-container { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| .main-header { | |
| background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); | |
| color: white; | |
| padding: 2rem; | |
| border-radius: 10px; | |
| margin-bottom: 2rem; | |
| text-align: center; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.1); | |
| } | |
| .main-header h1 { | |
| margin: 0; | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.3); | |
| } | |
| .main-header p { | |
| margin: 0.5rem 0 0 0; | |
| font-size: 1.1rem; | |
| opacity: 0.9; | |
| } | |
| .input-container { | |
| background: transparent; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| margin-bottom: 1rem; | |
| } | |
| .log-container { | |
| background: transparent; | |
| border-radius: 10px; | |
| padding: 1rem; | |
| margin-bottom: 2rem; | |
| } | |
| .log-header { | |
| display: flex; | |
| align-items: center; | |
| margin-bottom: 1rem; | |
| padding: 0.5rem; | |
| background: var(--background-fill-secondary); | |
| border-radius: 5px; | |
| } | |
| .log-icon { | |
| margin-right: 0.5rem; | |
| color: var(--body-text-color); | |
| } | |
| .log-title { | |
| font-weight: 600; | |
| color: var(--body-text-color); | |
| margin: 0; | |
| } | |
| .processing-indicator { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 1rem; | |
| background: linear-gradient(135deg, #28a745, #20c997); | |
| color: white; | |
| border-radius: 10px; | |
| margin-bottom: 1rem; | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 1; } | |
| 50% { opacity: 0.7; } | |
| 100% { opacity: 1; } | |
| } | |
| .spinner { | |
| border: 0.1875rem solid #f3f3f3; | |
| border-top: 0.1875rem solid #3498db; | |
| border-radius: 50%; | |
| width: 1.25rem; | |
| height: 1.25rem; | |
| animation: spin 1s linear infinite; | |
| margin-right: 0.5rem; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .response-container { | |
| background: var(--background-fill-primary); | |
| border: 1px solid var(--border-color-primary); | |
| border-radius: 10px; | |
| padding: 1.5rem; | |
| margin-top: 1rem; | |
| } | |
| .send-button { | |
| background: linear-gradient(135deg, #007bff, #0056b3) !important; | |
| color: white !important; | |
| border: none !important; | |
| padding: 0.5rem 1.5rem !important; | |
| border-radius: 0.5rem !important; | |
| font-weight: 600 !important; | |
| font-size: 0.95rem !important; | |
| transition: all 0.3s ease !important; | |
| box-shadow: 0 0.125rem 0.5rem rgba(0,123,255,0.3) !important; | |
| min-width: 7.5rem !important; | |
| max-width: 12.5rem !important; | |
| } | |
| .send-button:hover { | |
| transform: translateY(-0.0625rem) !important; | |
| box-shadow: 0 0.25rem 0.75rem rgba(0,123,255,0.4) !important; | |
| } | |
| .send-button:disabled { | |
| background: #6c757d !important; | |
| transform: none !important; | |
| box-shadow: none !important; | |
| } | |
| .log-text { | |
| font-family: 'Courier New', monospace; | |
| font-size: 0.9rem; | |
| line-height: 1.4; | |
| color: var(--body-text-color); | |
| background: var(--background-fill-secondary); | |
| border: 0.0625rem solid var(--border-color-primary); | |
| border-radius: 0.3125rem; | |
| padding: 1rem; | |
| max-height: 18.75rem; | |
| overflow-y: auto; | |
| } | |
| .log-entry { | |
| margin-bottom: 0.5rem; | |
| padding: 0.25rem 0; | |
| border-bottom: 1px solid var(--border-color-primary); | |
| } | |
| .log-entry:last-child { | |
| border-bottom: none; | |
| } | |
| .timestamp { | |
| color: var(--body-text-color-subdued); | |
| font-size: 0.8rem; | |
| } | |
| .tool-name { | |
| color: #007bff; | |
| font-weight: 600; | |
| } | |
| .tool-description { | |
| color: var(--body-text-color); | |
| } | |
| .hardware-icon { | |
| font-size: 2rem; | |
| margin-right: 1rem; | |
| } | |
| /* Dark mode specific adjustments */ | |
| .dark .input-container { | |
| background: transparent; | |
| } | |
| .dark .log-container { | |
| background: transparent; | |
| } | |
| .dark .response-container { | |
| background: var(--background-fill-primary); | |
| border-color: var(--border-color-primary); | |
| } | |
| .dark .log-text { | |
| background: var(--background-fill-secondary); | |
| border-color: var(--border-color-primary); | |
| color: var(--body-text-color); | |
| } | |
| .dark .log-header { | |
| background: var(--background-fill-secondary); | |
| } | |
| .dark .log-title { | |
| color: var(--body-text-color); | |
| } | |
| .dark .log-icon { | |
| color: var(--body-text-color); | |
| } | |
| .dark .timestamp { | |
| color: var(--body-text-color-subdued); | |
| } | |
| .dark .tool-description { | |
| color: var(--body-text-color); | |
| } | |
| .model-selector-compact { | |
| background: transparent; | |
| border: none; | |
| padding: 0; | |
| margin-bottom: 0.5rem; | |
| } | |
| .model-selector-compact .gr-dropdown { | |
| background: var(--background-fill-primary); | |
| color: var(--body-text-color); | |
| border: 0.0625rem solid var(--border-color-primary); | |
| border-radius: 0.375rem; | |
| padding: 0.25rem 0.5rem; | |
| font-size: 0.9rem; | |
| max-width: 12.5rem !important; | |
| } | |
| .model-selector-compact .gr-dropdown:hover { | |
| background: var(--background-fill-secondary); | |
| border-color: var(--border-color-accent); | |
| } | |
| .model-selector-compact .gr-dropdown .gr-dropdown-label { | |
| font-size: 0.85rem; | |
| font-weight: 500; | |
| margin-bottom: 0.25rem; | |
| } | |
| """ | |
| def format_log_entry(log_text): | |
| """Format log entries with timestamps and better structure""" | |
| if not log_text: | |
| return "" | |
| lines = log_text.split('\n') | |
| formatted_lines = [] | |
| for line in lines: | |
| if line.strip(): | |
| # Add timestamp and format tool calls | |
| timestamp = time.strftime("%H:%M:%S") | |
| if "Tool:" in line or "tool:" in line: | |
| formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>π§ {line.strip()}</span></div>") | |
| elif "Searching" in line or "searching" in line: | |
| formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>π {line.strip()}</span></div>") | |
| elif "Processing" in line or "processing" in line: | |
| formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>βοΈ {line.strip()}</span></div>") | |
| elif "Drawing" in line or "drawing" in line: | |
| formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>π¨ {line.strip()}</span></div>") | |
| else: | |
| formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-description'>{line.strip()}</span></div>") | |
| return "".join(formatted_lines) | |
| def run_agent_with_ui(user_input: str, model_id: str): | |
| """Enhanced agent runner with better UI feedback""" | |
| if not user_input.strip(): | |
| return "", "" | |
| # Initialize empty outputs | |
| log_buffer = "" | |
| agent_output = "" | |
| # Stream from the agent | |
| for agent_result, log_result in run_agent_stream(user_input, model_id): | |
| if log_result: | |
| log_buffer = log_result | |
| if agent_result: | |
| agent_output = agent_result | |
| # Format the log for display | |
| formatted_log = format_log_entry(log_buffer) | |
| yield agent_output, formatted_log | |
| # Create the Gradio interface | |
| with gr.Blocks(css=custom_css, title="DeepDraft - Hardware Engineering Assistant") as demo: | |
| # Header with hardware engineering theme | |
| with gr.Row(): | |
| gr.HTML(""" | |
| <div class="main-header"> | |
| <h1>π§ DeepDraft</h1> | |
| <p>Advanced Hardware Engineering Assistant</p> | |
| <p>Powered by AI-driven analysis and visualization</p> | |
| </div> | |
| """) | |
| # Model selection (compact) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| model_selector = gr.Dropdown( | |
| choices=[ | |
| ("π€ Mistral Medium", "mistral/mistral-medium-latest"), | |
| ("π§ DeepSeek Reasoner", "deepseek/deepseek-reasoner"), | |
| ("π DeepSeek Chat", "deepseek/deepseek-chat"), | |
| ("β‘ Mistral Small", "mistral/mistral-small-latest"), | |
| ], | |
| value="mistral/mistral-medium-latest", | |
| label="π€ AI Model", | |
| info="Choose the AI model for analysis", | |
| elem_classes=["model-selector-compact"], | |
| scale=1 | |
| ) | |
| # Main input section | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| gr.HTML('<div class="input-container">') | |
| user_input = gr.Textbox( | |
| label="π Describe your hardware engineering query", | |
| placeholder="e.g., 'Design a thermal management system for a high-power LED array' or 'Analyze the stress distribution in a mechanical bracket'", | |
| lines=3, | |
| max_lines=5 | |
| ) | |
| gr.HTML('</div>') | |
| # Send button | |
| with gr.Row(): | |
| send_button = gr.Button("π Launch Analysis", variant="primary", elem_classes=["send-button"]) | |
| # Processing indicator (hidden by default) | |
| processing_indicator = gr.HTML( | |
| """ | |
| <div class="processing-indicator" style="display: none;"> | |
| <div class="spinner"></div> | |
| <strong>DeepDraft is analyzing your request...</strong> | |
| </div> | |
| """, | |
| visible=False | |
| ) | |
| # Log output section | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.HTML(""" | |
| <div class="log-container"> | |
| <div class="log-header"> | |
| <span class="log-icon">π</span> | |
| <h3 class="log-title">Analysis Progress</h3> | |
| </div> | |
| </div> | |
| """) | |
| log_output = gr.HTML( | |
| value="<div class='log-text'>Ready to analyze your hardware engineering query...</div>", | |
| elem_classes=["log-text"] | |
| ) | |
| # Agent response section | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.HTML(""" | |
| <div class="response-container"> | |
| <h3>π― Analysis Results</h3> | |
| </div> | |
| """) | |
| agent_output = gr.Markdown( | |
| value="Enter your query above to begin the analysis.", | |
| elem_classes=["response-container"] | |
| ) | |
| # Event handlers | |
| def on_send_click(user_input, model_id): | |
| if not user_input.strip(): | |
| return "", "", gr.HTML(visible=False) | |
| # Show processing indicator | |
| processing_html = """ | |
| <div class="processing-indicator"> | |
| <div class="spinner"></div> | |
| <strong>DeepDraft is analyzing your request...</strong> | |
| </div> | |
| """ | |
| return "", "", gr.HTML(processing_html, visible=True) | |
| def on_generate_complete(agent_result, log_result): | |
| # Hide processing indicator when complete | |
| return agent_result, log_result, gr.HTML(visible=False) | |
| # Connect the button click | |
| send_button.click( | |
| fn=on_send_click, | |
| inputs=[user_input, model_selector], | |
| outputs=[agent_output, log_output, processing_indicator] | |
| ).then( | |
| fn=run_agent_with_ui, | |
| inputs=[user_input, model_selector], | |
| outputs=[agent_output, log_output], | |
| concurrency_limit=10 | |
| ).then( | |
| fn=on_generate_complete, | |
| inputs=[agent_output, log_output], | |
| outputs=[agent_output, log_output, processing_indicator] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch( | |
| allowed_paths=[DATA_DIR], | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False | |
| ) | |