Spaces:
Sleeping
Sleeping
Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -24,8 +24,8 @@ from langchain_groq import ChatGroq
|
|
| 24 |
|
| 25 |
# Page Configuration
|
| 26 |
st.set_page_config(
|
| 27 |
-
page_title="
|
| 28 |
-
page_icon="
|
| 29 |
layout="wide",
|
| 30 |
initial_sidebar_state="expanded"
|
| 31 |
)
|
|
@@ -70,7 +70,7 @@ st.markdown("""
|
|
| 70 |
.status-picked { background-color: #E3F2FD; color: #1565C0; }
|
| 71 |
.status-delivered { background-color: #E8F5E9; color: #2E7D32; }
|
| 72 |
.status-canceled { background-color: #FFEBEE; color: #C62828; }
|
| 73 |
-
|
| 74 |
.stButton>button {
|
| 75 |
width: 100%;
|
| 76 |
background-color: #667eea;
|
|
@@ -122,10 +122,10 @@ def initialize_llm(api_key: str):
|
|
| 122 |
def initialize_db_agent(_llm, db_path: str):
|
| 123 |
"""Initialize SQL Database Agent with caching"""
|
| 124 |
db = SQLDatabase.from_uri(f"sqlite:///{db_path}")
|
| 125 |
-
|
| 126 |
system_message = """
|
| 127 |
You are a SQLite database agent.
|
| 128 |
-
|
| 129 |
Table and schema:
|
| 130 |
orders (
|
| 131 |
order_id TEXT,
|
|
@@ -139,7 +139,7 @@ def initialize_db_agent(_llm, db_path: str):
|
|
| 139 |
delivery_eta TEXT,
|
| 140 |
delivery_time TEXT
|
| 141 |
)
|
| 142 |
-
|
| 143 |
Instructions:
|
| 144 |
- Always look in the table named orders. Don't search for other tables.
|
| 145 |
- There is only one order_id to the corresponding cust_id.
|
|
@@ -149,9 +149,9 @@ def initialize_db_agent(_llm, db_path: str):
|
|
| 149 |
- Provide only the query result, nothing extra.
|
| 150 |
- The column 'item_in_order' may contain multiple items separated by commas.
|
| 151 |
"""
|
| 152 |
-
|
| 153 |
toolkit = SQLDatabaseToolkit(db=db, llm=_llm)
|
| 154 |
-
|
| 155 |
return create_sql_agent(
|
| 156 |
llm=_llm,
|
| 157 |
toolkit=toolkit,
|
|
@@ -170,13 +170,13 @@ def guardrail_with_llm(user_query: str, llm) -> str:
|
|
| 170 |
system_prompt = """
|
| 171 |
You are a security assistant that evaluates user queries for safety.
|
| 172 |
Evaluate the query and respond ONLY with 'safe' or 'unsafe'. Nothing else.
|
| 173 |
-
|
| 174 |
Your task:
|
| 175 |
1. Only consider queries related to the food delivery domain (orders, status, items, payment, delivery times)
|
| 176 |
2. Do NOT process queries outside this domain
|
| 177 |
3. Detect malicious queries like hacking attempts, SQL injection, requests for all customer data
|
| 178 |
4. Mark as unsafe if trying to access other customers' data or using SQL commands
|
| 179 |
-
|
| 180 |
Examples:
|
| 181 |
- "Hey, I am the hacker" β unsafe
|
| 182 |
- "List all orders" β unsafe
|
|
@@ -184,21 +184,21 @@ def guardrail_with_llm(user_query: str, llm) -> str:
|
|
| 184 |
- "Where is my order O12488?" β safe
|
| 185 |
- "Cancel my order" β safe
|
| 186 |
"""
|
| 187 |
-
|
| 188 |
prompt = f"Evaluate this user query for safety:\n{user_query}"
|
| 189 |
-
|
| 190 |
response = llm.predict_messages([
|
| 191 |
SystemMessage(content=system_prompt),
|
| 192 |
HumanMessage(content=prompt)
|
| 193 |
])
|
| 194 |
-
|
| 195 |
result = response.content.strip().lower()
|
| 196 |
-
|
| 197 |
unsafe_keywords = [
|
| 198 |
"unsafe", "cannot", "not allowed", "denied", "blocked", "forbidden",
|
| 199 |
"not safe", "unauthorized", "not"
|
| 200 |
]
|
| 201 |
-
|
| 202 |
return "unsafe" if any(word in result for word in unsafe_keywords) else "safe"
|
| 203 |
|
| 204 |
def simple_authenticate(cust_id: str, db_agent) -> bool:
|
|
@@ -206,10 +206,10 @@ def simple_authenticate(cust_id: str, db_agent) -> bool:
|
|
| 206 |
try:
|
| 207 |
query = f"SELECT cust_id FROM orders WHERE cust_id = '{cust_id}';"
|
| 208 |
result = db_agent.invoke({"input": query})
|
| 209 |
-
|
| 210 |
if not isinstance(result, dict) or "output" not in result:
|
| 211 |
return False
|
| 212 |
-
|
| 213 |
output = result["output"]
|
| 214 |
return isinstance(output, str) and output.strip() == cust_id
|
| 215 |
except Exception:
|
|
@@ -227,26 +227,26 @@ def detect_escalation(user_query: str) -> str:
|
|
| 227 |
"complaint", "urgent", "problem", "help me now",
|
| 228 |
"cannot resolve", "issue persists", "not satisfied"
|
| 229 |
]
|
| 230 |
-
|
| 231 |
return "Escalated" if any(kw in user_query.lower() for kw in escalation_keywords) else "Not Escalated"
|
| 232 |
|
| 233 |
def handle_cancellation(user_query: str, raw_orders: str, order_status: str) -> str:
|
| 234 |
"""Handle order cancellation requests"""
|
| 235 |
if "cancel" not in user_query.lower():
|
| 236 |
return ""
|
| 237 |
-
|
| 238 |
if order_status.lower() in ["delivered", "canceled"]:
|
| 239 |
return (
|
| 240 |
f"Your order has already been {order_status.lower()}, "
|
| 241 |
"so cancellation is not possible. Thank you for understanding!"
|
| 242 |
)
|
| 243 |
-
|
| 244 |
elif order_status.lower() in ["preparing food", "picked up"]:
|
| 245 |
return (
|
| 246 |
f"Present status of your order is: {order_status.lower()}. "
|
| 247 |
"Cancellation is not possible at this stage. Thank you for understanding!"
|
| 248 |
)
|
| 249 |
-
|
| 250 |
return "Your order cannot be canceled. We hope to serve you again!"
|
| 251 |
|
| 252 |
def format_customer_response(cust_id: str, raw_orders: str, user_query: str, llm) -> str:
|
|
@@ -254,7 +254,7 @@ def format_customer_response(cust_id: str, raw_orders: str, user_query: str, llm
|
|
| 254 |
order_status = None
|
| 255 |
preparing_eta = None
|
| 256 |
delivery_time = None
|
| 257 |
-
|
| 258 |
# Parse order details
|
| 259 |
for line in raw_orders.splitlines():
|
| 260 |
if "Order Status" in line or "order_status" in line:
|
|
@@ -263,7 +263,7 @@ def format_customer_response(cust_id: str, raw_orders: str, user_query: str, llm
|
|
| 263 |
preparing_eta = line.split(":", 1)[1].strip()
|
| 264 |
elif "Delivery Time" in line or "delivery_time" in line:
|
| 265 |
delivery_time = line.split(":", 1)[1].strip()
|
| 266 |
-
|
| 267 |
# Check escalation
|
| 268 |
escalation_var = detect_escalation(user_query)
|
| 269 |
if escalation_var == "Escalated":
|
|
@@ -272,19 +272,19 @@ def format_customer_response(cust_id: str, raw_orders: str, user_query: str, llm
|
|
| 272 |
"β οΈ Your issue requires immediate attention. "
|
| 273 |
"We have escalated your query to a human agent who will contact you shortly."
|
| 274 |
)
|
| 275 |
-
|
| 276 |
# Check cancellation
|
| 277 |
cancel_response = handle_cancellation(user_query, raw_orders, order_status)
|
| 278 |
if cancel_response:
|
| 279 |
return cancel_response
|
| 280 |
-
|
| 281 |
# Generate normal response using LLM
|
| 282 |
system_prompt = f"""
|
| 283 |
You are a friendly customer support assistant for FoodHub.
|
| 284 |
-
|
| 285 |
Customer ID: {cust_id}
|
| 286 |
Order data: {raw_orders}
|
| 287 |
-
|
| 288 |
Instructions:
|
| 289 |
1. Respond naturally and conversationally in a short response
|
| 290 |
2. Use only the order data to answer
|
|
@@ -294,29 +294,29 @@ def format_customer_response(cust_id: str, raw_orders: str, user_query: str, llm
|
|
| 294 |
6. For "Where is my order" queries, provide order_status
|
| 295 |
7. For "How many items" queries, count items in item_in_order
|
| 296 |
"""
|
| 297 |
-
|
| 298 |
user_prompt = f"User Query: {user_query}"
|
| 299 |
-
|
| 300 |
response_msg = llm.predict_messages([
|
| 301 |
SystemMessage(content=system_prompt),
|
| 302 |
HumanMessage(content=user_prompt)
|
| 303 |
])
|
| 304 |
-
|
| 305 |
response = response_msg.content.strip()
|
| 306 |
return response if response else "Sorry, we could not retrieve your order details at this time."
|
| 307 |
|
| 308 |
def order_chatbot(cust_id: str, user_message: str, llm, db_agent) -> str:
|
| 309 |
"""Main chatbot function to handle customer queries"""
|
| 310 |
-
|
| 311 |
# Step 1: Security guardrail
|
| 312 |
guardrail_response = guardrail_with_llm(user_message, llm)
|
| 313 |
if "unsafe" in guardrail_response.lower():
|
| 314 |
return "π« Unauthorized or irrelevant query. Please ask something related to your order only."
|
| 315 |
-
|
| 316 |
# Step 2: Authentication
|
| 317 |
if not simple_authenticate(cust_id, db_agent):
|
| 318 |
return "π« Invalid customer ID. Please provide a valid customer ID."
|
| 319 |
-
|
| 320 |
# Step 3: Fetch order details
|
| 321 |
try:
|
| 322 |
order_result = db_agent.invoke(
|
|
@@ -325,7 +325,7 @@ def order_chatbot(cust_id: str, user_message: str, llm, db_agent) -> str:
|
|
| 325 |
raw_orders = order_result.get("output") if order_result else None
|
| 326 |
except Exception:
|
| 327 |
return "π« Sorry, we cannot fetch your order details right now. Please try again later."
|
| 328 |
-
|
| 329 |
# Step 4: Generate response
|
| 330 |
final_response = format_customer_response(cust_id, raw_orders, user_message, llm)
|
| 331 |
return final_response
|
|
@@ -336,27 +336,27 @@ def order_chatbot(cust_id: str, user_message: str, llm, db_agent) -> str:
|
|
| 336 |
|
| 337 |
def render_header():
|
| 338 |
"""Render the main header"""
|
| 339 |
-
st.markdown('<h1 class="main-header">
|
| 340 |
|
| 341 |
def render_sidebar():
|
| 342 |
"""Render sidebar with configuration and info"""
|
| 343 |
with st.sidebar:
|
| 344 |
-
st.header("
|
| 345 |
-
|
| 346 |
# API Key Input
|
| 347 |
api_key = st.text_input(
|
| 348 |
"Groq API Key",
|
| 349 |
type="password",
|
| 350 |
help="Enter your Groq API key to use the chatbot"
|
| 351 |
)
|
| 352 |
-
|
| 353 |
# Database Path Input
|
| 354 |
db_path = st.text_input(
|
| 355 |
"Database Path",
|
| 356 |
value="customer_orders.db",
|
| 357 |
help="Path to your SQLite database file"
|
| 358 |
)
|
| 359 |
-
|
| 360 |
# Initialize button
|
| 361 |
if st.button("π Initialize Chatbot"):
|
| 362 |
if api_key and db_path:
|
|
@@ -369,9 +369,9 @@ def render_sidebar():
|
|
| 369 |
st.error(f"β Error initializing chatbot: {str(e)}")
|
| 370 |
else:
|
| 371 |
st.warning("β οΈ Please provide both API key and database path")
|
| 372 |
-
|
| 373 |
st.divider()
|
| 374 |
-
|
| 375 |
# Customer Authentication Section
|
| 376 |
st.header("π€ Customer Login")
|
| 377 |
cust_id_input = st.text_input(
|
|
@@ -379,7 +379,7 @@ def render_sidebar():
|
|
| 379 |
placeholder="e.g., C1001",
|
| 380 |
help="Enter your customer ID to start chatting"
|
| 381 |
)
|
| 382 |
-
|
| 383 |
if st.button("π Login"):
|
| 384 |
if cust_id_input and st.session_state.db_agent:
|
| 385 |
if simple_authenticate(cust_id_input, st.session_state.db_agent):
|
|
@@ -393,7 +393,7 @@ def render_sidebar():
|
|
| 393 |
st.warning("β οΈ Please initialize the chatbot first")
|
| 394 |
else:
|
| 395 |
st.warning("β οΈ Please enter a Customer ID")
|
| 396 |
-
|
| 397 |
# Logout button
|
| 398 |
if st.session_state.authenticated:
|
| 399 |
if st.button("πͺ Logout"):
|
|
@@ -401,9 +401,9 @@ def render_sidebar():
|
|
| 401 |
st.session_state.customer_id = None
|
| 402 |
st.session_state.chat_history = []
|
| 403 |
st.rerun()
|
| 404 |
-
|
| 405 |
st.divider()
|
| 406 |
-
|
| 407 |
# Info section
|
| 408 |
st.header("βΉοΈ About")
|
| 409 |
st.info(
|
|
@@ -414,7 +414,7 @@ def render_sidebar():
|
|
| 414 |
"β
Request order cancellation\n"
|
| 415 |
"β
Escalate urgent issues"
|
| 416 |
)
|
| 417 |
-
|
| 418 |
# Quick commands
|
| 419 |
st.header("π‘ Quick Commands")
|
| 420 |
st.code("Where is my order?", language="text")
|
|
@@ -424,18 +424,18 @@ def render_sidebar():
|
|
| 424 |
|
| 425 |
def render_chat_interface():
|
| 426 |
"""Render the main chat interface"""
|
| 427 |
-
|
| 428 |
# Check if authenticated
|
| 429 |
if not st.session_state.authenticated:
|
| 430 |
st.info("π Please login with your Customer ID from the sidebar to start chatting")
|
| 431 |
return
|
| 432 |
-
|
| 433 |
# Display customer info
|
| 434 |
st.success(f"π Logged in as: **{st.session_state.customer_id}**")
|
| 435 |
-
|
| 436 |
# Chat history container
|
| 437 |
chat_container = st.container()
|
| 438 |
-
|
| 439 |
with chat_container:
|
| 440 |
for message in st.session_state.chat_history:
|
| 441 |
if message["role"] == "user":
|
|
@@ -452,12 +452,12 @@ def render_chat_interface():
|
|
| 452 |
f'</div>',
|
| 453 |
unsafe_allow_html=True
|
| 454 |
)
|
| 455 |
-
|
| 456 |
# Input area
|
| 457 |
st.divider()
|
| 458 |
-
|
| 459 |
col1, col2 = st.columns([5, 1])
|
| 460 |
-
|
| 461 |
with col1:
|
| 462 |
user_input = st.text_input(
|
| 463 |
"Type your message here...",
|
|
@@ -465,10 +465,10 @@ def render_chat_interface():
|
|
| 465 |
key="user_input",
|
| 466 |
label_visibility="collapsed"
|
| 467 |
)
|
| 468 |
-
|
| 469 |
with col2:
|
| 470 |
send_button = st.button("π€ Send", use_container_width=True)
|
| 471 |
-
|
| 472 |
# Handle message sending
|
| 473 |
if send_button and user_input:
|
| 474 |
if st.session_state.llm and st.session_state.db_agent:
|
|
@@ -477,7 +477,7 @@ def render_chat_interface():
|
|
| 477 |
"role": "user",
|
| 478 |
"content": user_input
|
| 479 |
})
|
| 480 |
-
|
| 481 |
# Get bot response
|
| 482 |
with st.spinner("π€ Thinking..."):
|
| 483 |
bot_response = order_chatbot(
|
|
@@ -486,18 +486,18 @@ def render_chat_interface():
|
|
| 486 |
st.session_state.llm,
|
| 487 |
st.session_state.db_agent
|
| 488 |
)
|
| 489 |
-
|
| 490 |
# Add bot response to history
|
| 491 |
st.session_state.chat_history.append({
|
| 492 |
"role": "bot",
|
| 493 |
"content": bot_response
|
| 494 |
})
|
| 495 |
-
|
| 496 |
# Rerun to update chat
|
| 497 |
st.rerun()
|
| 498 |
else:
|
| 499 |
st.warning("β οΈ Please initialize the chatbot from the sidebar first")
|
| 500 |
-
|
| 501 |
# Clear chat button
|
| 502 |
if st.session_state.chat_history:
|
| 503 |
if st.button("ποΈ Clear Chat History"):
|
|
@@ -513,12 +513,12 @@ def main():
|
|
| 513 |
render_header()
|
| 514 |
render_sidebar()
|
| 515 |
render_chat_interface()
|
| 516 |
-
|
| 517 |
# Footer
|
| 518 |
st.divider()
|
| 519 |
st.markdown(
|
| 520 |
"<p style='text-align: center; color: #666;'>"
|
| 521 |
-
"
|
| 522 |
"</p>",
|
| 523 |
unsafe_allow_html=True
|
| 524 |
)
|
|
|
|
| 24 |
|
| 25 |
# Page Configuration
|
| 26 |
st.set_page_config(
|
| 27 |
+
page_title="Food Delivery Chatbot",
|
| 28 |
+
page_icon="π΅ ",
|
| 29 |
layout="wide",
|
| 30 |
initial_sidebar_state="expanded"
|
| 31 |
)
|
|
|
|
| 70 |
.status-picked { background-color: #E3F2FD; color: #1565C0; }
|
| 71 |
.status-delivered { background-color: #E8F5E9; color: #2E7D32; }
|
| 72 |
.status-canceled { background-color: #FFEBEE; color: #C62828; }
|
| 73 |
+
|
| 74 |
.stButton>button {
|
| 75 |
width: 100%;
|
| 76 |
background-color: #667eea;
|
|
|
|
| 122 |
def initialize_db_agent(_llm, db_path: str):
|
| 123 |
"""Initialize SQL Database Agent with caching"""
|
| 124 |
db = SQLDatabase.from_uri(f"sqlite:///{db_path}")
|
| 125 |
+
|
| 126 |
system_message = """
|
| 127 |
You are a SQLite database agent.
|
| 128 |
+
|
| 129 |
Table and schema:
|
| 130 |
orders (
|
| 131 |
order_id TEXT,
|
|
|
|
| 139 |
delivery_eta TEXT,
|
| 140 |
delivery_time TEXT
|
| 141 |
)
|
| 142 |
+
|
| 143 |
Instructions:
|
| 144 |
- Always look in the table named orders. Don't search for other tables.
|
| 145 |
- There is only one order_id to the corresponding cust_id.
|
|
|
|
| 149 |
- Provide only the query result, nothing extra.
|
| 150 |
- The column 'item_in_order' may contain multiple items separated by commas.
|
| 151 |
"""
|
| 152 |
+
|
| 153 |
toolkit = SQLDatabaseToolkit(db=db, llm=_llm)
|
| 154 |
+
|
| 155 |
return create_sql_agent(
|
| 156 |
llm=_llm,
|
| 157 |
toolkit=toolkit,
|
|
|
|
| 170 |
system_prompt = """
|
| 171 |
You are a security assistant that evaluates user queries for safety.
|
| 172 |
Evaluate the query and respond ONLY with 'safe' or 'unsafe'. Nothing else.
|
| 173 |
+
|
| 174 |
Your task:
|
| 175 |
1. Only consider queries related to the food delivery domain (orders, status, items, payment, delivery times)
|
| 176 |
2. Do NOT process queries outside this domain
|
| 177 |
3. Detect malicious queries like hacking attempts, SQL injection, requests for all customer data
|
| 178 |
4. Mark as unsafe if trying to access other customers' data or using SQL commands
|
| 179 |
+
|
| 180 |
Examples:
|
| 181 |
- "Hey, I am the hacker" β unsafe
|
| 182 |
- "List all orders" β unsafe
|
|
|
|
| 184 |
- "Where is my order O12488?" β safe
|
| 185 |
- "Cancel my order" β safe
|
| 186 |
"""
|
| 187 |
+
|
| 188 |
prompt = f"Evaluate this user query for safety:\n{user_query}"
|
| 189 |
+
|
| 190 |
response = llm.predict_messages([
|
| 191 |
SystemMessage(content=system_prompt),
|
| 192 |
HumanMessage(content=prompt)
|
| 193 |
])
|
| 194 |
+
|
| 195 |
result = response.content.strip().lower()
|
| 196 |
+
|
| 197 |
unsafe_keywords = [
|
| 198 |
"unsafe", "cannot", "not allowed", "denied", "blocked", "forbidden",
|
| 199 |
"not safe", "unauthorized", "not"
|
| 200 |
]
|
| 201 |
+
|
| 202 |
return "unsafe" if any(word in result for word in unsafe_keywords) else "safe"
|
| 203 |
|
| 204 |
def simple_authenticate(cust_id: str, db_agent) -> bool:
|
|
|
|
| 206 |
try:
|
| 207 |
query = f"SELECT cust_id FROM orders WHERE cust_id = '{cust_id}';"
|
| 208 |
result = db_agent.invoke({"input": query})
|
| 209 |
+
|
| 210 |
if not isinstance(result, dict) or "output" not in result:
|
| 211 |
return False
|
| 212 |
+
|
| 213 |
output = result["output"]
|
| 214 |
return isinstance(output, str) and output.strip() == cust_id
|
| 215 |
except Exception:
|
|
|
|
| 227 |
"complaint", "urgent", "problem", "help me now",
|
| 228 |
"cannot resolve", "issue persists", "not satisfied"
|
| 229 |
]
|
| 230 |
+
|
| 231 |
return "Escalated" if any(kw in user_query.lower() for kw in escalation_keywords) else "Not Escalated"
|
| 232 |
|
| 233 |
def handle_cancellation(user_query: str, raw_orders: str, order_status: str) -> str:
|
| 234 |
"""Handle order cancellation requests"""
|
| 235 |
if "cancel" not in user_query.lower():
|
| 236 |
return ""
|
| 237 |
+
|
| 238 |
if order_status.lower() in ["delivered", "canceled"]:
|
| 239 |
return (
|
| 240 |
f"Your order has already been {order_status.lower()}, "
|
| 241 |
"so cancellation is not possible. Thank you for understanding!"
|
| 242 |
)
|
| 243 |
+
|
| 244 |
elif order_status.lower() in ["preparing food", "picked up"]:
|
| 245 |
return (
|
| 246 |
f"Present status of your order is: {order_status.lower()}. "
|
| 247 |
"Cancellation is not possible at this stage. Thank you for understanding!"
|
| 248 |
)
|
| 249 |
+
|
| 250 |
return "Your order cannot be canceled. We hope to serve you again!"
|
| 251 |
|
| 252 |
def format_customer_response(cust_id: str, raw_orders: str, user_query: str, llm) -> str:
|
|
|
|
| 254 |
order_status = None
|
| 255 |
preparing_eta = None
|
| 256 |
delivery_time = None
|
| 257 |
+
|
| 258 |
# Parse order details
|
| 259 |
for line in raw_orders.splitlines():
|
| 260 |
if "Order Status" in line or "order_status" in line:
|
|
|
|
| 263 |
preparing_eta = line.split(":", 1)[1].strip()
|
| 264 |
elif "Delivery Time" in line or "delivery_time" in line:
|
| 265 |
delivery_time = line.split(":", 1)[1].strip()
|
| 266 |
+
|
| 267 |
# Check escalation
|
| 268 |
escalation_var = detect_escalation(user_query)
|
| 269 |
if escalation_var == "Escalated":
|
|
|
|
| 272 |
"β οΈ Your issue requires immediate attention. "
|
| 273 |
"We have escalated your query to a human agent who will contact you shortly."
|
| 274 |
)
|
| 275 |
+
|
| 276 |
# Check cancellation
|
| 277 |
cancel_response = handle_cancellation(user_query, raw_orders, order_status)
|
| 278 |
if cancel_response:
|
| 279 |
return cancel_response
|
| 280 |
+
|
| 281 |
# Generate normal response using LLM
|
| 282 |
system_prompt = f"""
|
| 283 |
You are a friendly customer support assistant for FoodHub.
|
| 284 |
+
|
| 285 |
Customer ID: {cust_id}
|
| 286 |
Order data: {raw_orders}
|
| 287 |
+
|
| 288 |
Instructions:
|
| 289 |
1. Respond naturally and conversationally in a short response
|
| 290 |
2. Use only the order data to answer
|
|
|
|
| 294 |
6. For "Where is my order" queries, provide order_status
|
| 295 |
7. For "How many items" queries, count items in item_in_order
|
| 296 |
"""
|
| 297 |
+
|
| 298 |
user_prompt = f"User Query: {user_query}"
|
| 299 |
+
|
| 300 |
response_msg = llm.predict_messages([
|
| 301 |
SystemMessage(content=system_prompt),
|
| 302 |
HumanMessage(content=user_prompt)
|
| 303 |
])
|
| 304 |
+
|
| 305 |
response = response_msg.content.strip()
|
| 306 |
return response if response else "Sorry, we could not retrieve your order details at this time."
|
| 307 |
|
| 308 |
def order_chatbot(cust_id: str, user_message: str, llm, db_agent) -> str:
|
| 309 |
"""Main chatbot function to handle customer queries"""
|
| 310 |
+
|
| 311 |
# Step 1: Security guardrail
|
| 312 |
guardrail_response = guardrail_with_llm(user_message, llm)
|
| 313 |
if "unsafe" in guardrail_response.lower():
|
| 314 |
return "π« Unauthorized or irrelevant query. Please ask something related to your order only."
|
| 315 |
+
|
| 316 |
# Step 2: Authentication
|
| 317 |
if not simple_authenticate(cust_id, db_agent):
|
| 318 |
return "π« Invalid customer ID. Please provide a valid customer ID."
|
| 319 |
+
|
| 320 |
# Step 3: Fetch order details
|
| 321 |
try:
|
| 322 |
order_result = db_agent.invoke(
|
|
|
|
| 325 |
raw_orders = order_result.get("output") if order_result else None
|
| 326 |
except Exception:
|
| 327 |
return "π« Sorry, we cannot fetch your order details right now. Please try again later."
|
| 328 |
+
|
| 329 |
# Step 4: Generate response
|
| 330 |
final_response = format_customer_response(cust_id, raw_orders, user_message, llm)
|
| 331 |
return final_response
|
|
|
|
| 336 |
|
| 337 |
def render_header():
|
| 338 |
"""Render the main header"""
|
| 339 |
+
st.markdown('<h1 class="main-header">π΅ Food Delivery - AI Chatbot π€</h1>', unsafe_allow_html=True)
|
| 340 |
|
| 341 |
def render_sidebar():
|
| 342 |
"""Render sidebar with configuration and info"""
|
| 343 |
with st.sidebar:
|
| 344 |
+
st.header("π Configuration")
|
| 345 |
+
|
| 346 |
# API Key Input
|
| 347 |
api_key = st.text_input(
|
| 348 |
"Groq API Key",
|
| 349 |
type="password",
|
| 350 |
help="Enter your Groq API key to use the chatbot"
|
| 351 |
)
|
| 352 |
+
|
| 353 |
# Database Path Input
|
| 354 |
db_path = st.text_input(
|
| 355 |
"Database Path",
|
| 356 |
value="customer_orders.db",
|
| 357 |
help="Path to your SQLite database file"
|
| 358 |
)
|
| 359 |
+
|
| 360 |
# Initialize button
|
| 361 |
if st.button("π Initialize Chatbot"):
|
| 362 |
if api_key and db_path:
|
|
|
|
| 369 |
st.error(f"β Error initializing chatbot: {str(e)}")
|
| 370 |
else:
|
| 371 |
st.warning("β οΈ Please provide both API key and database path")
|
| 372 |
+
|
| 373 |
st.divider()
|
| 374 |
+
|
| 375 |
# Customer Authentication Section
|
| 376 |
st.header("π€ Customer Login")
|
| 377 |
cust_id_input = st.text_input(
|
|
|
|
| 379 |
placeholder="e.g., C1001",
|
| 380 |
help="Enter your customer ID to start chatting"
|
| 381 |
)
|
| 382 |
+
|
| 383 |
if st.button("π Login"):
|
| 384 |
if cust_id_input and st.session_state.db_agent:
|
| 385 |
if simple_authenticate(cust_id_input, st.session_state.db_agent):
|
|
|
|
| 393 |
st.warning("β οΈ Please initialize the chatbot first")
|
| 394 |
else:
|
| 395 |
st.warning("β οΈ Please enter a Customer ID")
|
| 396 |
+
|
| 397 |
# Logout button
|
| 398 |
if st.session_state.authenticated:
|
| 399 |
if st.button("πͺ Logout"):
|
|
|
|
| 401 |
st.session_state.customer_id = None
|
| 402 |
st.session_state.chat_history = []
|
| 403 |
st.rerun()
|
| 404 |
+
|
| 405 |
st.divider()
|
| 406 |
+
|
| 407 |
# Info section
|
| 408 |
st.header("βΉοΈ About")
|
| 409 |
st.info(
|
|
|
|
| 414 |
"β
Request order cancellation\n"
|
| 415 |
"β
Escalate urgent issues"
|
| 416 |
)
|
| 417 |
+
|
| 418 |
# Quick commands
|
| 419 |
st.header("π‘ Quick Commands")
|
| 420 |
st.code("Where is my order?", language="text")
|
|
|
|
| 424 |
|
| 425 |
def render_chat_interface():
|
| 426 |
"""Render the main chat interface"""
|
| 427 |
+
|
| 428 |
# Check if authenticated
|
| 429 |
if not st.session_state.authenticated:
|
| 430 |
st.info("π Please login with your Customer ID from the sidebar to start chatting")
|
| 431 |
return
|
| 432 |
+
|
| 433 |
# Display customer info
|
| 434 |
st.success(f"π Logged in as: **{st.session_state.customer_id}**")
|
| 435 |
+
|
| 436 |
# Chat history container
|
| 437 |
chat_container = st.container()
|
| 438 |
+
|
| 439 |
with chat_container:
|
| 440 |
for message in st.session_state.chat_history:
|
| 441 |
if message["role"] == "user":
|
|
|
|
| 452 |
f'</div>',
|
| 453 |
unsafe_allow_html=True
|
| 454 |
)
|
| 455 |
+
|
| 456 |
# Input area
|
| 457 |
st.divider()
|
| 458 |
+
|
| 459 |
col1, col2 = st.columns([5, 1])
|
| 460 |
+
|
| 461 |
with col1:
|
| 462 |
user_input = st.text_input(
|
| 463 |
"Type your message here...",
|
|
|
|
| 465 |
key="user_input",
|
| 466 |
label_visibility="collapsed"
|
| 467 |
)
|
| 468 |
+
|
| 469 |
with col2:
|
| 470 |
send_button = st.button("π€ Send", use_container_width=True)
|
| 471 |
+
|
| 472 |
# Handle message sending
|
| 473 |
if send_button and user_input:
|
| 474 |
if st.session_state.llm and st.session_state.db_agent:
|
|
|
|
| 477 |
"role": "user",
|
| 478 |
"content": user_input
|
| 479 |
})
|
| 480 |
+
|
| 481 |
# Get bot response
|
| 482 |
with st.spinner("π€ Thinking..."):
|
| 483 |
bot_response = order_chatbot(
|
|
|
|
| 486 |
st.session_state.llm,
|
| 487 |
st.session_state.db_agent
|
| 488 |
)
|
| 489 |
+
|
| 490 |
# Add bot response to history
|
| 491 |
st.session_state.chat_history.append({
|
| 492 |
"role": "bot",
|
| 493 |
"content": bot_response
|
| 494 |
})
|
| 495 |
+
|
| 496 |
# Rerun to update chat
|
| 497 |
st.rerun()
|
| 498 |
else:
|
| 499 |
st.warning("β οΈ Please initialize the chatbot from the sidebar first")
|
| 500 |
+
|
| 501 |
# Clear chat button
|
| 502 |
if st.session_state.chat_history:
|
| 503 |
if st.button("ποΈ Clear Chat History"):
|
|
|
|
| 513 |
render_header()
|
| 514 |
render_sidebar()
|
| 515 |
render_chat_interface()
|
| 516 |
+
|
| 517 |
# Footer
|
| 518 |
st.divider()
|
| 519 |
st.markdown(
|
| 520 |
"<p style='text-align: center; color: #666;'>"
|
| 521 |
+
"Build by πͺ by Group 7 GL PGP Team | Powered by HuggingFace LangChain & Groq"
|
| 522 |
"</p>",
|
| 523 |
unsafe_allow_html=True
|
| 524 |
)
|