TinyHome / app.py
Karley8's picture
Update app.py
2e638b9 verified
# === 🧠 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)_")