|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
st.set_page_config( |
|
|
page_title="Tiny Home AI Designer", |
|
|
layout="wide" |
|
|
) |
|
|
|
|
|
|
|
|
genai.configure(api_key=st.secrets["GEMINI_API_KEY"]) |
|
|
REPLICATE_API_TOKEN = st.secrets["REPLICATE_API_TOKEN"] |
|
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
import re |
|
|
|
|
|
st.header("Agent 3: Material Explorer") |
|
|
|
|
|
|
|
|
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 = [] |
|
|
|
|
|
|
|
|
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" |
|
|
} |
|
|
|
|
|
|
|
|
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)_") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|