Spaces:
Runtime error
Runtime error
#!/usr/bin/env python3 | |
""" | |
Hugging Face Spaces deployment for Residential Architecture Assistant | |
Multi-Agent LangGraph System for Architecture Consultation | |
""" | |
import os | |
import gradio as gr | |
import json | |
import traceback | |
from typing import List, Tuple, Dict, Any | |
from datetime import datetime | |
# Import the core system | |
from graph import ArchitectureAssistant | |
class HuggingFaceArchitectureApp: | |
"""Hugging Face Spaces compatible wrapper for the Architecture Assistant""" | |
def __init__(self): | |
self.assistant = None | |
self.conversation_history = [] | |
def initialize_assistant(self, api_key: str, user_id: str = None) -> Tuple[str, bool]: | |
"""Initialize the assistant with API key""" | |
try: | |
if not api_key or api_key.strip() == "": | |
return "β Please provide your OpenAI API key to get started.", False | |
# Initialize the assistant | |
self.assistant = ArchitectureAssistant( | |
openai_api_key=api_key.strip(), | |
user_id=user_id or f"hf_user_{datetime.now().strftime('%Y%m%d_%H%M%S')}" | |
) | |
return "β Architecture Assistant initialized! You can now start your consultation.", True | |
except Exception as e: | |
error_msg = f"β Error initializing assistant: {str(e)}" | |
print(f"Initialization error: {traceback.format_exc()}") | |
return error_msg, False | |
def chat_with_assistant(self, message: str, history: List[List[str]], api_key: str) -> Tuple[List[List[str]], str]: | |
"""Process chat message and return updated history""" | |
try: | |
# Check if assistant is initialized | |
if not self.assistant or not api_key: | |
error_response = "β Please provide your OpenAI API key first." | |
history.append([message, error_response]) | |
return history, "" | |
# Get response from assistant | |
response = self.assistant.chat(message, save_state=True) | |
# Add to history | |
history.append([message, response]) | |
return history, "" | |
except Exception as e: | |
error_response = f"β Error: {str(e)}\n\nPlease check your API key and try again." | |
print(f"Chat error: {traceback.format_exc()}") | |
history.append([message, error_response]) | |
return history, "" | |
def get_conversation_summary(self, api_key: str) -> str: | |
"""Get current conversation summary""" | |
try: | |
if not self.assistant or not api_key: | |
return "β Assistant not initialized. Please provide your API key first." | |
summary = self.assistant.get_conversation_summary() | |
# Format summary nicely | |
formatted_summary = f"""π **CONVERSATION SUMMARY** | |
π€ **User Requirements:** | |
β’ Budget: ${summary['user_requirements'].get('budget', 0):,} CAD | |
β’ Location: {summary['user_requirements'].get('location', 'Not specified')} | |
β’ Family Size: {summary['user_requirements'].get('family_size', 'Not specified')} | |
β’ Preferences: {', '.join(summary['user_requirements'].get('lifestyle_preferences', []) or ['None specified'])} | |
π **Floorplan Requirements:** | |
β’ Total Square Footage: {summary['floorplan_requirements'].get('total_sqft', 'Not specified')} sq ft | |
β’ Number of Floors: {summary['floorplan_requirements'].get('num_floors', 'Not specified')} | |
β’ Lot Dimensions: {summary['floorplan_requirements'].get('lot_dimensions', 'Not specified')} | |
β’ Rooms: {len(summary['floorplan_requirements'].get('rooms', []))} room types specified | |
π **Progress:** | |
β’ Total Messages: {summary['total_messages']} | |
β’ Current Topic: {summary['current_topic'] or 'General consultation'} | |
""" | |
return formatted_summary | |
except Exception as e: | |
return f"β Error getting summary: {str(e)}" | |
def reset_conversation(self, api_key: str) -> Tuple[List, str, str]: | |
"""Reset the conversation""" | |
try: | |
if self.assistant and api_key: | |
self.assistant.reset_conversation() | |
return [], "", "β Conversation reset. You can start a new consultation." | |
else: | |
return [], "", "β Please initialize with your API key first." | |
except Exception as e: | |
return [], "", f"β Error resetting: {str(e)}" | |
# Create app instance | |
app = HuggingFaceArchitectureApp() | |
# Create Gradio interface | |
def create_gradio_interface(): | |
"""Create the Gradio interface for Hugging Face Spaces""" | |
with gr.Blocks( | |
title="π Residential Architecture Assistant", | |
theme=gr.themes.Soft(), | |
css=""" | |
.container { max-width: 1200px; margin: auto; } | |
.header { text-align: center; padding: 20px; } | |
.chat-container { height: 500px; } | |
.summary-box { background-color: #f8f9fa; padding: 15px; border-radius: 10px; } | |
""" | |
) as demo: | |
# Header | |
gr.HTML(""" | |
<div class="header"> | |
<h1>π Residential Architecture Assistant</h1> | |
<p><em>Multi-Agent LangGraph System for Professional Architecture Consultation</em></p> | |
<p><strong>β¨ 7 AI Specialists | π Professional Floorplans | π° Montreal Market Analysis | π Building Codes</strong></p> | |
</div> | |
""") | |
# API Key input (persistent across the session) | |
with gr.Row(): | |
api_key_input = gr.Textbox( | |
label="π OpenAI API Key", | |
placeholder="Enter your OpenAI API key (sk-...)", | |
type="password", | |
info="Your API key is used securely and not stored. Required for AI agent functionality." | |
) | |
with gr.Row(): | |
init_btn = gr.Button("π Initialize Assistant", variant="primary", size="sm") | |
reset_btn = gr.Button("π Reset Conversation", variant="secondary", size="sm") | |
init_status = gr.HTML("") | |
# Main chat interface | |
with gr.Row(): | |
with gr.Column(scale=2): | |
chatbot = gr.Chatbot( | |
label="π¬ Architecture Consultation", | |
height=500, | |
show_label=True | |
) | |
with gr.Row(): | |
msg_input = gr.Textbox( | |
label="Your Message", | |
placeholder="Ask about home design, budgets, floorplans, building codes...", | |
lines=2, | |
scale=4 | |
) | |
send_btn = gr.Button("π€ Send", variant="primary", scale=1) | |
with gr.Column(scale=1): | |
gr.HTML("<h3>π Project Status</h3>") | |
summary_display = gr.HTML( | |
value="<div class='summary-box'>Initialize the assistant to see your project summary.</div>", | |
label="Conversation Summary" | |
) | |
summary_btn = gr.Button("π Update Summary", variant="secondary", size="sm") | |
# Usage instructions | |
with gr.Accordion("π How to Use", open=False): | |
gr.HTML(""" | |
<div style="padding: 15px;"> | |
<h4>π Getting Started:</h4> | |
<ol> | |
<li>Enter your OpenAI API key above</li> | |
<li>Click "Initialize Assistant"</li> | |
<li>Start your consultation with questions like:</li> | |
</ol> | |
<h4>π‘ Example Questions:</h4> | |
<ul> | |
<li>"I want to design a home but don't know where to start"</li> | |
<li>"I have a $800,000 budget for Montreal - is that realistic?"</li> | |
<li>"We're a family of 4, what size house do we need?"</li> | |
<li>"Can you generate a floorplan for a 2500 sq ft house?"</li> | |
<li>"What building permits do I need in Montreal?"</li> | |
</ul> | |
<h4>π€ Our AI Specialists:</h4> | |
<ul> | |
<li><strong>RouterAgent:</strong> Intelligent conversation routing</li> | |
<li><strong>GeneralDesignAgent:</strong> Architecture principles & design guidance</li> | |
<li><strong>BudgetAnalysisAgent:</strong> Montreal market cost analysis</li> | |
<li><strong>FloorplanAgent:</strong> Spatial planning & requirements</li> | |
<li><strong>FloorplanGeneratorAgent:</strong> Detailed architectural specifications</li> | |
<li><strong>DetailedBudgetAgent:</strong> Comprehensive cost breakdowns</li> | |
<li><strong>RegulationAgent:</strong> Montreal building codes & permits</li> | |
</ul> | |
</div> | |
""") | |
# Event handlers | |
def handle_init(api_key): | |
status_msg, success = app.initialize_assistant(api_key) | |
if success: | |
return gr.HTML(f"<div style='color: green; padding: 10px;'>{status_msg}</div>") | |
else: | |
return gr.HTML(f"<div style='color: red; padding: 10px;'>{status_msg}</div>") | |
def handle_chat(message, history, api_key): | |
return app.chat_with_assistant(message, history, api_key) | |
def handle_summary(api_key): | |
summary = app.get_conversation_summary(api_key) | |
return f"<div class='summary-box'>{summary}</div>" | |
def handle_reset(api_key): | |
history, msg, status = app.reset_conversation(api_key) | |
return history, msg, gr.HTML(f"<div style='color: green; padding: 10px;'>{status}</div>") | |
# Wire up events | |
init_btn.click( | |
handle_init, | |
inputs=[api_key_input], | |
outputs=[init_status] | |
) | |
msg_input.submit( | |
handle_chat, | |
inputs=[msg_input, chatbot, api_key_input], | |
outputs=[chatbot, msg_input] | |
) | |
send_btn.click( | |
handle_chat, | |
inputs=[msg_input, chatbot, api_key_input], | |
outputs=[chatbot, msg_input] | |
) | |
summary_btn.click( | |
handle_summary, | |
inputs=[api_key_input], | |
outputs=[summary_display] | |
) | |
reset_btn.click( | |
handle_reset, | |
inputs=[api_key_input], | |
outputs=[chatbot, msg_input, init_status] | |
) | |
# Footer | |
gr.HTML(""" | |
<div style="text-align: center; padding: 20px; margin-top: 30px; border-top: 1px solid #ddd;"> | |
<p><strong>π Residential Architecture Assistant v3.0</strong></p> | |
<p>Built with <a href="https://langchain.ai/langgraph">LangGraph</a> β’ | |
Powered by <a href="https://openai.com">OpenAI</a> β’ | |
Interface by <a href="https://gradio.app">Gradio</a></p> | |
<p><em>Professional architecture consultation from concept to construction</em></p> | |
</div> | |
""") | |
return demo | |
# Create and launch the interface | |
if __name__ == "__main__": | |
demo = create_gradio_interface() | |
# Launch configuration for Hugging Face Spaces | |
demo.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=False, | |
show_error=True, | |
max_threads=10 | |
) |