wangzerui commited on
Commit
3e4f0ef
Β·
1 Parent(s): a4c34d8
Files changed (2) hide show
  1. __pycache__/app.cpython-311.pyc +0 -0
  2. app.py +108 -472
__pycache__/app.cpython-311.pyc CHANGED
Binary files a/__pycache__/app.cpython-311.pyc and b/__pycache__/app.cpython-311.pyc differ
 
app.py CHANGED
@@ -1,516 +1,152 @@
1
  #!/usr/bin/env python3
2
  """
3
- Final Enhanced Gradio Web UI with Auto-Loading User Conversations
 
4
  """
5
 
6
  import os
7
  import gradio as gr
8
- from dotenv import load_dotenv
9
- from graph import ArchitectureAssistant
10
- from user_state_manager import user_state_manager
11
- import json
12
- import hashlib
13
- from datetime import datetime
14
 
15
-
16
- class GradioArchitectureApp:
17
- def __init__(self):
18
- load_dotenv()
19
- # For Hugging Face Spaces deployment - get API key from environment
20
- api_key = os.environ.get("OPENAI_API_KEY")
21
-
22
- # Make API key optional for HF Spaces - users can provide their own
23
- self.api_key = api_key
24
- self.assistant = None
25
- self.current_user_id = None
26
- self.conversation_history = []
27
-
28
- # Try to import state manager, but don't fail if it's not available
29
- try:
30
- from user_state_manager import user_state_manager
31
- self.state_manager = user_state_manager
32
- except ImportError:
33
- self.state_manager = None
34
-
35
- def initialize_user_session(self, user_identifier: str = None, api_key: str = None):
36
- """Initialize user session with persistent state"""
37
- if not user_identifier:
38
- # Generate user ID from timestamp for demo
39
- user_identifier = hashlib.md5(str(datetime.now()).encode()).hexdigest()[:12]
40
-
41
- # Use provided API key or fallback to environment
42
- effective_api_key = api_key or self.api_key
43
- if not effective_api_key:
44
- raise ValueError("No API key provided")
45
-
46
- self.current_user_id = user_identifier
47
- self.assistant = ArchitectureAssistant(effective_api_key, user_identifier)
48
-
49
- return user_identifier
50
-
51
- def change_user_id(self, new_user_id: str, api_key: str = None):
52
- """Change user ID and auto-load their conversation history"""
53
- if not new_user_id.strip():
54
- return [], "❌ Please enter a valid User ID", "", "Start chatting to see your requirements here!"
55
-
56
- try:
57
- # Use provided API key or fallback to environment
58
- effective_api_key = api_key or self.api_key
59
- if not effective_api_key:
60
- return [], "❌ Please provide your OpenAI API key", "", "API key required"
61
-
62
- # Set new user ID
63
- self.current_user_id = new_user_id.strip()
64
-
65
- # Create new assistant instance for this user
66
- self.assistant = ArchitectureAssistant(effective_api_key, self.current_user_id)
67
-
68
- # Try to load their latest conversation
69
- if self.state_manager:
70
- history = self.state_manager.get_user_history(self.current_user_id)
71
- else:
72
- history = []
73
-
74
- if history:
75
- # Load most recent conversation
76
- latest_session = history[0]["session_id"]
77
- success = self.assistant.load_previous_state(latest_session)
78
-
79
- if success:
80
- # Convert state messages to chat history
81
- chat_history = []
82
- messages = self.assistant.state.get("messages", [])
83
-
84
- current_pair = [None, None]
85
- for msg in messages:
86
- if msg["role"] == "user":
87
- current_pair = [msg["content"], None]
88
- elif msg["role"] == "assistant":
89
- if current_pair[0] is not None:
90
- current_pair[1] = msg["content"]
91
- chat_history.append(current_pair)
92
- current_pair = [None, None]
93
-
94
- status_msg = f"βœ… Loaded {len(history)} previous conversation(s) for user {new_user_id}"
95
- summary = self.get_conversation_summary()
96
-
97
- return chat_history, status_msg, new_user_id, summary
98
- else:
99
- status_msg = f"βœ… User {new_user_id} found but couldn't load conversation. Starting fresh."
100
- summary = self.get_conversation_summary()
101
- return [], status_msg, new_user_id, summary
102
- else:
103
- # New user
104
- status_msg = f"βœ… New user {new_user_id} - starting fresh conversation"
105
- summary = self.get_conversation_summary()
106
- return [], status_msg, new_user_id, summary
107
-
108
- except Exception as e:
109
- return [], f"❌ Error loading user {new_user_id}: {str(e)}", "", "Error loading user"
110
 
