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()