Spaces:
Runtime error
Runtime error
File size: 11,549 Bytes
10b617b d8fafa8 10b617b d8fafa8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
#!/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)
# 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() |