111
- def chat_with_assistant(self, message, history, user_id_input="", api_key=""):
112
- """Process user message and return response with state saving"""
113
- if not message.strip():
114
- return history, "", user_id_input, self.get_conversation_summary()
115
-
116
- # Check for API key first
117
- effective_api_key = api_key or self.api_key
118
- if not effective_api_key:
119
- error_msg = "❌ Please provide your OpenAI API key to use the Architecture Assistant."
120
  history.append([message, error_msg])
121
- return history, "", user_id_input, "API key required"
122
-
123
- # Ensure we have an assistant instance
124
- if not self.assistant:
125
- if user_id_input.strip():
126
- self.current_user_id = user_id_input.strip()
127
- self.assistant = ArchitectureAssistant(effective_api_key, self.current_user_id)
128
- else:
129
- user_id = self.initialize_user_session(api_key=effective_api_key)
130
- return history, "", user_id, self.get_conversation_summary()
131
 
132
- # Handle special commands
133
- if message.lower() == 'reset':
134
- self.assistant.reset_conversation()
135
- self.conversation_history = []
136
- status_msg = "πŸ”„ Conversation reset! New session started."
137
- return [[status_msg, ""]], "", self.current_user_id or "", self.get_conversation_summary()
138
 
139
- # Get response from assistant
140
  try:
141
- response = self.assistant.chat(message)
142
-
143
- # Add response to history
144
- history.append([message, response])
145
 
146
- # Add to conversation history
147
- self.conversation_history.append({"user": message, "assistant": response})
 
 
 
148
 
149
- # Get updated summary
150
- summary = self.get_conversation_summary()
 
151
 
152
- return history, "", self.current_user_id or "", summary
 
 
 
 
 
 
 
 
 
153
 
154
- except Exception as e:
155
- error_msg = f"❌ Error: {str(e)}"
156
  history.append([message, error_msg])
