SparkMart / app.py
Tamannathakur's picture
Update app.py
c7dbc2e verified
import gradio as gr
from supervisor_agent import supervisor_agent
from database import engine
from sqlalchemy import text
import uuid
import pandas as pd
import os
from supabase import create_client
from dotenv import load_dotenv
import base64
from langchain_groq import ChatGroq
load_dotenv()
supabase = create_client(os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_KEY"))
# Initialize LLM for evidence detection
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
if not GROQ_API_KEY:
raise ValueError(
"GROQ_API_KEY environment variable is not set. "
"Please set it in your environment variables."
)
evidence_detector_llm = ChatGroq(
model="moonshotai/kimi-k2-instruct-0905",
api_key=GROQ_API_KEY,
temperature=0.3
)
# Handle logo loading safely
try:
logo_path = os.path.join(os.getcwd(), "ic_logo.png")
def embed_image_base64(path):
with open(path, "rb") as f:
return "data:image/png;base64," + base64.b64encode(f.read()).decode()
logo_b64 = embed_image_base64(logo_path)
except:
# Fallback if logo file doesn't exist
logo_b64 = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDEyMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiByeD0iMTAiIGZpbGw9IiM2NjdlZWEiLz4KPHRleHQgeD0iNjAiIHk9IjcwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSJ3aGl0ZSIgZm9udC1zaXplPSIyNCIgZm9udC13ZWlnaHQ9ImJvbGQiPkU8L3RleHQ+Cjwvc3ZnPgo="
def upload_file_to_supabase_gradio(file_path: str, order_id: str):
"""
Uploads a file to Supabase storage from a file path (Gradio format)
Returns the public URL or None if upload fails
"""
if not file_path:
return None
try:
with open(file_path, 'rb') as f:
file_bytes = f.read()
file_ext = os.path.splitext(file_path)[1].lstrip('.')
if not file_ext:
file_ext = 'bin'
timestamp = str(uuid.uuid4().hex)
storage_path = f"{order_id}_{timestamp}.{file_ext}"
res = supabase.storage.from_("complaints").upload(storage_path, file_bytes)
if isinstance(res, dict) and res.get("error"):
print(f"Supabase upload error: {res.get('error')}")
return None
public_url = supabase.storage.from_("complaints").get_public_url(storage_path)
print(f"File uploaded successfully: {public_url}")
return public_url
except Exception as e:
print(f"Error uploading file to Supabase: {e}")
import traceback
traceback.print_exc()
return None
def check_if_evidence_requested(bot_response: str) -> bool:
"""
Use LLM to intelligently detect if the bot is asking for file/evidence upload.
Returns True if evidence is requested, False otherwise.
"""
try:
prompt = f"""You are an assistant that determines if a customer service response is asking the user to upload files, photos, videos, or any evidence.
Analyze the following bot response and determine if it is EXPLICITLY asking the user to upload or attach files/photos/videos/evidence.
Bot Response: "{bot_response}"
Rules:
- Only respond with "true" if the bot is CLEARLY asking for file uploads, photos, videos, or evidence
- Respond with "false" if the bot is just asking questions, providing information, or not requesting any files
- Keywords to look for: upload, attach, file, photo, video, evidence, picture, image, send, provide (when related to files)
- Do NOT say "true" for general questions about the issue
Respond with ONLY "true" or "false" - nothing else."""
response = evidence_detector_llm.invoke(prompt)
result = response.content.strip().lower()
print(f"🤖 LLM Evidence Detection - Bot said: '{bot_response[:100]}...'")
print(f"🤖 LLM Response: '{result}'")
# Return True only if LLM explicitly says "true"
return result == "true"
except Exception as e:
print(f"❌ Error in LLM evidence detection: {e}")
# Fallback to False (don't show upload button if LLM fails)
return False
def chat_with_agent(message, file, session_id):
"""Main chat function"""
try:
if not session_id:
session_id = int(uuid.uuid4().int % 1000000)
session_id = int(session_id)
file_url = None
if file is not None:
print(f"📎 File received: {file}")
print(f"📎 File type: {type(file)}")
try:
file_url = upload_file_to_supabase_gradio(
file,
order_id=str(session_id)
)
if file_url:
print(f"✅ File uploaded: {file_url}")
else:
print("⚠️ File upload failed, continuing without file")
except Exception as e:
# File upload should not crash the conversation
print(f"❌ File upload error: {e}")
return (
"The file could not be uploaded due to a processing error. "
"Your request was received, but the file was not saved.",
session_id,
gr.update(visible=False)
)
supervisor_input = f"Session ID: {session_id} | User Query: {message}"
if file_url:
supervisor_input += f" | FileURL: {file_url}"
print(f"\n📨 Sending to supervisor: {supervisor_input}\n")
try:
result = supervisor_agent.invoke(
{"messages": [{"role": "user", "content": supervisor_input}]},
{"configurable": {"thread_id": str(session_id)}},
)
bot_response = result["messages"][-1].content
# USE LLM TO INTELLIGENTLY DETECT IF EVIDENCE/FILE UPLOAD IS REQUESTED
should_show_upload = check_if_evidence_requested(bot_response)
print(f"👁 Upload button visibility: {should_show_upload}")
return (
bot_response,
session_id,
gr.update(visible=should_show_upload)
)
except Exception as e:
# Dedicated LLM failure response
print(f"❌ LLM processing error: {e}")
import traceback
traceback.print_exc()
return (
"The current model is not responding reliably at the moment. "
"Please try again shortly or contact support if the issue continues.",
session_id,
gr.update(visible=False)
)
except Exception as e:
# Catch-all to avoid silent crashes
print(f"❌ Unexpected server error: {e}")
import traceback
traceback.print_exc()
return (
"An unexpected internal error occurred while processing your request.",
session_id,
gr.update(visible=False)
)
def check_complaints(order_id):
"""View all complaints for a user"""
try:
if not order_id:
return pd.DataFrame({"Message": ["Please enter a valid Order ID"]})
with engine.connect() as conn:
result = conn.execute(
text("""
SELECT order_id, product_name, complaint_text, complaint_file_url, created_at
FROM orders
WHERE order_id = :order_id AND is_complaint = 1
ORDER BY created_at DESC
"""),
{"order_id": order_id}
)
rows = result.fetchall()
if not rows:
return pd.DataFrame({"Message": ["No complaints found for this Order ID"]})
df = pd.DataFrame(rows, columns=["Order ID", "Product", "Complaint", "Evidence", "Date"])
return df
except Exception as e:
return pd.DataFrame({"Error": [str(e)]})
def upload_catalog(file, table_name):
"""Upload CSV/Excel to database with validation"""
if not file:
return "❌ Please select a file to upload"
try:
file_path = file if isinstance(file, str) else file.name
file_name = os.path.basename(file_path)
# Check file extension
if not (file_path.lower().endswith('.csv') or file_path.lower().endswith(('.xlsx', '.xls'))):
return f"❌ Invalid file type: {file_name}\n\n⚠️ Please select a CSV or Excel file (.csv, .xlsx, .xls)"
# Read file based on extension
if file_path.lower().endswith('.csv'):
df = pd.read_csv(file_path)
elif file_path.lower().endswith(('.xlsx', '.xls')):
df = pd.read_excel(file_path)
# Upload to database
df.to_sql(table_name, con=engine, if_exists="replace", index=False)
return f"✅ Successfully uploaded {len(df)} rows from {file_name} to table '{table_name}'"
except Exception as e:
return f"❌ Upload failed: {str(e)}"
custom_css = """
body {
position: relative;
background-image:
linear-gradient(rgba(59, 130, 246, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(59, 130, 246, 0.03) 1px, transparent 1px);
background-size: 60px 60px;
z-index: 1;
transform: unset !important;
background:
radial-gradient(circle at 20% 15%, rgba(59, 130, 246, 0.12), transparent 40%),
radial-gradient(circle at 80% 25%, rgba(139, 92, 246, 0.1), transparent 45%),
radial-gradient(circle at 50% 70%, rgba(16, 185, 129, 0.08), transparent 50%),
radial-gradient(circle at 10% 85%, rgba(236, 72, 153, 0.08), transparent 45%),
linear-gradient(135deg, #0a0e27 0%, #1a1f3a 25%, #0f1629 50%, #1e2139 75%, #0d1425 100%) !important;
}
gradio-app {
background-color: transparent !important;
}
/* Hexagon pattern overlay */
body::after {
content: '';
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image:
repeating-linear-gradient(45deg, transparent, transparent 25px, rgba(174, 246, 59, 0.015) 35px, rgba(93, 205, 18, 0.015) 70px),
repeating-linear-gradient(-45deg, transparent, transparent 25px, rgba(139, 92, 246, 0.015) 35px, rgba(139, 92, 246, 0.015) 70px);
pointer-events: none;
opacity: 0.6;
}
.header {
padding: 40px 30px;
background:
radial-gradient(circle at 20% 10%, rgba(255, 255, 255, 0.25), transparent 55%),
radial-gradient(circle at 85% 20%, rgba(45, 135, 255, 0.22), transparent 70%),
linear-gradient(135deg, #0e1a2b, #0f1032, #03011a);
color: white;
border-bottom-left-radius: 40px;
border-bottom-right-radius: 40px;
box-shadow: 0 50px 90px -20px rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
width: 100%;
}
.header-logo {
flex: unset;
display: flex;
justify-content: flex-start;
width: 100px;
}
.header h1 {
margin: 0;
flex: 1;
text-align: center;
font-size: 2.5em;
font-weight: 800;
letter-spacing: 1px;
background: linear-gradient(to right, #fff 20%, #dce4ff 40%, #cedcff 60%);
-webkit-background-clip: text;
color: transparent;
}
/* Tab headers with gradient */
.tabs button {
background:
radial-gradient(circle at 20% 10%, rgba(255, 255, 255, 0.25), transparent 55%),
radial-gradient(circle at 85% 20%, rgba(45, 135, 255, 0.22), transparent 70%),
linear-gradient(35deg, #0e1a2b, #0f1032, #03011a);
color: white !important;
font-weight: 600 !important;
font-size: 1.1em !important;
padding: 8px 24px !important;
border-radius: 8px 8px 0 0 !important;
border: none !important;
transition: all 0.3s ease !important;
}
.tabs button:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important;
transform: translateY(-2px) !important;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
}
.tabs button[aria-selected="true"] {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.5) !important;
}
.tabs button .label-text {
width: 100%;
max-width: 200px;
}
/* Target all buttons with variant="primary" */
button[variant="primary"] {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
color: white !important;
font-weight: 600 !important;
border-radius: 8px !important;
transition: all 0.3s ease !important;
}
button[variant="primary"]:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important;
transform: translateY(-2px) !important;
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.5) !important;
}
/* Backup selectors for buttons */
.primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
color: white !important;
}
.primary:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important;
}
/* ========== NEW: CENTERED FILE UPLOAD BUTTON ========== */
/* Container for centered file button */
.centered-file-container {
display: flex !important;
justify-content: center !important;
align-items: center !important;
width: 100% !important;
margin: 15px 0 !important;
padding: 0 !important;
}
/* Style the file upload button itself */
#evidence-upload {
max-width: 200px !important;
min-width: 180px !important;
margin: 0 auto !important;
display: flex !important;
justify-content: center !important;
}
#evidence-upload button {
font-size: 0.9em !important;
padding: 10px 20px !important;
white-space: nowrap !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
border: none !important;
border-radius: 8px !important;
font-weight: 600 !important;
transition: all 0.3s ease !important;
cursor: pointer !important;
}
#evidence-upload button:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important;
transform: translateY(-2px) !important;
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.5) !important;
}
#evidence-upload .wrap {
display: flex !important;
justify-content: center !important;
}
/* ========== END CENTERED FILE UPLOAD BUTTON ========== */
.compact-action-row {
display: flex;
justify-content: center !important;
gap: 12px !important;
margin-top: 10px;
}
.compact-action-row button {
min-width: 120px !important;
height: 42px !important;
font-size: 0.9rem !important;
border-radius: 10px !important;
font-weight: 600 !important;
}
/* Upload button center alignment */
.upload-section {
display: flex;
flex-direction: column;
align-items: center !important;
}
.upload-btn {
min-width: 165px !important;
height: 45px !important;
color: white;
font-size: 0.9rem !important;
border-radius: 12px !important;
}
/* Hover effect */
.compact-action-row button:hover {
transform: translateY(-2px) !important;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.5) !important;
}
/* Clear button override */
button[variant="secondary"] {
background: rgba(255, 255, 255, 0.14) !important;
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.25) !important;
}
.upload-section button {
background: linear-gradient(135deg, #56ccf2 0%, #2f80ed 100%) !important;
min-width: 165px !important;
height: 45px !important;
font-size: 0.9rem !important;
border-radius: 12px !important;
color: #ffffff !important;
margin:auto;
margin-bottom: 6px;
}
/* Align Upload button center horizontally */
.upload-section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.compact-btn button {
padding: 4px 0px !important;
font-size: 0.85em !important;
min-height: 4px !important;
max-height: 14px !important;
}
.how-to-use .gr-markdown,
.how-to-use .gr-markdown *,
.how-to-use .prose,
.how-to-use .prose *,
.how-to-use p,
.how-to-use li,
.how-to-use h3{
color: white !important;
}
.how-to-use span{
color:black;
}
.how-to-use label{
color:black;
}
.how-to-use {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(14px) brightness(1.3);
-webkit-backdrop-filter: blur(14px) brightness(1.3);
border: 1px solid rgba(255, 255, 255, 0.18);
border-radius: 20px;
padding: 25px;
margin: 20px 10px;
box-shadow: 0px 8px 25px rgba(0, 0, 0, 0.3);
}
.how-to-use .gr-markdown h3 {
margin-top: 0;
font-size: 1.4rem;
font-weight: 700;
text-shadow: 0px 0px 8px rgba(255,255,255,0.3);
}
.how-to-use .gr-markdown li {
padding: 4px 0;
}
.how-to-use li::marker {
color: #ffffff !important;
}
.upload-btn{
padding: 0px ;
background-color: #1E1C4B;
}
.single-btn {
width: 190px;
margin: auto;
}
.label-text label {
color: white !important;
}
/* Dataframe styling */
.label-text .gr-dataframe,
.label-text [data-testid="dataframe"],
.label-text .dataframe,
div[data-testid="dataframe"],
.gr-dataframe {
min-height: 600px !important;
height: 600px !important;
max-height: 600px !important;
overflow-y: auto !important;
border: 2px solid rgba(255, 255, 255, 0.3) !important;
border-radius: 8px !important;
background: rgba(0, 0, 0, 0.2) !important;
width: 100% !important;
display: block !important;
margin: 10px 0 !important;
}
.label-text .gr-dataframe .wrap,
.label-text [data-testid="dataframe"] .wrap,
div[data-testid="dataframe"] .wrap,
.gr-dataframe .wrap {
min-height: 600px !important;
height: 600px !important;
max-height: 600px !important;
overflow-y: auto !important;
width: 100% !important;
display: block !important;
}
.label-text table,
.gr-dataframe table,
[data-testid="dataframe"] table {
width: 100% !important;
background: rgba(255, 255, 255, 0.05) !important;
color: white !important;
font-size: 14px !important;
}
.label-text th,
.gr-dataframe th,
[data-testid="dataframe"] th {
background: rgba(255, 255, 255, 0.15) !important;
color: white !important;
padding: 12px !important;
font-weight: bold !important;
}
.label-text td,
.gr-dataframe td,
[data-testid="dataframe"] td {
color: white !important;
padding: 10px !important;
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
}
.table-wrap.svelte-1bvc1.p0.no-wrap,
.table-wrap.svelte-1bvc1p0.no-wrap,
.table-wrap {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
min-height: 300px !important;
max-height: 400px !important;
overflow-y: auto !important;
overflow-x: auto !important;
background: rgba(255, 255, 255, 0.05) !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
border-radius: 8px !important;
padding: 10px !important;
}
.table-wrap table {
width: 100% !important;
display: table !important;
color: white !important;
}
.table-wrap::-webkit-scrollbar {
width: 8px !important;
height: 8px !important;
}
.table-wrap::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1) !important;
border-radius: 4px !important;
}
.table-wrap::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3) !important;
border-radius: 4px !important;
}
.table-wrap::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5) !important;
}
.wrap.svelte-1vmd51o{
color: #ffffff !important;
}
button.svelte-jqnyug {
display: none !important;
visibility: hidden !important;
}
* [data-testid="dataframe"],
* .gr-dataframe,
* .dataframe {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
background: rgba(0, 0, 0, 0.6) !important;
border: 3px solid #ffffff !important;
min-height: 200px !important;
}
* [data-testid="dataframe"] *,
* .gr-dataframe *,
* .dataframe * {
display: inherit !important;
visibility: visible !important;
opacity: 1 !important;
color: white !important;
}
div#component-70.block.svelte-90oupt.hide-container {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
background: rgba(255, 255, 255, 0.05) !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important;
border-radius: 8px !important;
padding: 10px !important;
min-height: 300px !important;
max-height: 400px !important;
overflow-y: auto !important;
}
div#component-70 .gr-dataframe,
div#component-70 [data-testid="dataframe"] {
display: block !important;
visibility: visible !important;
background: transparent !important;
}
div#component-70 table {
width: 100% !important;
color: white !important;
display: table !important;
}
div#component-70 td,
div#component-70 th {
color: white !important;
padding: 8px !important;
border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important;
}
.orders-table, .complaints-table {
width: 100% !important;
color: white !important;
border-collapse: collapse !important;
}
.orders-table th, .complaints-table th {
background: rgba(255, 255, 255, 0.1) !important;
color: white !important;
padding: 10px !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
}
.orders-table td, .complaints-table td {
color: white !important;
padding: 8px !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
}
[data-testid="dataframe"] {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
background: rgba(0, 0, 0, 0.6) !important;
border: 3px solid #00ff00 !important;
min-height: 250px !important;
padding: 15px !important;
margin: 15px 0 !important;
}
[data-testid="dataframe"] table {
display: table !important;
visibility: visible !important;
width: 100% !important;
color: white !important;
background: transparent !important;
}
[data-testid="dataframe"] th {
background: rgba(255, 255, 255, 0.2) !important;
color: white !important;
padding: 10px !important;
border: 1px solid white !important;
}
[data-testid="dataframe"] td {
color: white !important;
padding: 8px !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important;
background: rgba(0, 0, 0, 0.2) !important;
}
.gradio-container table tbody tr td[colspan="5"] {
color: white !important;
}
.gradio-container .prose span.md.prose > h3 {
color: white !important;
}
.gradio-container table thead tr th {
color: white !important;
}
.gradio-container table tbody tr td {
color: white !important;
}
"""
# with gr.Blocks(css=custom_css) as demo:
# gr.HTML(
# f"""
# <div class='header'>
# <div class='header-logo'>
# <img src="{logo_b64}" width="120">
# </div>
# <h1>SparkMart</h1>
# </div>
# <div style='text-align: center;'>
# <p style='margin-top:20px; font-size:1.30em; opacity:0.92; color: white;'>
# A conversational AI solution for e-commerce that automates product inquiries,
# order management, and complaint resolution while maintaining customer context,
# history, and secure evidence-based complaint handling.
# </p>
# <p style='margin-top:5px; font-size:1.05em; opacity:0.85; color: white;'>
# Powered by SparkBrains
# </p>
# </div>
# """
# )
# initial_session = int(uuid.uuid4().int % 1000000)
# session_state = gr.State(value=initial_session)
# with gr.Row():
# with gr.Column(scale=1, elem_classes="how-to-use"):
# gr.Markdown("""
# ### How to Use:
# 1. **Chat**: Talk to AI for product recommendations, place orders, or file complaints
# 2. **My Complaints**: Track complaint status and view evidence
# """)
with gr.Blocks(css=custom_css) as demo:
gr.HTML(
f"""
<div class='header'>
<div class='header-logo'>
<img src='{logo_b64}' width="120">
</div>
<h1>SparkMart</h1>
</div>
<div style='text-align: center;'>
<p style='margin-top:20px; font-size:1.30em; opacity:0.92; color: white;'>
A conversational AI solution for e-commerce that automates product inquiries,
order management, and complaint resolution while maintaining customer context,
history, and secure evidence-based complaint handling.
</p>
<p style='margin-top:5px; font-size:1.05em; opacity:0.85; color: white;'>
Powered by SparkBrains
</p>
</div>
"""
)
# MUST BE ALIGNED WITH gr.HTML (same indentation)
initial_session = int(uuid.uuid4().int % 1000000)
session_state = gr.State(value=initial_session)
with gr.Row():
with gr.Column(scale=1, elem_classes="how-to-use"):
gr.Markdown("""
### How to Use:
1. **Chat**: Talk to AI for product recommendations, place orders, or file complaints
2. **My Complaints**: Track complaint status and view evidence
""")
with gr.Column(scale=3):
with gr.Tabs():
with gr.Tab("Chat with Support"):
chatbot = gr.Chatbot(height=550)
# CENTERED FILE UPLOAD BUTTON - placed right below chatbot
with gr.Row(elem_classes="centered-file-container"):
file_input = gr.File(
file_types=["image", "video", ".jpg", ".jpeg", ".png", ".mp4", ".mov"],
file_count="single",
visible=False,
elem_id="evidence-upload"
)
msg_input = gr.Textbox(
placeholder="Type your message here...",
label="Your Message",
lines=1
)
with gr.Row(elem_classes="compact-action-row"):
send_btn = gr.Button("Send", variant="secondary", size="xs", scale=1, elem_classes="compact-btn")
clear_btn = gr.Button("Clear", variant="secondary", size="xs", scale=1, elem_classes="compact-btn")
def respond(message, chat_history, file_path, session_id):
"""Handle user message and bot response"""
if not message or not message.strip():
return chat_history or [], session_id, None, ""
try:
bot_response, updated_session, file_visibility = chat_with_agent(message, file_path, session_id)
# Ensure chat_history is a list
if chat_history is None:
chat_history = []
# Gradio 6.0 expects messages format with role and content
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": bot_response})
return chat_history, updated_session, file_visibility, ""
except Exception as e:
print(f"Error in respond function: {e}")
import traceback
traceback.print_exc()
error_msg = f"I apologize, but I encountered an error: {str(e)}"
if chat_history is None:
chat_history = []
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": error_msg})
return chat_history, session_id, None, ""
send_btn.click(
respond,
inputs=[msg_input, chatbot, file_input, session_state],
outputs=[chatbot, session_state, file_input, msg_input]
)
msg_input.submit(
respond,
inputs=[msg_input, chatbot, file_input, session_state],
outputs=[chatbot, session_state, file_input, msg_input]
)
def clear_and_update():
new_session = int(uuid.uuid4().int % 1000000)
return [], new_session, gr.update(visible=False)
clear_btn.click(
clear_and_update,
outputs=[chatbot, session_state, file_input]
)
with gr.Tab("My Complaints", elem_classes="label-text"):
with gr.Column():
user_id_complaints = gr.Textbox(
label="Enter Your Order ID",
)
check_complaints_btn = gr.Button("View My Complaints", variant="primary", elem_classes='single-btn')
gr.Markdown("### Your Complaints")
complaints_output = gr.HTML(
value="""
<div style='background:rgba(0,0,0,0.4); border:2px solid rgba(255,255,255,0.5); border-radius:8px; padding:15px; max-height:350px; overflow-y:auto;'>
<table style='width:100%; border-collapse:collapse; color:white;'>
<thead>
<tr style='background:rgba(255,255,255,0.1);'>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Order ID</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Product</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Complaint</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Evidence Files</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Date</th>
</tr>
</thead>
<tbody>
<tr>
<td style='border:1px solid rgba(255,255,255,0.2); padding:8px;' colspan='5'>Enter Order ID and click 'View My Complaints' to see your complaints</td>
</tr>
</tbody>
</table>
</div>
"""
)
def handle_view_complaints(user_id):
print(f"Complaints button clicked with user_id: {user_id}")
result = check_complaints(user_id)
print(f"Complaints function returned: {result}")
if hasattr(result, 'empty') and result.empty:
return """
<div style='background:rgba(0,0,0,0.4); border:2px solid rgba(255,255,255,0.5); border-radius:8px; padding:15px;'>
<table style='width:100%; border-collapse:collapse; color:white;'>
<thead>
<tr style='background:rgba(255,255,255,0.1);'>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Order ID</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Product</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Complaint</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Evidence Files</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Date</th>
</tr>
</thead>
<tbody>
<tr><td style='border:1px solid rgba(255,255,255,0.2); padding:8px; text-align:center;' colspan='5'>No complaints found for this Order ID</td></tr>
</tbody>
</table>
</div>
"""
if hasattr(result, 'columns') and 'Message' in result.columns:
message = result['Message'].iloc[0]
return f"""
<div style='background:rgba(0,0,0,0.4); border:2px solid rgba(255,255,255,0.5); border-radius:8px; padding:15px;'>
<table style='width:100%; border-collapse:collapse; color:white;'>
<thead>
<tr style='background:rgba(255,255,255,0.1);'>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Order ID</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Product</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Complaint</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Evidence Files</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Date</th>
</tr>
</thead>
<tbody>
<tr><td style='border:1px solid rgba(255,255,255,0.2); padding:8px; text-align:center;' colspan='5'>{message}</td></tr>
</tbody>
</table>
</div>
"""
if hasattr(result, 'columns') and 'Error' in result.columns:
error_msg = result['Error'].iloc[0]
return f"""
<div style='background:rgba(139,0,0,0.4); border:2px solid rgba(255,100,100,0.5); border-radius:8px; padding:15px;'>
<table style='width:100%; border-collapse:collapse; color:white;'>
<thead>
<tr style='background:rgba(255,255,255,0.1);'>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Order ID</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Product</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Complaint</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Evidence Files</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Date</th>
</tr>
</thead>
<tbody>
<tr><td style='border:1px solid rgba(255,255,255,0.2); padding:8px; text-align:center; color:#ffcccc;' colspan='5'>⚠️ Error: {error_msg}</td></tr>
</tbody>
</table>
</div>
"""
if hasattr(result, 'values') and hasattr(result, 'columns') and len(result.columns) >= 5:
rows_html = ""
for _, row in result.iterrows():
rows_html += f"""
<tr>
<td style='border:1px solid rgba(255,255,255,0.2); padding:8px;'>{row.iloc[0]}</td>
<td style='border:1px solid rgba(255,255,255,0.2); padding:8px;'>{row.iloc[1]}</td>
<td style='border:1px solid rgba(255,255,255,0.2); padding:8px;'>{row.iloc[2]}</td>
<td style='border:1px solid rgba(255,255,255,0.2); padding:8px;'>{row.iloc[3]}</td>
<td style='border:1px solid rgba(255,255,255,0.2); padding:8px;'>{row.iloc[4]}</td>
</tr>
"""
return f"""
<div style='background:rgba(0,0,0,0.4); border:2px solid rgba(255,255,255,0.5); border-radius:8px; padding:15px; max-height:350px; overflow-y:auto;'>
<table style='width:100%; border-collapse:collapse; color:white;'>
<thead>
<tr style='background:rgba(255,255,255,0.1);'>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Order ID</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Product</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Complaint</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Evidence Files</th>
<th style='border:1px solid rgba(255,255,255,0.3); padding:10px; text-align:left;'>Date</th>
</tr>
</thead>
<tbody>
{rows_html}
</tbody>
</table>
</div>
"""
return f"<div style='padding:20px; color:white;'>{str(result)}</div>"
check_complaints_btn.click(
handle_view_complaints,
inputs=[user_id_complaints],
outputs=[complaints_output]
)
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
debug=True
)