File size: 19,356 Bytes
de95d76
901094c
7deb072
de95d76
901094c
de95d76
fca1204
de95d76
901094c
3332b70
de95d76
fca1204
901094c
de95d76
 
 
 
 
 
 
 
 
901094c
de95d76
 
 
 
 
 
 
 
 
 
 
 
901094c
de95d76
 
 
 
 
 
 
 
 
901094c
de95d76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901094c
de95d76
 
 
 
 
 
 
 
 
 
 
 
 
901094c
 
de95d76
c6c83f4
de95d76
 
620920c
de95d76
901094c
 
 
 
 
de95d76
3332b70
 
2d9ca38
901094c
 
2d9ca38
 
 
 
 
 
 
 
 
901094c
 
 
3332b70
7d38fab
901094c
 
3332b70
2d9ca38
3332b70
 
2d9ca38
 
 
 
3332b70
901094c
3332b70
 
901094c
 
3332b70
 
 
 
 
901094c
d8b2c18
3332b70
 
 
 
 
901094c
3332b70
 
 
 
 
 
 
901094c
 
3332b70
 
 
 
 
 
 
 
 
 
901094c
 
3332b70
 
 
 
 
 
 
 
901094c
3332b70
 
 
 
fca1204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d763dc5
de95d76
 
9261a71
 
ecb7969
 
71b4fa4
b2607d3
ee37240
187bbc5
7d38fab
ef846b5
2d6bb38
35731b4
8b629c2
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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# === IMPORTS ===

import streamlit as st
from PIL import Image
from openai import OpenAI
from pathlib import Path


# βœ… 1. Page Configuration β€” MUST be first Streamlit command
st.set_page_config(page_title="AI Architecture Assistant", layout="centered")