157
- return history, "", self.current_user_id or "", self.get_conversation_summary()
158
-
159
- def get_conversation_summary(self):
160
- """Get formatted conversation summary"""
161
- if not self.assistant:
162
- return "Enter a User ID above to start or resume a conversation!"
163
-
164
- summary = self.assistant.get_conversation_summary()
165
-
166
- summary_text = f"πŸ“‹ **CONVERSATION SUMMARY**\n"
167
- summary_text += f"πŸ‘€ **User ID:** {self.current_user_id or 'Not set'}\n\n"
168
-
169
- # User requirements
170
- reqs = summary["user_requirements"]
171
- summary_text += "**USER REQUIREMENTS:**\n"
172
- if reqs["budget"]:
173
- summary_text += f"β€’ Budget: ${reqs['budget']:,.0f}\n"
174
- if reqs["location"]:
175
- summary_text += f"β€’ Location: {reqs['location']}\n"
176
- if reqs["family_size"]:
177
- summary_text += f"β€’ Family size: {reqs['family_size']}\n"
178
- if reqs["lifestyle_preferences"]:
179
- summary_text += f"β€’ Preferences: {', '.join(reqs['lifestyle_preferences'])}\n"
180
-
181
- # Floorplan requirements
182
- floor_reqs = summary["floorplan_requirements"]
183
- summary_text += "\n**FLOORPLAN REQUIREMENTS:**\n"
184
- if floor_reqs["num_floors"]:
185
- summary_text += f"β€’ Floors: {floor_reqs['num_floors']}\n"
186
- if floor_reqs["total_sqft"]:
187
- summary_text += f"β€’ Total sq ft: {floor_reqs['total_sqft']}\n"
188
- if floor_reqs["lot_shape"]:
189
- summary_text += f"β€’ Lot shape: {floor_reqs['lot_shape']}\n"
190
- if floor_reqs["lot_dimensions"]:
191
- summary_text += f"β€’ Lot dimensions: {floor_reqs['lot_dimensions']}\n"
192
- if floor_reqs["rooms"]:
193
- rooms_str = ", ".join([f"{r['count']}x {r['type']}" for r in floor_reqs["rooms"]])
194
- summary_text += f"β€’ Rooms: {rooms_str}\n"
195
-
196
- # Project progress
197
- agent_memory = self.assistant.state.get("agent_memory", {})
198
- completed_phases = []
199
- if self.assistant.state.get("detailed_floorplan", {}).get("detailed_rooms"):
200
- completed_phases.append("Architectural Design")
201
- if self.assistant.state.get("budget_breakdown", {}).get("total_construction_cost"):
202
- completed_phases.append("Budget Analysis")
203
- if agent_memory.get("structural_analysis"):
204
- completed_phases.append("Structural Analysis")
205
- if agent_memory.get("sustainability"):
206
- completed_phases.append("Sustainability Review")
207
- if agent_memory.get("permits"):
208
- completed_phases.append("Permit Planning")
209
- if agent_memory.get("interior_design"):
210
- completed_phases.append("Interior Design")
211
-
212
- summary_text += f"\n**PROJECT PROGRESS:**\n"
213
- summary_text += f"β€’ Completed: {len(completed_phases)}/6 phases\n"
214
- if completed_phases:
215
- summary_text += f"β€’ Phases: {', '.join(completed_phases)}\n"
216
- summary_text += f"β€’ Total messages: {summary['total_messages']}\n"
217
-
218
- return summary_text
219
-
220
- def get_user_history_display(self, user_id: str):
221
- """Get formatted user history for display"""
222
- if not user_id.strip():
223
- return "Please enter a User ID to view history."
224
-
225
- if not self.state_manager:
226
- return "❌ User history not available - state manager not initialized."
227
-
228
- try:
229
- history = self.state_manager.get_user_history(user_id.strip())
230
-
231
- if not history:
232
- return f"No conversation history found for user: {user_id}"
233
-
234
- display_text = f"πŸ“š **CONVERSATION HISTORY FOR USER: {user_id}**\n\n"
235
-
236
- for i, conv in enumerate(history, 1):
237
- timestamp = conv["timestamp"]
238
- session_id = conv["session_id"]
239
- summary = conv.get("summary", {})
240
-
241
- display_text += f"**#{i} - {timestamp}**\n"
242
- display_text += f"Session ID: `{session_id}`\n"
243
-
244
- if summary.get("total_messages", 0) > 0:
245
- display_text += f"Messages: {summary['total_messages']}\n"
246
-
247
- if summary.get("user_requirements"):
248
- reqs = summary["user_requirements"]
249
- if reqs.get("budget"):
250
- display_text += f"Budget: {reqs['budget']}\n"
251
- if reqs.get("location"):
252
- display_text += f"Location: {reqs['location']}\n"
253
- if reqs.get("family_size"):
254
- display_text += f"Family: {reqs['family_size']} people\n"
255
-
256
- if summary.get("floorplan_status"):
257
- floor_status = summary["floorplan_status"]
258
- if floor_status.get("size"):
259
- display_text += f"House Size: {floor_status['size']}\n"
260
- if floor_status.get("rooms"):
261
- display_text += f"Rooms: {floor_status['rooms']}\n"
262
-
263
- if summary.get("project_progress"):
264
- progress = summary["project_progress"]
265
- completed = progress.get("completed_phases", [])
266
- percentage = progress.get("completion_percentage", 0)
267
- display_text += f"Progress: {percentage}% - {', '.join(completed)}\n"
268
-
269
- display_text += "\n---\n\n"
270
-
271
- return display_text
272
 
273
  except Exception as e:
