Spaces:
Sleeping
Sleeping
File size: 5,899 Bytes
80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 b2348a1 80d2311 | 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 | import streamlit as st
import pandas as pd
import numpy as np
import torch
import openai
import os
import io
import re
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import matplotlib.pyplot as plt
# --- Groq API setup ---
openai.api_key = os.getenv("GROQ_API_KEY")
openai.api_base = "https://api.groq.com/openai/v1"
GROQ_MODEL = "llama3-8b-8192"
# --- Load Excel ---
@st.cache_data
def load_excel(file):
xl = pd.read_excel(file, sheet_name=None)
all_data = pd.concat(xl.values(), ignore_index=True)
return all_data
# --- Chunk using regex instead of nltk ---
def chunk_data(df, max_tokens=100):
text = "\n".join(df.astype(str).apply(lambda row: " | ".join(row), axis=1))
# Split on period, newline, or semicolon
sentences = re.split(r'(?<=[.;])\s+|\n+', text)
chunks, current_chunk, current_len = [], [], 0
for sent in sentences:
tokens = sent.split()
if current_len + len(tokens) > max_tokens:
chunks.append(" ".join(current_chunk))
current_chunk, current_len = [], 0
current_chunk.append(sent)
current_len += len(tokens)
if current_chunk:
chunks.append(" ".join(current_chunk))
return chunks
# --- Embed chunks ---
@st.cache_resource
def embed_chunks(chunks):
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
embeddings = model.encode(chunks)
return embeddings, model
# --- Query chunks ---
def query_embedding(user_query, chunks, embeddings, model):
query_vec = model.encode([user_query])
similarities = cosine_similarity(query_vec, embeddings)[0]
top_indices = similarities.argsort()[::-1][:5]
top_chunks = "\n\n".join([chunks[i] for i in top_indices])
return top_chunks
# --- Generate Estimate from Groq ---
def generate_estimate(context, user_input):
prompt = f"""You are a construction estimator in Pakistan. Use the following schedule of rates:
{context}
Estimate a full itemized construction BOQ for:
{user_input}
Include all relevant items for a complete house: excavation, foundation, RCC, masonry, plastering, flooring, doors, windows, paint, distemper, fans, lights, wiring, DBs, plumbing, sanitary fittings, water supply, cupboards, wardrobes, gate, etc.
Provide output in a markdown table with columns: Item No, Description, Qty, Unit, Rate, Amount in Rs.
"""
response = openai.ChatCompletion.create(
model=GROQ_MODEL,
messages=[{"role": "user", "content": prompt}]
)
return response['choices'][0]['message']['content']
# --- Quantity Calculator ---
def calculate_quantities(rooms, area, baths, car_porch, living):
return {
"Total Area (sqft)": area,
"No. of Rooms": rooms,
"No. of Bathrooms": baths,
"Living Rooms": living,
"Car Porch Area (est.)": car_porch * 200
}
# --- Floor Plan Sketch ---
def draw_floor_plan(rooms, baths, living, car_porch, area):
total_spaces = rooms + baths + living + car_porch
cols = int(np.ceil(np.sqrt(total_spaces)))
rows = int(np.ceil(total_spaces / cols))
fig, ax = plt.subplots(figsize=(10, 8))
scale = np.sqrt(area) / 10
width, height = scale, scale * 0.75
labels = (["Room"] * rooms + ["Bath"] * baths +
["Living"] * living + ["Car Porch"] * car_porch)
for i, label in enumerate(labels):
row = i // cols
col = i % cols
x = col * width
y = (rows - 1 - row) * height
ax.add_patch(plt.Rectangle((x, y), width, height, edgecolor='black', facecolor='lightblue'))
ax.text(x + width / 2, y + height / 2, label, ha='center', va='center', fontsize=8)
ax.set_xlim(0, cols * width)
ax.set_ylim(0, rows * height)
ax.set_aspect('equal')
ax.set_title(f"Tentative Floor Plan (Scale: 1 unit = {int(scale)} sqft)")
ax.axis('off')
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
return buf
# --- Main App ---
def main():
st.set_page_config(page_title="Construction Estimator", layout="centered")
st.title("🧱 Construction Estimator (RAG + LLaMA 3 + Sketch)")
excel_file = st.file_uploader("Upload Schedule of Rates (.xlsx or .xlsm)", type=["xlsx", "xlsm"])
if excel_file:
df = load_excel(excel_file)
st.success("Excel file loaded successfully.")
chunks = chunk_data(df)
embeddings, model = embed_chunks(chunks)
st.subheader("🏗️ Enter Project Details")
rooms = st.number_input("Number of Rooms", min_value=1, value=3)
area = st.number_input("Total Covered Area (sqft)", min_value=100, value=1200)
baths = st.number_input("Number of Washrooms", min_value=1, value=2)
living = st.number_input("Number of Living Rooms", min_value=0, value=1)
car_porch = st.number_input("Number of Car Porches", min_value=0, value=1)
if st.button("Generate Estimate"):
quantities = calculate_quantities(rooms, area, baths, car_porch, living)
user_query = f"Estimate cost for {rooms} rooms, {baths} bathrooms, {living} living rooms, total area {area} sqft, and {car_porch} car porch(es)."
context = query_embedding(user_query, chunks, embeddings, model)
response = generate_estimate(context, user_query)
st.subheader("📊 Input Quantities")
st.json(quantities)
st.subheader("💸 Estimated Construction Cost (BOQ Style)")
st.markdown(response)
buf = draw_floor_plan(rooms, baths, living, car_porch, area)
st.subheader("🏠 Tentative Floor Plan Sketch")
st.image(buf, caption="Auto-generated Line Plan", use_column_width=True)
st.download_button("📥 Download Sketch", buf, file_name="floor_plan.png", mime="image/png")
if __name__ == "__main__":
main()
|