# βœ… 2. Inline custom CSS with Poppins font and olive green theme
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;500;700&display=swap');
.stApp {
    background-color: #f4f7f5 !important;
    font-family: 'Poppins', sans-serif;
    color: #1e1e1e;
    padding: 1rem;
}
/* Headings */
h1 {
    font-family: 'Poppins', sans-serif;
    color: #3a5a40 !important;
    font-weight: 700;
    font-size: 2.3rem;
}
h2, h3 {
    font-family: 'Poppins', sans-serif;
    color: #344e41 !important;
    font-weight: 600;
    font-size: 1.4rem;
}
/* Inputs */
.stTextInput input, .stTextArea textarea {
    background-color: #ffffff !important;
    color: #1e1e1e !important;
    border: 1px solid #c8d6c1;
    border-radius: 10px;
    padding: 14px;
    font-size: 1rem;
    font-family: 'Poppins', sans-serif;
}
/* Buttons */
.stButton > button {
    background-color: #588157;
    color: white;
    font-weight: 600;
    font-family: 'Poppins', sans-serif;
    padding: 0.7rem 1.6rem;
    font-size: 1rem;
    border: none;
    border-radius: 10px;
    transition: all 0.3s ease;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.stButton > button:hover {
    background-color: #3a5a40;
    box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
/* Links */
a {
    color: #3a5a40;
    font-family: 'Poppins', sans-serif;
    text-decoration: none;
    font-weight: 500;
}
a:hover {
    text-decoration: underline;
    color: #2c4030;
}
</style>
""", unsafe_allow_html=True)


# βœ… 3. Logo Display (must come after styling)
try:
    logo = Image.open("ThinkTiny.jpg")
    st.image(logo, width=300)
except FileNotFoundError:
    st.warning("Logo not found. Please ensure 'ThinkTiny.jpg' is in the same folder as app.py.")


# βœ… 4. Connect to OpenAI
client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])


# βœ… 5. Session Setup
if "prompt" not in st.session_state:
    st.session_state.prompt = ""


# βœ… 6. Sample Prompt
sample_prompt = (
    "A highly detailed, realistic architectural floor plan for a modern tiny home. "
    "The design features an open-concept layout with a multi-functional living space "
    "that combines a compact living area, dining space, and efficient kitchen with smart storage. "
    "A cozy loft bedroom is accessible via a sleek staircase or ladder. The minimalist bathroom "
    "includes a shower. Emphasize large windows with natural light, clean lines, and neutral tones "
    "with subtle accents. Annotate key dimensions and furniture placements. Professional architectural rendering style."
)


# === UI START ===
st.title("🏠 AI Architecture Assistant")
st.caption("Design smarter. Visualize faster. Build better.")

# === 1. Prompt Input ===
st.markdown("### ✍️ Enter Your Floor Plan Prompt")
st.text_area("Prompt", key="prompt_input", value=st.session_state.prompt, height=200)

col1, col2 = st.columns(2)
with col1:
    if st.button("Insert Sample Prompt"):
        st.session_state.prompt = sample_prompt
        st.rerun()

st.session_state.prompt = st.session_state.prompt_input

st.text_area("Current Prompt Being Used", st.session_state.prompt, height=200, disabled=True)

# === 2. Generate Image ===
st.markdown("### πŸ–ΌοΈ Generate a Floor Plan Image")

if st.button("Generate Image"):
    if st.session_state.prompt.strip():
        with st.spinner("Generating image..."):
            try:
                response = client.images.generate(
                    model="dall-e-3",
                    prompt=st.session_state.prompt,
                    size="1024x1024",
                    quality="standard",
                    n=1
                )
                image_url = response.data[0].url
                st.image(image_url, caption="Generated Floor Plan", use_column_width=True)
                st.markdown(f"[Download Image]({image_url})", unsafe_allow_html=True)
            except Exception as e:
                st.error(f"Error: {e}")
    else:
        st.warning("Please enter a valid prompt.")

# === 3. Recommend Materials ===
st.markdown("### 🧱 Recommended Building Materials")

if st.button("Get Material Recommendations"):
    if st.session_state.prompt.strip():
        with st.spinner("Analyzing design and recommending materials..."):
            def generate_material_recommendations(prompt):
                system_instruction = (
                    "You are a smart building material recommender. Based on the architectural prompt, "
                    "return a list of 5 to 10 building materials implied by the layout or concept. "
                    "Respond in clear, plain English using bullet points. Do not return JSON."
                )

                response = client.chat.completions.create(
                    model="gpt-4",
                    messages=[
                        {"role": "system", "content": system_instruction},
                        {"role": "user", "content": prompt}
                    ],
                    temperature=0.7
                )
                return response.choices[0].message.content.strip()

            result = generate_material_recommendations(st.session_state.prompt)
            st.markdown(result)
    else:
        st.warning("Please enter a prompt first.")
st.markdown("### πŸ’° Cost Estimate by Region")

region = st.selectbox("Select a region in Canada:", [
    "Alberta", "British Columbia", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
])

if st.button("Get Cost Estimate"):
    if st.session_state.prompt.strip():
        with st.spinner("Calculating cost estimate..."):
            def generate_cost_estimate(prompt, region):
                system_instruction = (
                    "You are a residential construction cost estimator for Canadian regions. "
                    "Based on the given architectural prompt and selected region, estimate a typical low-to-high cost range "
                    "for building the described design. Mention 4–5 key cost categories. Use Canadian dollars. "
                    "Adjust prices to be realistic for the selected region, and keep your response brief and clear."
                )

                user_input = f"Region: {region}\n\nPrompt:\n{prompt}"

                response = client.chat.completions.create(
                    model="gpt-4",
                    messages=[
                        {"role": "system", "content": system_instruction},
                        {"role": "user", "content": user_input}
                    ],
                    temperature=0.7
                )
                return response.choices[0].message.content.strip()

            estimate = generate_cost_estimate(st.session_state.prompt, region)
            st.markdown(estimate)
    else:
        st.warning("Please enter a prompt first.")
        st.markdown("### πŸ—οΈ Building Code Comparison")

st.markdown("---")
st.markdown("### 🧾 Building Code Comparison Between Provinces")

province_1 = st.selectbox("Select first province:", [
    "British Columbia", "Alberta", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
], key="prov1")

province_2 = st.selectbox("Select second province:", [
    "British Columbia", "Alberta", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
], key="prov2")

if st.button("Compare Building Codes"):
    if province_1 != province_2:
        with st.spinner("Comparing codes across provinces..."):
            def compare_building_codes(p1, p2, prompt):
                system_instruction = (
                    "You are a Canadian building code expert. Compare the key differences in building code requirements "
                    f"between {p1} and {p2} for residential construction, especially related to small or modern homes like the one described below. "
                    "Focus on differences in energy efficiency, foundations, insulation, accessibility, and design limitations. "
                    "Keep your response clear and under 10 bullet points."
                )

                response = client.chat.completions.create(
                    model="gpt-4",
                    messages=[
                        {"role": "system", "content": system_instruction},
                        {"role": "user", "content": prompt}
                    ],
                    temperature=0.7
                )
                return response.choices[0].message.content.strip()

            comparison = compare_building_codes(province_1, province_2, st.session_state.prompt)
            st.markdown(f"### πŸ›οΈ {province_1} vs. {province_2}")
            st.markdown(comparison)
    else:
        st.warning("Please select two different provinces.")
        st.markdown("### ➑️ What would you like to do next?")

# === DESIGN IDEAS SECTION ===

st.markdown("### ➑️ What would you like to do next?")

next_step = st.selectbox(
    "Now that you have some information and a layout in mind, would you like help with...",
    [
        "Select an option",
        "🎨 Explore design ideas (color palettes, materials for your climate)",
        "πŸ” Continue researching (codes, build types, regulations)"
    ]
)

if next_step == "🎨 Explore design ideas (color palettes, materials for your climate)":
    st.markdown("#### 🎨 Design Recommendations")
    st.markdown("We'll give you smart and stylish ideas based on your region and layout.")

    # Select region
    design_region = st.selectbox("Where will your home be built?", [
        "British Columbia", "Alberta", "Ontario", "Quebec", "Atlantic Canada", "Prairies"
    ], key="design_region")

    # Select style
    design_style = st.selectbox("What style do you like?", [
        "Scandinavian", "Modern Minimalist", "Rustic Cabin", "Japandi", "West Coast Contemporary"
    ], key="design_style")

    # Select design advice categories
    selected_categories = st.multiselect(
        "What kind of design advice would you like?",
        [
            "Climate-Responsive Material Suggestions",
            "Interior Color Palette",
            "Lighting & Window Placement Tips",
            "Space-Saving Built-ins"
        ],
        default=["Climate-Responsive Material Suggestions", "Interior Color Palette"]
    )

# Generate design ideas with GPT
if st.button("Generate Design Ideas"):
        with st.spinner("Designing your dream space..."):

            def generate_design_ideas(prompt, region, style, categories):
                system_instruction = (
                    "You are a residential design assistant. Based on the home description, region, and preferred design style, "
                    "give tailored recommendations for the selected categories. Keep it concise, stylish, and easy to implement for a small-scale home."
                )

                user_input = (
                    f"Prompt:\n{prompt}\n\n"
                    f"Region: {region}\n"
                    f"Style: {style}\n"
                    f"Categories: {', '.join(categories)}"
                )

                response = client.chat.completions.create(
                    model="gpt-4",
                    messages=[
                        {"role": "system", "content": system_instruction},
                        {"role": "user", "content": user_input}
                    ],
                    temperature=0.7
                )
                return response.choices[0].message.content.strip()

            design_results = generate_design_ideas(
                st.session_state.prompt,
                design_region,
                design_style,
                selected_categories
            )

            st.markdown("### πŸ’‘ Design Suggestions")
            st.markdown(design_results)

            # Store summary in session state
            st.session_state.design_summary = {
                "Prompt": st.session_state.prompt,
                "Region": design_region,
                "Style": design_style,
                "Categories": selected_categories,
                "Design Suggestions": design_results,
                "Materials": st.session_state.get("material_recommendations", "Not generated"),
                "Image URL": st.session_state.get("generated_image_url", "Not generated")
            }

# === Download Summary ===
import io

st.markdown("---")
st.markdown("### πŸ“₯ Download Your Concept Summary")

if st.button("Download Summary as Text"):
    summary = st.session_state.design_summary
    content = f"""🏠 AI Architecture Design Summary
Prompt:
{summary['Prompt']}
Region: {summary['Region']}
Style: {summary['Style']}
Selected Categories: {', '.join(summary['Categories'])}
🎨 Design Suggestions:
{summary['Design Suggestions']}
🧱 Recommended Materials:
{summary['Materials']}
πŸ–ΌοΈ Floor Plan Image:
{summary['Image URL']}
"""

    st.download_button(
        label="Download Summary",
        data=io.StringIO(content),
        file_name="design_summary.txt",
        mime="text/plain"
    )

# === Concept Image Generation ===
st.markdown("### πŸ–ΌοΈ Visualizing Your Design Concept")

if st.button("Generate Concept Image from Design Summary"):
    with st.spinner("Creating a personalized visual..."):
        refined_prompt = (
    f"Based on the following tiny home project details:\n\n"
    f"Region: {design_region}\n"
    f"Style: {design_style}\n"
    f"Categories: {', '.join(selected_categories)}\n"
    f"Design Suggestions: {st.session_state.design_summary['Design Suggestions']}\n\n"
    "Create a professional architectural rendering that visually reflects this concept.\n"
    "Focus on realistic materials, lighting, and color palette. Use a 3D rendering style with natural surroundings."
)


        try:
            response = client.images.generate(
                model="dall-e-3",
                prompt=refined_prompt,
                size="1024x1024",
                quality="standard",
                n=1
            )
            concept_image_url = response.data[0].url
            st.image(concept_image_url, caption="Generated Concept Image", use_column_width=True)
            st.markdown(f"[Download Image]({concept_image_url})", unsafe_allow_html=True)

            st.session_state.generated_concept_image_url = concept_image_url

        except Exception as e:
            st.error(f"Image generation failed: {e}")
            
if next_step == "πŸ” Continue researching (codes, build types, regulations)":
    st.markdown("#### πŸ” Research Hub")
    st.info("Here are some useful topics and tools to help you dig deeper before you build.")

    st.markdown("##### 🧠 Suggested Topics to Explore")
    research_topics = {
        "Prefab vs. Traditional Construction": "Compare pros and cons of prefabricated (modular) vs. traditional stick-built homes.",
        "Land Use & Zoning": "Understand how land use bylaws and zoning affect what and where you can build.",
        "Permit & Code Requirements": "Find out what permits are typically needed for a small home project in Canada.",
        "Sustainable Building Materials": "Explore environmentally-friendly materials that are affordable and effective.",
        "Off-Grid Living Regulations": "Learn the legal considerations if you want to live off-grid.",
        "Energy Efficiency Standards": "Understand how to meet or exceed Canadian energy code requirements."
    }

    selected_topic = st.selectbox("Choose a topic to learn more:", ["Select a topic"] + list(research_topics.keys()))
    if selected_topic != "Select a topic":
        with st.spinner("Researching..."):
            response = client.chat.completions.create(
                model="gpt-4",
                messages=[
                    {"role": "system", "content": "You are a Canadian construction research assistant."},
                    {"role": "user", "content": research_topics[selected_topic]}
                ],
                temperature=0.7
            )
            st.markdown(f"### πŸ“– {selected_topic}")
            st.markdown(response.choices[0].message.content.strip())

    st.markdown("##### πŸ’¬ Ask Your Own Research Question")
user_question = st.text_input("What else would you like to learn about?")

if st.button("Ask"):
    if user_question:
        with st.spinner("Getting the answer..."):
            response = client.chat.completions.create(
                model="gpt-4",
                messages=[
                    {"role": "system", "content": "You're a helpful Canadian homebuilding advisor. Keep answers clear and based on current standards."},
                    {"role": "user", "content": user_question}
                ],
                temperature=0.7
            )
            st.markdown("### πŸ’‘ Answer")
            st.markdown(response.choices[0].message.content.strip())
    else:
        st.warning("Please type a question first.")

# === Helpful Resource Links (shown no matter what) ===
st.markdown("##### πŸ”— Helpful Resources")
st.markdown("""
- [National Building Code of Canada (2020)](https://nrc.canada.ca/en/certifications-evaluations-standards/codes-canada/codes-canada-publications/national-building-code-canada-2020)
- [BC Building Code](https://www.bccodes.ca/)
- [Alberta Building Codes & Standards](https://www.alberta.ca/building-codes-and-standards.aspx)
- [Ontario Building Code](https://www.ontario.ca/page/building-code)
- [Quebec Construction Code](https://www.rbq.gouv.qc.ca/en/technical-buildings/buildings/construction-code.html)
- [Tiny Home Legal Info (Canada)](https://tinyhousecanada.ca/)
""")
# === 9. Eco-Friendly Upgrades Agent ===

st.markdown("---")
st.markdown("### 🌱 Suggest Eco-Friendly Upgrades")

if st.button("Get Eco-Friendly Suggestions"):
    if st.session_state.prompt.strip():
        with st.spinner("Analyzing your layout for green upgrades..."):

            def suggest_eco_upgrades(prompt):
                system_instruction = (
                    "You are a sustainability expert in residential architecture. "
                    "Based on the following home design prompt, suggest 5 to 7 eco-friendly upgrades. "
                    "Be specific and include options like energy efficiency, water conservation, material choices, and renewable energy use. "
                    "Avoid general adviceβ€”tailor suggestions to the design and layout described."
                )

                response = client.chat.completions.create(
                    model="gpt-4",
                    messages=[
                        {"role": "system", "content": system_instruction},
                        {"role": "user", "content": prompt}
                    ],
                    temperature=0.7
                )

                return response.choices[0].message.content.strip()

            eco_suggestions = suggest_eco_upgrades(st.session_state.prompt)

            st.markdown("### βœ… Recommended Eco-Friendly Features")
            st.markdown(eco_suggestions)

            # Save to session for summary
            st.session_state.eco_suggestions = eco_suggestions

    else:
        st.warning("Please enter a prompt first.")