274
- return f"❌ Error retrieving history: {str(e)}"
275
-
276
- def get_all_users_summary(self):
277
- """Get summary of all users"""
278
- if not self.state_manager:
279
- return "❌ User management not available - state manager not initialized."
280
-
281
- try:
282
- users = self.state_manager.get_all_users()
283
-
284
- if not users:
285
- return "No users found in the system."
286
-
287
- display_text = f"πŸ‘₯ **ALL USERS SUMMARY** ({len(users)} users)\n\n"
288
-
289
- for i, user in enumerate(users, 1):
290
- user_id = user["user_id"]
291
- total_conversations = user["total_conversations"]
292
- last_activity = user["last_activity"]
293
- latest_summary = user.get("latest_summary", {})
294
-
295
- display_text += f"**#{i} User: {user_id}**\n"
296
- display_text += f"Conversations: {total_conversations}\n"
297
- display_text += f"Last Activity: {last_activity}\n"
298
-
299
- if latest_summary.get("project_progress"):
300
- progress = latest_summary["project_progress"]
301
- percentage = progress.get("completion_percentage", 0)
302
- display_text += f"Latest Progress: {percentage}%\n"
303
-
304
- display_text += "\n---\n\n"
305
-
306
- return display_text
307
-
308
- except Exception as e:
309
- return f"❌ Error retrieving users summary: {str(e)}"
310
 
 
 
 
 
311
 
312
- def create_gradio_interface():
313
- """Create and return the final enhanced Gradio interface"""
314
- app = GradioArchitectureApp()
315
-
316
- # Custom CSS for better styling
317
- css = """
318
- .gradio-container {
319
- max-width: 1400px !important;
320
- }
321
- .chat-message {
322
- border-radius: 10px !important;
323
- }
324
- .user-id-input {
325
- background-color: #e3f2fd !important;
326
- font-weight: bold !important;
327
- }
328
- """
329
-
330
- with gr.Blocks(
331
- title="🏠 Architecture Assistant - Multi-User",
332
- theme=gr.themes.Soft(),
333
- css=css
334
- ) as interface:
335
 
 
 
 
336
  gr.HTML("""
337
- <div style="text-align: center; margin-bottom: 20px;">
338
  <h1>🏠 Residential Architecture Assistant</h1>
339
- <p>Multi-user system with automatic conversation loading and precise floorplan specifications</p>
 
340
  </div>
341
  """)
342
 
343
- # API Key input for Hugging Face Spaces
344
- api_key_input = gr.Textbox(
345
- label="πŸ”‘ OpenAI API Key",
346
- placeholder="Enter your OpenAI API key (sk-...)",
347
  type="password",
 
348
  info="Required for AI functionality. Not stored or logged."
349
  )
350
 
351
- # User ID input
352
- with gr.Row():
353
- with gr.Column(scale=4):
354
- user_id_input = gr.Textbox(
355
- placeholder="Enter your User ID (e.g., 'john_smith_house') - automatically loads your conversation history",
356
- label="πŸ‘€ User ID",
357
- elem_classes=["user-id-input"],
358
- interactive=True
359
- )
360
- with gr.Column(scale=1):
361
- load_user_btn = gr.Button("Load User", variant="primary")
362
-
363
- status_display = gr.Markdown(
364
- label="πŸ“Š Status",
365
- value="Enter a User ID above to start or resume your architectural consultation."
366
  )
367
 
