wangzerui's picture
updata gradio
a7e11ee
raw
history blame
11.7 kB
#!/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
)