cfaiassistant / app.py
IAMTFRMZA's picture
Update app.py
30c6dd3 verified
import streamlit as st
from openai import OpenAI
from PIL import Image
import time
import os
import uuid
import firebase_admin
from firebase_admin import credentials, firestore
# πŸ” Firebase setup
if not firebase_admin._apps:
cred = credentials.Certificate("firebase-service-account.json")
firebase_admin.initialize_app(cred)
db = firestore.client()
# πŸ” OpenAI setup
openai_key = os.getenv("openai_key")
assistant_id = os.getenv("ASSISTANT_ID")
client = OpenAI(api_key=openai_key)
# 🌐 Streamlit Config
st.set_page_config(page_title="Carfind.co.za AI Assistant", layout="wide")
# 🎯 Session + User ID
if "user_id" not in st.session_state:
st.session_state["user_id"] = str(uuid.uuid4())
user_id = st.session_state["user_id"]
# πŸ–ΌοΈ Branding + Styling
st.markdown("""
<style>
.block-container {padding-top: 1rem; padding-bottom: 0rem;}
header {visibility: hidden;}
.stChatMessage { max-width: 85%; border-radius: 12px; padding: 8px; margin-bottom: 10px; }
.stChatMessage[data-testid="stChatMessage-user"] { background: #f0f0f0; color: #000000; }
.stChatMessage[data-testid="stChatMessage-assistant"] { background: #D6E9FE; color: #000000; }
@keyframes bounceIn {
0% { transform: scale(0.7); opacity: 0; }
60% { transform: scale(1.1); opacity: 1; }
80% { transform: scale(0.95); }
100% { transform: scale(1); }
}
.carfind-logo {
animation: bounceIn 0.6s ease-out;
}
.car-spec-output {
font-family: "Segoe UI", sans-serif;
font-size: 13px;
background-color: #ffffff;
color: #003B6F;
padding: 20px;
border-radius: 10px;
border-left: 5px solid #0071BC;
line-height: 1.6;
}
</style>
""", unsafe_allow_html=True)
st.markdown("""
<div style='text-align: center; margin-top: 20px; margin-bottom: -10px;'>
<span style='display: inline-flex; align-items: center; gap: 8px;'>
<img src='https://www.carfind.co.za/images/Carfind-Icon.svg' width='28' class='carfind-logo'/>
<span style='font-size: 12px; color: gray;'>Powered by Carfind</span>
</span>
</div>
""", unsafe_allow_html=True)
def get_or_create_thread_id():
doc_ref = db.collection("users").document(user_id)
doc = doc_ref.get()
if doc.exists:
return doc.to_dict()["thread_id"]
else:
thread = client.beta.threads.create()
doc_ref.set({"thread_id": thread.id, "created_at": firestore.SERVER_TIMESTAMP})
return thread.id
def save_message(role, content):
db.collection("users").document(user_id).collection("messages").add({
"role": role,
"content": content,
"timestamp": firestore.SERVER_TIMESTAMP
})
def display_chat_history():
messages = db.collection("users").document(user_id).collection("messages").order_by("timestamp").stream()
assistant_icon_html = "<img src='https://www.carfind.co.za/images/Carfind-Icon.svg' width='20' style='vertical-align:middle;'/>"
for msg in list(messages)[::-1]:
data = msg.to_dict()
if data["role"] == "user":
st.markdown(f"<div class='stChatMessage' data-testid='stChatMessage-user'>πŸ‘€ <strong>You:</strong> {data['content']}</div>", unsafe_allow_html=True)
else:
st.markdown(f"<div class='stChatMessage' data-testid='stChatMessage-assistant'>{assistant_icon_html} <strong>Carfind Assistant:</strong> {data['content']}</div>", unsafe_allow_html=True)
# Tabs
tab1, tab2 = st.tabs(["AI Chat", "What car is that?"])
with tab1:
input_col, clear_col = st.columns([9, 1])
with input_col:
user_input = st.chat_input("Type your message here...")
with clear_col:
if st.button("πŸ—‘οΈ", key="clear-chat", help="Clear Chat"):
try:
user_doc_ref = db.collection("users").document(user_id)
for msg in user_doc_ref.collection("messages").stream():
msg.reference.delete()
user_doc_ref.delete()
st.session_state.clear()
st.rerun()
except Exception as e:
st.error(f"Failed to clear chat: {e}")
thread_id = get_or_create_thread_id()
display_chat_history()
if user_input:
client.beta.threads.messages.create(thread_id=thread_id, role="user", content=user_input)
save_message("user", user_input)
with st.spinner("Thinking and typing... πŸ’­"):
run = client.beta.threads.runs.create(thread_id=thread_id, assistant_id=assistant_id)
while True:
run_status = client.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run.id)
if run_status.status == "completed":
break
time.sleep(1)
messages_response = client.beta.threads.messages.list(thread_id=thread_id)
latest_response = sorted(messages_response.data, key=lambda x: x.created_at)[-1]
assistant_message = latest_response.content[0].text.value
save_message("assistant", assistant_message)
time.sleep(0.5)
st.rerun()
with tab2:
uploaded_image = st.file_uploader("Upload an image of a car and let Ai identify it for you", type=["jpg", "jpeg", "png"])
if uploaded_image:
col1, col2 = st.columns([1.2, 1.8])
with col1:
image = Image.open(uploaded_image)
st.image(image, caption="Uploaded Image", use_container_width=True)
with col2:
try:
image_thread = client.beta.threads.create()
file_response = client.files.create(file=uploaded_image, purpose="assistants")
client.beta.threads.messages.create(
thread_id=image_thread.id,
role="user",
content=[
{"type": "image_file", "image_file": {"file_id": file_response.id}},
{"type": "text", "text": "Identify this car image and respond using the markdown output format shown in your instructions β€” include all sections (🟒 Identified Vehicle, πŸ“‹ Details, 🚘 Overview, πŸ”Ž Recommended For) without follow-ups or links."}
]
)
run = client.beta.threads.runs.create(thread_id=image_thread.id, assistant_id=assistant_id)
with st.spinner("πŸ” Analyzing image and identifying the car..."):
while True:
run_status = client.beta.threads.runs.retrieve(thread_id=image_thread.id, run_id=run.id)
if run_status.status == "completed":
break
time.sleep(1)
messages = client.beta.threads.messages.list(thread_id=image_thread.id)
assistant_message = messages.data[0].content[0].text.value
st.success("βœ… Identification Complete")
st.markdown(f"<div class='car-spec-output'>{assistant_message}</div>", unsafe_allow_html=True)
except Exception as e:
st.error(f"❌ Error during image analysis: {str(e)}")