368
- # Main interface
369
- with gr.Row():
370
- with gr.Column(scale=2):
371
- chatbot = gr.Chatbot(
372
- label="πŸ’¬ Chat with your Architecture Assistant",
373
- height=500,
374
- show_copy_button=True,
375
- bubble_full_width=False
376
- )
377
-
378
- with gr.Row():
379
- msg = gr.Textbox(
380
- placeholder="Ask about home design, budget, or floorplan planning...",
381
- container=False,
382
- scale=4,
383
- label="Your message"
384
- )
385
- send_btn = gr.Button("Send", variant="primary", scale=1)
386
-
387
- with gr.Row():
388
- clear_btn = gr.Button("πŸ”„ Reset Conversation", variant="secondary")
389
-
390
- gr.HTML("""
391
- <div style="margin-top: 15px; padding: 10px; background-color: #f0f0f0; border-radius: 5px;">
392
- <h4>πŸ’‘ How to use:</h4>
393
- <ul>
394
- <li><strong>New User:</strong> Enter any User ID above and start chatting</li>
395
- <li><strong>Returning User:</strong> Enter your previous User ID - conversation automatically loads</li>
396
- <li><strong>Multiple Users:</strong> Each User ID has completely separate conversations</li>
397
- <li><strong>Floorplans:</strong> Get precise room dimensions for drawing actual plans</li>
398
- </ul>
399
- </div>
400
- """)
401
-
402
- with gr.Column(scale=1):
403
- summary_display = gr.Markdown(
404
- label="πŸ“‹ Current Project Summary",
405
- value="Enter a User ID above to start or resume a conversation!"
406
- )
407
-
408
- gr.HTML("""
409
- <div style="margin-top: 20px; padding: 10px; background-color: #e8f4f8; border-radius: 5px;">
410
- <h4>πŸ€– Available Core Specialists:</h4>
411
- <p><strong>🧭 Router:</strong> Intelligent conversation routing</p>
412
- <p><strong>πŸ›οΈ General Design:</strong> Architecture principles & design guidance</p>
413
- <p><strong>πŸ’° Budget Analysis:</strong> Montreal market costs & feasibility</p>
414
- <p><strong>πŸ“ Floorplan:</strong> Layout planning with exact dimensions</p>
415
- <p><strong>πŸ“‹ Regulation:</strong> Montreal building codes & permit requirements</p>
416
- <br>
417
- </div>
418
- """)
419
-
420
- # Example prompts
421
- with gr.Row():
422
- gr.HTML("<h3>πŸš€ Try these examples:</h3>")
423
 
424
- with gr.Row():
425
- example1 = gr.Button("What are key home design principles?", size="sm")
426
- example2 = gr.Button("I have $800k budget for Montreal - realistic?", size="sm")
427
- example3 = gr.Button("Need 3BR/2BA house, 2500 sqft - help floorplan design", size="sm")
 
428
 
429
  with gr.Row():
430
- example4 = gr.Button("Show me a detailed budget breakdown", size="sm")
431
- example5 = gr.Button("What permits do I need for my house design in Montreal?", size="sm")
432
- example6 = gr.Button("Help me with Montreal building codes", size="sm")
433
 
434
- # History tab
435
- with gr.Accordion("πŸ“š User History & Management", open=False):
436
- with gr.Row():
437
- with gr.Column():
438
- gr.HTML("<h4>πŸ“‹ View User History</h4>")
439
-
440
- with gr.Row():
441
- history_user_id = gr.Textbox(
442
- placeholder="Enter User ID to view history",
443
- label="User ID for History",
444
- scale=3
445
- )
446
- view_history_btn = gr.Button("View History", variant="primary", scale=1)
447
-
448
- user_history_display = gr.Markdown(
449
- label="User History",
450
- value="Enter a User ID to view their conversation history."
451
- )
452
-
453
- with gr.Column():
454
- gr.HTML("<h4>πŸ‘₯ All Users Overview</h4>")
455
-
456
- view_all_btn = gr.Button("View All Users", variant="secondary")
457
-
458
- all_users_display = gr.Markdown(
459
- label="All Users Summary",
460
- value="Click 'View All Users' to see system overview."
461
- )
462
 
463
  # Event handlers
464
- def send_message(message, history, user_id, api_key):
465
- return app.chat_with_assistant(message, history, user_id, api_key)
466
-
467
- def change_user(user_id, api_key):
468
- return app.change_user_id(user_id, api_key)
469
-
470
- def view_user_history(user_id):
471
- return app.get_user_history_display(user_id)
472
-
473
- def view_all_users():
474
- return app.get_all_users_summary()
475
-
476
- # Wire up the events
477
- load_user_btn.click(change_user, [user_id_input, api_key_input], [chatbot, status_display, user_id_input, summary_display])
478
-
479
- msg.submit(send_message, [msg, chatbot, user_id_input, api_key_input], [chatbot, msg, user_id_input, summary_display])
480
- send_btn.click(send_message, [msg, chatbot, user_id_input, api_key_input], [chatbot, msg, user_id_input, summary_display])
481
 
