# === 🧠 Tiny Home AI Designer === # === Required Imports === import streamlit as st import google.generativeai as genai import replicate import requests from PIL import Image from io import BytesIO import json import os # === Page Setup === st.set_page_config( page_title="Tiny Home AI Designer", layout="wide" ) # === API Key Config === genai.configure(api_key=st.secrets["GEMINI_API_KEY"]) REPLICATE_API_TOKEN = st.secrets["REPLICATE_API_TOKEN"] # === Agent 1: Requirement Gatherer (Gemini) === st.header("Agent 1: Describe Your Dream Tiny Home") user_input = st.text_area( "Tell us about your dream tiny home (location, style, size, climate, etc.):" ) if st.button("Extract Requirements"): with st.spinner("Analyzing your description with Gemini..."): agent1_prompt = f""" You're a professional architectural planner and tiny home designer. Your task is to produce an **extremely detailed** tiny home plan based on the user concept below. Respond in a structured, rich layout description that includes: - Overall dimensions - Exterior materials, foundation, roof type, and regional considerations - Complete room-by-room interior layout (dimensions, fixtures, furniture ideas) - Storage features and off-grid systems (water, solar, composting toilet) - Special design considerations for British Columbia's rainy climate - Rustic style interior and material suggestions - Regulatory and building code notes for BC - Any labeled floorplan sketch ideas or text representations (ASCII-style ok) - Bonus tips for maximizing space and sustainability Be verbose, descriptive, and specific. User concept: \"\"\"{user_input}\"\"\" """ try: model = genai.GenerativeModel("gemini-2.0-flash-thinking-exp-01-21") detailed_prompt = f""" You're an expert tiny home layout assistant. Generate a rich, detailed layout plan description for this concept: "{user_input}" Your response should include: - Square footage - Style (e.g. rustic, modern, off-grid) - Climate considerations - Room types (kitchen, bathroom, sleeping area, etc.) - Any special features - Materials or design elements if relevant Respond in clear, natural English. No bullet points. """ response = model.generate_content(detailed_prompt) summary = response.text.strip() st.subheader("📋 Detailed Layout Summary") st.write(summary) st.session_state.requirements_summary = summary except Exception as e: st.error(f"❌ Gemini failed: {e}") # === Agent 2: Generate Layout Image (Replicate + Gemini prompt) === if "requirements_summary" in st.session_state: st.header("Agent 2: Generate Tiny Home Layout (Image)") if st.button("Generate Layout Image"): with st.spinner("Creating blueprint using Replicate SDXL Lightning..."): layout_description = st.session_state.requirements_summary image_prompt = ( layout_description.strip() + "\n\nTop-down architectural blueprint layout, labeled rooms, clean black lines, technical drawing, white background" ) try: output = replicate.run( "bytedance/sdxl-lightning-4step:6f7a773af6fc3e8de9d5a3c00be77c17308914bf67772726aff83496ba1e3bbe", input={"prompt": image_prompt}, api_token=REPLICATE_API_TOKEN ) if isinstance(output, list) and output: for index, url in enumerate(output): response = requests.get(url) image = Image.open(BytesIO(response.content)) st.image(image, caption="📐 AI-Generated Layout") image.save(f"tiny_home_layout_{index}.png") st.success(f"✅ Saved: tiny_home_layout_{index}.png") st.session_state.generated_image_url = url else: st.error("⚠️ No image returned from Replicate.") except Exception as e: st.error(f"❌ Replicate API failed: {e}") # === Agent 3: Material Explorer === import re st.header("Agent 3: Material Explorer") # Use the previous layout description as input your_dalle_prompt = st.session_state.get("requirements_summary", "") if st.button("Get Suggested Materials"): with st.spinner("Using Gemini to extract materials..."): material_prompt = f""" From the following blueprint prompt and layout concept: Blueprint Prompt: \"\"\"{your_dalle_prompt}\"\"\" Return 5 key building materials implied by the layout, even if not explicitly listed. Respond ONLY with a raw JSON list like: ["Concrete slab foundation", "Metal roofing", "Plywood sheathing", "Double-pane windows", "Insulation"] """ try: model = genai.GenerativeModel("gemini-1.5-pro") response = model.generate_content(material_prompt) raw = response.text.strip() cleaned = re.sub(r"```json|```", "", raw).strip() materials = json.loads(cleaned) st.session_state.materials = materials st.success("✅ Materials extracted from layout!") except Exception as e: st.error("❌ Could not parse Gemini response.") st.code(raw) materials = [] # Predefined Home Depot product links material_links = { "Engineered wood flooring": "https://www.homedepot.ca/product/home-decorators-collection-laminate-flooring-castle-oak/1001624267", "Laminate countertops": "https://www.homedepot.ca/product/hampton-bay-premium-laminate-countertop-butchers-block/1001185104", "Tile (bathroom)": "https://www.homedepot.ca/product/enigma-12-inch-x-24-inch-glazed-porcelain-tile-aria-grey/1001066513", "Painted drywall": "https://www.homedepot.ca/product/certainteed-1-2-inch-x-4-ft-x-8-ft-drywall/1000116648", "Wood framing (stud walls)": "https://www.homedepot.ca/product/2-inch-x-4-inch-x-8-ft-construction-grade-lumber/1000115194", "Spray foam insulation": "https://www.homedepot.ca/product/great-stuff-big-gap-filler-insulating-foam-sealant-12-oz/1000110899", "Triple-glazed windows": "https://www.homedepot.ca/product/american-craftsman-60-inch-x-60-inch-double-pane-double-hung-vinyl-window/1001202871", "Metal roofing": "https://www.homedepot.ca/product/ondura-36-inch-x-79-inch-black-roof-panel/1000167679", "Solar panel kit": "https://www.homedepot.ca/product/nature-power-110-watt-solar-panel-kit-with-inverter-and-charging-controller/1001037002", "Mini wood stove": "https://www.homedepot.ca/product/englander-1-200-sq-ft-wood-burning-stove/1000829230" } # Show links in Streamlit if "materials" in st.session_state: st.subheader("🔗 Explore Suggested Materials") cols = st.columns(2) for i, mat in enumerate(st.session_state.materials): col = cols[i % 2] with col: url = material_links.get(mat) if url: st.markdown(f"✅ **{mat}**\n\n[🛒 View Product on Home Depot]({url})", unsafe_allow_html=True) else: st.markdown(f"🟡 **{mat}**\n\n_(no direct link available)_")