482
- clear_btn.click(lambda: app.assistant.reset_conversation() if app.assistant else None, outputs=[chatbot, summary_display])
483
-
484
- # History management events
485
- view_history_btn.click(view_user_history, [history_user_id], [user_history_display])
486
- view_all_btn.click(view_all_users, outputs=[all_users_display])
487
-
488
- # Example button handlers
489
- example1.click(lambda: "What are key home design principles?", outputs=msg)
490
- example2.click(lambda: "I have $800k budget for Montreal - is that realistic?", outputs=msg)
491
- example3.click(lambda: "I need a 3 bedroom, 2 bathroom house with about 2500 square feet. Can you help me plan the layout?", outputs=msg)
492
- example4.click(lambda: "Can you show me a detailed budget breakdown for my house design?", outputs=msg)
493
- example5.click(lambda: "What permits do I need for my house design in Montreal?", outputs=msg)
494
- example6.click(lambda: "Help me understand Montreal building codes and regulations", outputs=msg)
495
 
496
  return interface
497
 
498
-
499
- def main():
500
- """Launch the enhanced Gradio interface for Hugging Face Spaces"""
501
- try:
502
- interface = create_gradio_interface()
503
- print("🏠 Starting Architecture Assistant for Hugging Face Spaces...")
504
- print("πŸ‘₯ Features: Multi-user support, auto-loading conversations, precise floorplans")
505
- print("πŸ”‘ Users provide their own OpenAI API key via the interface")
506
- print("πŸš€ Ready for Hugging Face Spaces deployment!")
507
-
508
- # Simple launch for HF Spaces
509
- interface.launch()
510
-
511
- except Exception as e:
512
- print(f"❌ Error starting the application: {e}")
513
-
514
-
515
  if __name__ == "__main__":
516
- main()
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ Hugging Face Spaces deployment for Residential Architecture Assistant
4
+ Standalone version that avoids all LangGraph imports at startup
5
  """
6
 
7
  import os
8
  import gradio as gr
 
 
 
 
 
 
9
 
10
+ def create_interface():
11
+ """Create interface that works reliably in HF Spaces"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ def handle_chat(message, history, api_key, user_id=""):
14
+ if not api_key or not api_key.strip():
15
+ error_msg = "❌ Please provide your OpenAI API key to start using the Architecture Assistant."
 
 
 
 
 
 
16
  history.append([message, error_msg])
17
+ return history, ""
 
 
 
 
 
 
 
 
 
18
 
19
+ if not message.strip():
20
+ return history, ""
 
 
 
 
21
 
 
22
  try:
23
+ # Only import when actually needed to avoid schema issues
24
+ from graph import ArchitectureAssistant
 
 
25
 
26
+ # Create assistant instance
27
+ assistant = ArchitectureAssistant(
28
+ openai_api_key=api_key.strip(),
29
+ user_id=user_id.strip() if user_id.strip() else f"hf_user_{len(history)}"
30
+ )
31
 
32
+ # Get response
33
+ response = assistant.chat(message)
34
+ history.append([message, response])
35
 
36
+ except ImportError as e:
37
+ error_msg = f"""❌ **System modules not available**: {str(e)}
38
+
39
+ **This should be the Residential Architecture Assistant** with:
40
+ - πŸ›οΈ Architecture design guidance
41
+ - πŸ’° Montreal market cost analysis
42
+ - πŸ“ Professional floorplan generation
43
+ - πŸ“‹ Building codes & permit requirements
44
+
45
+ The system modules couldn't be loaded in this environment. Please try again or contact support."""
46
 
 
 
47
  history.append([message, error_msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  except Exception as e:
50
+ error_msg = f"""❌ **Error**: {str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ **Troubleshooting:**
53
+ 1. **Check your OpenAI API key** - Ensure it's valid and has credits
54
+ 2. **Try a simpler question** - Start with basic architecture questions
55
+ 3. **Wait and retry** - There might be temporary connectivity issues
56
 
57
+ **Expected functionality:**
58
+ - Multi-agent architecture consultation
59
+ - Montreal-specific building analysis
60
+ - Professional floorplan generation
61
+ - Building code guidance
62
+
63
+ Please try again or contact support if issues persist."""
64
+
65
+ history.append([message, error_msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ return history, ""
68
+
69
+ with gr.Blocks(title="🏠 Architecture Assistant") as interface:
70
  gr.HTML("""
71
+ <div style="text-align: center; padding: 20px;">
72
  <h1>🏠 Residential Architecture Assistant</h1>
73
+ <p><strong>Multi-Agent LangGraph System for Professional Architecture Consultation</strong></p>
74
+ <p>✨ 7 AI Specialists | πŸ“ Professional Floorplans | πŸ’° Montreal Market Analysis | πŸ“‹ Building Codes</p>
75
  </div>
76
  """)
77
 
78
+ # API Key input
79
+ api_key = gr.Textbox(
80
+ label="πŸ”‘ OpenAI API Key",
 
81
  type="password",
82
+ placeholder="Enter your OpenAI API key (sk-...)",
83
  info="Required for AI functionality. Not stored or logged."
84
  )
85
 
86
+ # Optional User ID
87
+ user_id = gr.Textbox(
88
+ label="πŸ‘€ User ID (Optional)",
89
+ placeholder="Enter a unique ID to save your conversation (e.g., 'john_house_project')",
90
+ info="Leave blank for anonymous session"
 
 
 
 
 
 
 
 
 
 
91
  )
92
 
93
+ # Chat interface
94
+ chatbot = gr.Chatbot(
95
+ label="πŸ’¬ Architecture Consultation",
96
+ height=400
97
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ msg = gr.Textbox(
100
+ label="Your Message",
101
+ placeholder="Ask about home design, budgets, floorplans, Montreal building codes...",
102
+ lines=2
103
+ )
104
 
105
  with gr.Row():
106
+ send_btn = gr.Button("πŸ“€ Send", variant="primary")
107
+ clear_btn = gr.Button("πŸ”„ Clear", variant="secondary")
 
108
 
109
+ gr.HTML("""
110
+ <div style="background: #f8f9fa; padding: 15px; margin: 20px 0; border-radius: 8px;">
111
+ <h4>πŸ’‘ Example Questions:</h4>
112
+ <ul style="margin: 10px 0;">
113
+ <li>"I want to design a home but don't know where to start"</li>
114
+ <li>"I have a $800,000 budget for Montreal - is that realistic?"</li>
115
+ <li>"We're a family of 4, what size house do we need?"</li>
116
+ <li>"Can you generate a floorplan for a 2500 sq ft house?"</li>
117
+ <li>"What building permits do I need in Montreal?"</li>
118
+ </ul>
119
+
120
+ <h4>πŸ€– Our AI Specialists:</h4>
121
+ <ul style="margin: 10px 0;">
122
+ <li><strong>RouterAgent:</strong> Intelligent conversation routing</li>
123
+ <li><strong>GeneralDesignAgent:</strong> Architecture principles & design guidance</li>
124
+ <li><strong>BudgetAnalysisAgent:</strong> Montreal market cost analysis</li>
125
+ <li><strong>FloorplanAgent:</strong> Spatial planning & requirements</li>
126
+ <li><strong>FloorplanGeneratorAgent:</strong> Detailed architectural specifications</li>
127
+ <li><strong>DetailedBudgetAgent:</strong> Comprehensive cost breakdowns</li>
128
+ <li><strong>RegulationAgent:</strong> Montreal building codes & permits</li>
129
+ </ul>
130
+ </div>
131
+ """)
 
 
 
 
 
132
 
133
  # Event handlers
134
+ msg.submit(handle_chat, [msg, chatbot, api_key, user_id], [chatbot, msg])
135
+ send_btn.click(handle_chat, [msg, chatbot, api_key, user_id], [chatbot, msg])
136
+ clear_btn.click(lambda: ([], ""), outputs=[chatbot, msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
+ gr.HTML("""
139
+ <div style="text-align: center; padding: 20px; border-top: 1px solid #ddd; margin-top: 20px;">
140
+ <p><strong>🏠 Residential Architecture Assistant v3.0</strong></p>
141
+ <p>Built with <a href="https://langchain.ai/langgraph">LangGraph</a> β€’
142
+ Powered by <a href="https://openai.com">OpenAI</a> β€’
143
+ Interface by <a href="https://gradio.app">Gradio</a></p>
144
+ <p><em>Professional architecture consultation from concept to construction</em></p>
145
+ </div>
146
+ """)
 
 
 
 
147
 
148
  return interface
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  if __name__ == "__main__":
151
+ demo = create_interface()
152
+ demo.launch()