Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,10 +1,6 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
-
import
|
| 3 |
-
import
|
| 4 |
-
import toml
|
| 5 |
-
import pathlib
|
| 6 |
-
import json
|
| 7 |
-
import time
|
| 8 |
|
| 9 |
# Page configuration
|
| 10 |
st.set_page_config(
|
|
@@ -13,83 +9,10 @@ st.set_page_config(
|
|
| 13 |
layout="centered"
|
| 14 |
)
|
| 15 |
|
| 16 |
-
#
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
if hasattr(st, 'secrets'):
|
| 21 |
-
try:
|
| 22 |
-
return st.secrets.get("HF_API_TOKEN", "")
|
| 23 |
-
except:
|
| 24 |
-
pass
|
| 25 |
-
|
| 26 |
-
# Second, try to find secrets.toml in the .streamlit directory
|
| 27 |
-
secrets_path = pathlib.Path(".streamlit/secrets.toml")
|
| 28 |
-
if secrets_path.exists():
|
| 29 |
-
secrets = toml.load(secrets_path)
|
| 30 |
-
return secrets.get("HF_API_TOKEN", "")
|
| 31 |
-
|
| 32 |
-
# Third, check in user's home directory
|
| 33 |
-
home_secrets_path = pathlib.Path.home() / ".streamlit/secrets.toml"
|
| 34 |
-
if home_secrets_path.exists():
|
| 35 |
-
secrets = toml.load(home_secrets_path)
|
| 36 |
-
return secrets.get("HF_API_TOKEN", "")
|
| 37 |
-
|
| 38 |
-
# Last, check environment variables
|
| 39 |
-
return os.environ.get("HF_API_TOKEN", "")
|
| 40 |
-
except Exception as e:
|
| 41 |
-
st.sidebar.error(f"Error loading secrets: {e}")
|
| 42 |
-
return ""
|
| 43 |
-
|
| 44 |
-
# Model selection
|
| 45 |
-
MODEL_OPTIONS = {
|
| 46 |
-
"Llama-3-8B-Instruct": "meta-llama/Meta-Llama-3-8B-Instruct", # More up-to-date model
|
| 47 |
-
"Llama-2-7B-Chat": "meta-llama/Llama-2-7b-chat-hf",
|
| 48 |
-
"Mistral-7B-Instruct": "mistralai/Mistral-7B-Instruct-v0.2",
|
| 49 |
-
"Falcon-7B-Instruct": "tiiuae/falcon-7b-instruct",
|
| 50 |
-
"OpenAssistant": "OpenAssistant/oasst-sft-4-pythia-12b-epoch-3.5",
|
| 51 |
-
"Flan-T5-Large": "google/flan-t5-large", # Open access model
|
| 52 |
-
"GPT2": "gpt2", # Fully open model
|
| 53 |
-
"Rule-Based (No API)": "local" # Completely local option
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
# Allow model selection and initialize with default
|
| 57 |
-
if "selected_model" not in st.session_state:
|
| 58 |
-
st.session_state.selected_model = "Rule-Based (No API)" # Default to local option for reliability
|
| 59 |
-
|
| 60 |
-
# Hugging Face API setup
|
| 61 |
-
def get_api_url():
|
| 62 |
-
model_id = MODEL_OPTIONS[st.session_state.selected_model]
|
| 63 |
-
return f"https://api-inference.huggingface.co/models/{model_id}"
|
| 64 |
-
|
| 65 |
-
API_TOKEN = load_secrets()
|
| 66 |
-
headers = {"Authorization": f"Bearer {API_TOKEN}"}
|
| 67 |
-
|
| 68 |
-
# Show warning if no API token is provided
|
| 69 |
-
if not API_TOKEN:
|
| 70 |
-
st.sidebar.warning("⚠️ No API token found. Using fallback responses. Add your API token in environment variables or create a .streamlit/secrets.toml file.")
|
| 71 |
-
else:
|
| 72 |
-
st.sidebar.success("✅ API token loaded successfully")
|
| 73 |
-
|
| 74 |
-
# Customer support context and guidelines
|
| 75 |
-
SYSTEM_PROMPT = """You are an AI-powered customer support assistant integrated into a company website.
|
| 76 |
-
Your role is to assist customers by responding accurately, politely, and efficiently to their inquiries about
|
| 77 |
-
the company's products, services, policies, and general information.
|
| 78 |
-
|
| 79 |
-
Rules:
|
| 80 |
-
- Answer questions in a professional yet friendly tone
|
| 81 |
-
- Ask for clarification if the user's question is ambiguous
|
| 82 |
-
- Direct users to relevant resources when needed
|
| 83 |
-
- Be available 24/7 and never mention that you're an AI unless explicitly asked
|
| 84 |
-
- Avoid hallucinating facts - if you don't know something, politely let the user know and offer to connect them with a human representative
|
| 85 |
-
- Keep responses concise and to the point
|
| 86 |
-
|
| 87 |
-
Example questions you should be able to handle:
|
| 88 |
-
- "I need help tracking my order"
|
| 89 |
-
- "What is your return policy?"
|
| 90 |
-
- "I'm having trouble logging into my account"
|
| 91 |
-
- "Can I cancel my subscription anytime?"
|
| 92 |
-
- "Do you offer support on weekends?"
|
| 93 |
"""
|
| 94 |
|
| 95 |
# Sample company information and policies
|
|
@@ -99,241 +22,166 @@ COMPANY_INFO = {
|
|
| 99 |
"account_issues": "For account login issues, try resetting your password. If problems persist, please provide your email address so we can investigate.",
|
| 100 |
"subscription": "Yes, you can cancel your subscription at any time through your account settings. There are no cancellation fees.",
|
| 101 |
"support_hours": "Our customer support team is available 24/7 via this chat. For phone support, our hours are 9 AM - 8 PM Monday through Friday, and 10 AM - 6 PM on weekends.",
|
| 102 |
-
"contact": "For complex issues, you can reach our human support team at support@example.com or call 1-800-123-4567."
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
}
|
| 104 |
|
| 105 |
-
#
|
| 106 |
-
|
| 107 |
-
"greeting":
|
| 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 |
-
# Flan-T5 works better with direct questions/instructions
|
| 134 |
-
prompt = f"Answer this customer support question in a helpful way: {user_input}"
|
| 135 |
-
return {"inputs": prompt}
|
| 136 |
-
elif st.session_state.selected_model == "GPT2":
|
| 137 |
-
# Special format for GPT2
|
| 138 |
-
prompt = f"Customer: {user_input}\nSupport agent:"
|
| 139 |
-
return {"inputs": prompt}
|
| 140 |
-
else:
|
| 141 |
-
# For other models that use text completion format
|
| 142 |
-
prompt = SYSTEM_PROMPT + "\n\n"
|
| 143 |
-
for user_msg, bot_msg in history:
|
| 144 |
-
prompt += f"User: {user_msg}\nAssistant: {bot_msg}\n\n"
|
| 145 |
-
prompt += f"User: {user_input}\nAssistant:"
|
| 146 |
-
|
| 147 |
-
return {"inputs": prompt}
|
| 148 |
|
| 149 |
-
def
|
| 150 |
-
"""
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
retry_delay = 2
|
| 154 |
|
| 155 |
-
#
|
| 156 |
-
|
| 157 |
-
if "use_tgi_api" in st.session_state and st.session_state.use_tgi_api:
|
| 158 |
-
use_tgi_api = True
|
| 159 |
-
# The TGI API has a different format
|
| 160 |
-
if st.session_state.selected_model in ["Llama-3-8B-Instruct", "Llama-2-7B-Chat", "Mistral-7B-Instruct"]:
|
| 161 |
-
# Extract just the messages for chat models
|
| 162 |
-
if "inputs" in payload and isinstance(payload["inputs"], list):
|
| 163 |
-
tgi_payload = {
|
| 164 |
-
"inputs": payload["inputs"][-1]["content"],
|
| 165 |
-
"parameters": {
|
| 166 |
-
"max_new_tokens": 256,
|
| 167 |
-
"temperature": 0.7,
|
| 168 |
-
"top_p": 0.95,
|
| 169 |
-
"do_sample": True
|
| 170 |
-
}
|
| 171 |
-
}
|
| 172 |
-
payload = tgi_payload
|
| 173 |
-
api_url = f"https://api-inference.huggingface.co/models/{MODEL_OPTIONS[st.session_state.selected_model]}"
|
| 174 |
|
| 175 |
-
|
| 176 |
-
|
|
|
|
| 177 |
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
response = requests.post(api_url, headers=headers, json=payload, timeout=90)
|
| 181 |
-
|
| 182 |
-
# Check if model is still loading
|
| 183 |
-
if response.status_code == 503:
|
| 184 |
-
st.warning(f"Model is loading. Retrying in {retry_delay} seconds... (Attempt {attempt+1}/{max_retries})")
|
| 185 |
-
time.sleep(retry_delay)
|
| 186 |
-
retry_delay *= 2 # Exponential backoff
|
| 187 |
-
continue
|
| 188 |
-
|
| 189 |
-
# Handle 422 errors specifically - try simplifying the input format
|
| 190 |
-
if response.status_code == 422 and not tried_simple_string:
|
| 191 |
-
st.warning("Model expecting different input format. Trying simpler format...")
|
| 192 |
-
# Convert to simple string input
|
| 193 |
-
if isinstance(payload["inputs"], dict) or isinstance(payload["inputs"], list):
|
| 194 |
-
# Extract just the user's query for simplicity
|
| 195 |
-
payload = {"inputs": user_input}
|
| 196 |
-
tried_simple_string = True
|
| 197 |
-
continue
|
| 198 |
-
|
| 199 |
-
# Handle other errors
|
| 200 |
-
if response.status_code != 200:
|
| 201 |
-
st.error(f"API error: {response.status_code} - {response.text}")
|
| 202 |
-
return {"error": f"API error: {response.status_code} - {response.text}"}
|
| 203 |
-
|
| 204 |
-
return response.json()
|
| 205 |
-
|
| 206 |
-
except Exception as e:
|
| 207 |
-
st.error(f"Error querying API (attempt {attempt+1}/{max_retries}): {e}")
|
| 208 |
-
if attempt < max_retries - 1:
|
| 209 |
-
time.sleep(retry_delay)
|
| 210 |
-
retry_delay *= 2
|
| 211 |
-
else:
|
| 212 |
-
return {"error": str(e)}
|
| 213 |
-
|
| 214 |
-
def extract_response(api_response):
|
| 215 |
-
"""Extract the response text from different model response formats"""
|
| 216 |
-
try:
|
| 217 |
-
if "debug_mode" in st.session_state and st.session_state.debug_mode:
|
| 218 |
-
st.sidebar.subheader("Raw Response:")
|
| 219 |
-
st.sidebar.json(api_response)
|
| 220 |
-
|
| 221 |
-
# Handle list format responses
|
| 222 |
-
if isinstance(api_response, list):
|
| 223 |
-
if len(api_response) > 0:
|
| 224 |
-
# Standard response format for some models
|
| 225 |
-
if "generated_text" in api_response[0]:
|
| 226 |
-
return api_response[0]["generated_text"]
|
| 227 |
-
# TGI API direct text response
|
| 228 |
-
elif isinstance(api_response[0], str):
|
| 229 |
-
return api_response[0]
|
| 230 |
-
|
| 231 |
-
# Handle dictionary format responses
|
| 232 |
-
if isinstance(api_response, dict):
|
| 233 |
-
# For text generation models
|
| 234 |
-
if "generated_text" in api_response:
|
| 235 |
-
return api_response["generated_text"]
|
| 236 |
-
|
| 237 |
-
# For simple string response in a dict
|
| 238 |
-
if "text" in api_response:
|
| 239 |
-
return api_response["text"]
|
| 240 |
-
|
| 241 |
-
# For chat models that return as list array
|
| 242 |
-
if "conversation" in api_response:
|
| 243 |
-
return api_response["conversation"]["messages"][-1]["content"]
|
| 244 |
-
|
| 245 |
-
# For Llama-3/Mistral chat format
|
| 246 |
-
if "outputs" in api_response and len(api_response["outputs"]) > 0:
|
| 247 |
-
return api_response["outputs"][0]["text"]
|
| 248 |
-
|
| 249 |
-
# Fallback: attempt to extract any text or return the response as string
|
| 250 |
-
if isinstance(api_response, str):
|
| 251 |
-
return api_response
|
| 252 |
-
|
| 253 |
-
return str(api_response)
|
| 254 |
-
|
| 255 |
-
except Exception as e:
|
| 256 |
-
st.error(f"Error extracting response: {e}")
|
| 257 |
-
return None
|
| 258 |
-
|
| 259 |
-
def get_fallback_response(user_input):
|
| 260 |
-
"""Get a fallback response based on keywords in the user input"""
|
| 261 |
-
lower_input = user_input.lower()
|
| 262 |
|
| 263 |
-
if
|
| 264 |
-
return
|
| 265 |
-
elif "return" in lower_input:
|
| 266 |
-
return FALLBACK_RESPONSES["return"]
|
| 267 |
-
elif any(word in lower_input for word in ["track", "order", "package", "shipping"]):
|
| 268 |
-
return FALLBACK_RESPONSES["track"]
|
| 269 |
-
elif any(word in lower_input for word in ["login", "sign in", "account", "password"]):
|
| 270 |
-
return FALLBACK_RESPONSES["login"]
|
| 271 |
-
elif "subscription" in lower_input:
|
| 272 |
-
return FALLBACK_RESPONSES["subscription"]
|
| 273 |
-
elif any(word in lower_input for word in ["hours", "available", "weekend"]):
|
| 274 |
-
return FALLBACK_RESPONSES["hours"]
|
| 275 |
-
elif any(word in lower_input for word in ["human", "person", "agent", "representative"]):
|
| 276 |
-
return FALLBACK_RESPONSES["contact"]
|
| 277 |
-
else:
|
| 278 |
-
return FALLBACK_RESPONSES["default"]
|
| 279 |
-
|
| 280 |
-
def get_response(user_input, history):
|
| 281 |
-
"""Get response from the LLM via Hugging Face API or fall back to simple responses"""
|
| 282 |
-
# Use rule-based responses if selected or if API token is missing
|
| 283 |
-
if st.session_state.selected_model == "Rule-Based (No API)" or not API_TOKEN:
|
| 284 |
-
return get_fallback_response(user_input)
|
| 285 |
|
| 286 |
-
#
|
| 287 |
-
|
| 288 |
|
| 289 |
-
#
|
| 290 |
-
|
|
|
|
| 291 |
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
st.sidebar.json(api_response)
|
| 295 |
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
assistant_response = extract_response(api_response)
|
| 299 |
-
if assistant_response:
|
| 300 |
-
return assistant_response
|
| 301 |
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
lower_input = user_input.lower()
|
| 308 |
|
| 309 |
-
if "
|
| 310 |
-
|
| 311 |
-
elif "track" in lower_input and ("order" in lower_input or "package" in lower_input):
|
| 312 |
-
base_response += f"\n\n{COMPANY_INFO['tracking_order']}"
|
| 313 |
-
elif ("login" in lower_input or "logging in" in lower_input or "account" in lower_input) and ("trouble" in lower_input or "problem" in lower_input or "can't" in lower_input):
|
| 314 |
-
base_response += f"\n\n{COMPANY_INFO['account_issues']}"
|
| 315 |
-
elif "cancel" in lower_input and "subscription" in lower_input:
|
| 316 |
-
base_response += f"\n\n{COMPANY_INFO['subscription']}"
|
| 317 |
-
elif "weekend" in lower_input and "support" in lower_input:
|
| 318 |
-
base_response += f"\n\n{COMPANY_INFO['support_hours']}"
|
| 319 |
-
elif "human" in lower_input or "representative" in lower_input or "agent" in lower_input or "person" in lower_input:
|
| 320 |
-
base_response += f"\n\n{COMPANY_INFO['contact']}"
|
| 321 |
|
| 322 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
|
| 324 |
# App title and intro
|
| 325 |
st.title("Customer Support Assistant")
|
| 326 |
st.markdown("Welcome to our customer support chat! How can I help you today?")
|
| 327 |
|
| 328 |
-
# Initialize session state for chat history
|
| 329 |
if "chat_history" not in st.session_state:
|
| 330 |
st.session_state.chat_history = []
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
-
|
| 333 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
|
| 335 |
-
|
| 336 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
|
| 338 |
# Display chat history
|
| 339 |
for user_msg, bot_msg in st.session_state.chat_history:
|
|
@@ -350,103 +198,11 @@ if user_input:
|
|
| 350 |
with st.chat_message("user"):
|
| 351 |
st.write(user_input)
|
| 352 |
|
| 353 |
-
# Get bot response
|
| 354 |
with st.chat_message("assistant", avatar="🧑💼"):
|
| 355 |
-
with st.spinner(
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
bot_response = enhance_response(user_input, base_response)
|
| 359 |
-
st.write(bot_response)
|
| 360 |
-
# Add to chat history
|
| 361 |
-
st.session_state.chat_history.append((user_input, bot_response))
|
| 362 |
-
except Exception as e:
|
| 363 |
-
st.error(f"Sorry, I encountered an error processing your request: {str(e)}")
|
| 364 |
-
|
| 365 |
-
# Sidebar options
|
| 366 |
-
with st.sidebar:
|
| 367 |
-
st.title("Options")
|
| 368 |
-
|
| 369 |
-
# Model selection
|
| 370 |
-
st.markdown("---")
|
| 371 |
-
st.markdown("### Model Settings")
|
| 372 |
-
selected_model = st.selectbox(
|
| 373 |
-
"Select AI Model",
|
| 374 |
-
list(MODEL_OPTIONS.keys()),
|
| 375 |
-
index=list(MODEL_OPTIONS.keys()).index(st.session_state.selected_model)
|
| 376 |
-
)
|
| 377 |
-
|
| 378 |
-
if selected_model != st.session_state.selected_model:
|
| 379 |
-
st.session_state.selected_model = selected_model
|
| 380 |
-
st.success(f"Model changed to {selected_model}")
|
| 381 |
-
|
| 382 |
-
# API mode toggle
|
| 383 |
-
st.checkbox("Use TGI API (try if regular API fails)", key="use_tgi_api",
|
| 384 |
-
help="Use Text Generation Inference API format which works better for some models")
|
| 385 |
-
|
| 386 |
-
# API token input
|
| 387 |
-
st.markdown("---")
|
| 388 |
-
st.markdown("### API Settings")
|
| 389 |
-
manually_entered_token = st.text_input("Enter Hugging Face API Token", type="password", help="Your API token will not be stored permanently")
|
| 390 |
-
if manually_entered_token:
|
| 391 |
-
st.session_state.api_token = manually_entered_token
|
| 392 |
-
API_TOKEN = manually_entered_token
|
| 393 |
-
headers = {"Authorization": f"Bearer {API_TOKEN}"}
|
| 394 |
-
st.success("✅ API Token set for this session")
|
| 395 |
-
|
| 396 |
-
# Test API button
|
| 397 |
-
if st.button("Test API Connection"):
|
| 398 |
-
with st.spinner("Testing API connection..."):
|
| 399 |
-
test_model = "gpt2" # Use a simple model that everyone has access to
|
| 400 |
-
test_url = f"https://api-inference.huggingface.co/models/{test_model}"
|
| 401 |
-
test_payload = {"inputs": "Hello, I'm testing the API connection."}
|
| 402 |
-
try:
|
| 403 |
-
response = requests.post(test_url, headers=headers, json=test_payload, timeout=10)
|
| 404 |
-
if response.status_code == 200:
|
| 405 |
-
st.success("✅ API connection successful! Your token is working correctly.")
|
| 406 |
-
# Update the API token in session
|
| 407 |
-
if not API_TOKEN:
|
| 408 |
-
st.session_state.api_token = manually_entered_token
|
| 409 |
-
API_TOKEN = manually_entered_token
|
| 410 |
-
headers = {"Authorization": f"Bearer {API_TOKEN}"}
|
| 411 |
-
else:
|
| 412 |
-
st.error(f"❌ API Error: {response.status_code} - {response.text}")
|
| 413 |
-
st.info("Please check your API token and try again. Make sure you're using a valid Hugging Face API token.")
|
| 414 |
-
except Exception as e:
|
| 415 |
-
st.error(f"❌ Connection Error: {str(e)}")
|
| 416 |
-
st.info("Please check your internet connection and try again.")
|
| 417 |
-
|
| 418 |
-
# Debug toggle
|
| 419 |
-
st.checkbox("Debug Mode", key="debug_mode", help="Show raw API responses in the sidebar")
|
| 420 |
-
|
| 421 |
-
if st.button("Clear Conversation"):
|
| 422 |
-
st.session_state.chat_history = []
|
| 423 |
-
st.experimental_rerun()
|
| 424 |
-
|
| 425 |
-
# API status indicator
|
| 426 |
-
st.markdown("---")
|
| 427 |
-
if API_TOKEN:
|
| 428 |
-
st.success("✅ API Connected")
|
| 429 |
-
st.info(f"Using model: {st.session_state.selected_model}")
|
| 430 |
-
else:
|
| 431 |
-
st.error("❌ API Not Connected")
|
| 432 |
-
st.info("Add your Hugging Face API token in the field above, in environment variables, or create a .streamlit/secrets.toml file")
|
| 433 |
-
|
| 434 |
-
st.markdown("---")
|
| 435 |
-
st.markdown("### About")
|
| 436 |
-
st.markdown("This customer support chatbot is powered by AI and provides assistance for common customer inquiries.")
|
| 437 |
-
|
| 438 |
-
# Company info in sidebar
|
| 439 |
-
st.markdown("---")
|
| 440 |
-
st.markdown("### Company Info Quick Links")
|
| 441 |
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
if st.button("Order Tracking"):
|
| 446 |
-
st.info(COMPANY_INFO["tracking_order"])
|
| 447 |
-
|
| 448 |
-
if st.button("Support Hours"):
|
| 449 |
-
st.info(COMPANY_INFO["support_hours"])
|
| 450 |
-
|
| 451 |
-
if st.button("Contact Information"):
|
| 452 |
-
st.info(COMPANY_INFO["contact"])
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
import re
|
| 3 |
+
import random
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
# Page configuration
|
| 6 |
st.set_page_config(
|
|
|
|
| 9 |
layout="centered"
|
| 10 |
)
|
| 11 |
|
| 12 |
+
# Customer support context
|
| 13 |
+
SYSTEM_INFO = """
|
| 14 |
+
This is a customer support assistant that helps with common inquiries about products, services,
|
| 15 |
+
policies, and general information. It provides accurate and helpful responses to assist customers.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
"""
|
| 17 |
|
| 18 |
# Sample company information and policies
|
|
|
|
| 22 |
"account_issues": "For account login issues, try resetting your password. If problems persist, please provide your email address so we can investigate.",
|
| 23 |
"subscription": "Yes, you can cancel your subscription at any time through your account settings. There are no cancellation fees.",
|
| 24 |
"support_hours": "Our customer support team is available 24/7 via this chat. For phone support, our hours are 9 AM - 8 PM Monday through Friday, and 10 AM - 6 PM on weekends.",
|
| 25 |
+
"contact": "For complex issues, you can reach our human support team at support@example.com or call 1-800-123-4567.",
|
| 26 |
+
"shipping": "Standard shipping takes 3-5 business days. Express shipping takes 1-2 business days. International shipping may take 7-14 business days.",
|
| 27 |
+
"payment": "We accept all major credit cards, PayPal, and Apple Pay. Payment information is securely processed and encrypted.",
|
| 28 |
+
"refund": "Refunds are processed within 5-7 business days of receiving your returned item. The refund will be issued to the original payment method.",
|
| 29 |
+
"warranty": "Our products come with a standard 1-year warranty that covers manufacturing defects. Extended warranties are available for purchase."
|
| 30 |
}
|
| 31 |
|
| 32 |
+
# Response templates with variations for more natural conversation
|
| 33 |
+
RESPONSE_TEMPLATES = {
|
| 34 |
+
"greeting": [
|
| 35 |
+
"Hello! Welcome to our customer support. How can I assist you today?",
|
| 36 |
+
"Hi there! I'm here to help with any questions you might have. What can I do for you?",
|
| 37 |
+
"Welcome! How may I assist you with our products or services today?"
|
| 38 |
+
],
|
| 39 |
+
"thanks": [
|
| 40 |
+
"You're welcome! Is there anything else I can help you with?",
|
| 41 |
+
"Happy to help! Do you have any other questions I can assist with?",
|
| 42 |
+
"My pleasure! Is there something else you'd like to know?"
|
| 43 |
+
],
|
| 44 |
+
"goodbye": [
|
| 45 |
+
"Thank you for contacting our support. Have a great day!",
|
| 46 |
+
"I appreciate your time today. If you need anything else, don't hesitate to reach out!",
|
| 47 |
+
"Thanks for chatting with us. Feel free to return if you have any other questions!"
|
| 48 |
+
],
|
| 49 |
+
"unclear": [
|
| 50 |
+
"I'm not sure I understand your question. Could you please provide more details?",
|
| 51 |
+
"I'd like to help, but I need a bit more information. Could you clarify what you're asking about?",
|
| 52 |
+
"I'm sorry, but I don't have enough information to answer that. Could you be more specific?"
|
| 53 |
+
],
|
| 54 |
+
"human": [
|
| 55 |
+
f"I understand you'd like to speak with a human representative. {COMPANY_INFO['contact']}",
|
| 56 |
+
f"For more complex issues, our human support team is available. {COMPANY_INFO['contact']}",
|
| 57 |
+
f"I'd be happy to connect you with one of our human agents. {COMPANY_INFO['contact']}"
|
| 58 |
+
]
|
| 59 |
}
|
| 60 |
|
| 61 |
+
# Patterns for matching different types of queries
|
| 62 |
+
PATTERNS = {
|
| 63 |
+
"greeting": r"\b(hi|hello|hey|good morning|good afternoon|good evening)\b",
|
| 64 |
+
"thanks": r"\b(thanks|thank you|appreciate it)\b",
|
| 65 |
+
"goodbye": r"\b(bye|goodbye|see you|farewell)\b",
|
| 66 |
+
"return_policy": r"\b(return|return policy|send back|give back|exchange|bring back|refund policy)\b",
|
| 67 |
+
"tracking": r"\b(track|track order|tracking|shipping status|where is|delivery status|package|when.*arrive|where.*order)\b",
|
| 68 |
+
"account": r"\b(account|log in|login|sign in|password|forgot password|reset password|username|can\'?t log in|trouble logging in)\b",
|
| 69 |
+
"subscription": r"\b(subscription|cancel|cancellation|unsubscribe|stop.*subscription|end.*subscription|pause)\b",
|
| 70 |
+
"hours": r"\b(hours|time|availability|when are you|schedule|open|closed|available|support hours|customer service hours)\b",
|
| 71 |
+
"human": r"\b(human|person|agent|representative|speak to someone|talk to someone|real person|live agent|customer service rep)\b",
|
| 72 |
+
"shipping": r"\b(shipping|delivery|ship|deliver|how long|how soon|when will it arrive|shipping time|delivery time|express shipping)\b",
|
| 73 |
+
"payment": r"\b(payment|pay|credit card|debit card|paypal|apple pay|billing|payment method|transaction|purchase|buying)\b",
|
| 74 |
+
"refund": r"\b(refund|money back|reimburse|reimbursement|get.*money back|return.*money|credit back|refund process)\b",
|
| 75 |
+
"warranty": r"\b(warranty|guarantee|broken|defective|repair|fix|not working|doesn\'?t work|stopped working|damaged|replacement)\b"
|
| 76 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
+
def get_response(user_input):
|
| 79 |
+
"""Generate a response based on the user's input"""
|
| 80 |
+
if not user_input or user_input.strip() == "":
|
| 81 |
+
return "Hello! How can I help you today? Feel free to ask about our return policy, shipping, account help, or any other questions."
|
|
|
|
| 82 |
|
| 83 |
+
# Convert input to lowercase for easier matching
|
| 84 |
+
input_lower = user_input.lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
+
# Check for greetings, thanks, or goodbyes first
|
| 87 |
+
if re.search(PATTERNS["greeting"], input_lower):
|
| 88 |
+
return random.choice(RESPONSE_TEMPLATES["greeting"])
|
| 89 |
|
| 90 |
+
if re.search(PATTERNS["thanks"], input_lower):
|
| 91 |
+
return random.choice(RESPONSE_TEMPLATES["thanks"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
+
if re.search(PATTERNS["goodbye"], input_lower):
|
| 94 |
+
return random.choice(RESPONSE_TEMPLATES["goodbye"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
# Store all matching responses and pick the most relevant
|
| 97 |
+
matched_responses = []
|
| 98 |
|
| 99 |
+
# Check for specific product/policy queries
|
| 100 |
+
if re.search(PATTERNS["return_policy"], input_lower):
|
| 101 |
+
matched_responses.append(f"Here's our return policy: {COMPANY_INFO['return_policy']}")
|
| 102 |
|
| 103 |
+
if re.search(PATTERNS["tracking"], input_lower):
|
| 104 |
+
matched_responses.append(f"To track your order: {COMPANY_INFO['tracking_order']}")
|
|
|
|
| 105 |
|
| 106 |
+
if re.search(PATTERNS["account"], input_lower):
|
| 107 |
+
matched_responses.append(f"For account-related issues: {COMPANY_INFO['account_issues']}")
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
+
if re.search(PATTERNS["subscription"], input_lower):
|
| 110 |
+
matched_responses.append(f"Regarding subscriptions: {COMPANY_INFO['subscription']}")
|
| 111 |
+
|
| 112 |
+
if re.search(PATTERNS["hours"], input_lower):
|
| 113 |
+
matched_responses.append(f"Our support hours are: {COMPANY_INFO['support_hours']}")
|
|
|
|
| 114 |
|
| 115 |
+
if re.search(PATTERNS["shipping"], input_lower):
|
| 116 |
+
matched_responses.append(f"Shipping information: {COMPANY_INFO['shipping']}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
+
if re.search(PATTERNS["payment"], input_lower):
|
| 119 |
+
matched_responses.append(f"Payment methods: {COMPANY_INFO['payment']}")
|
| 120 |
+
|
| 121 |
+
if re.search(PATTERNS["refund"], input_lower):
|
| 122 |
+
matched_responses.append(f"Our refund process: {COMPANY_INFO['refund']}")
|
| 123 |
+
|
| 124 |
+
if re.search(PATTERNS["warranty"], input_lower):
|
| 125 |
+
matched_responses.append(f"Warranty information: {COMPANY_INFO['warranty']}")
|
| 126 |
+
|
| 127 |
+
if re.search(PATTERNS["human"], input_lower):
|
| 128 |
+
matched_responses.append(random.choice(RESPONSE_TEMPLATES["human"]))
|
| 129 |
+
|
| 130 |
+
# Return a matched response if any were found
|
| 131 |
+
if matched_responses:
|
| 132 |
+
return matched_responses[0]
|
| 133 |
+
|
| 134 |
+
# Try broader keyword matching if no specific pattern matched
|
| 135 |
+
for key, info in COMPANY_INFO.items():
|
| 136 |
+
keywords = key.replace("_", " ").split()
|
| 137 |
+
for word in keywords:
|
| 138 |
+
if word in input_lower and len(word) > 3: # Only match significant words
|
| 139 |
+
return f"Regarding {key.replace('_', ' ')}: {info}"
|
| 140 |
+
|
| 141 |
+
# Check if asking a general question
|
| 142 |
+
if any(q in input_lower for q in ["what", "how", "when", "where", "why", "who", "can", "do", "is", "are"]):
|
| 143 |
+
general_response = "Based on your question, you might be interested in one of these topics:\n"
|
| 144 |
+
general_response += "• Return policy\n• Order tracking\n• Account issues\n• Subscriptions\n"
|
| 145 |
+
general_response += "• Support hours\n• Shipping information\n• Payment methods\n• Refunds\n• Warranty"
|
| 146 |
+
return general_response
|
| 147 |
+
|
| 148 |
+
# If no specific pattern is matched, respond with a general answer or ask for clarification
|
| 149 |
+
return random.choice(RESPONSE_TEMPLATES["unclear"])
|
| 150 |
|
| 151 |
# App title and intro
|
| 152 |
st.title("Customer Support Assistant")
|
| 153 |
st.markdown("Welcome to our customer support chat! How can I help you today?")
|
| 154 |
|
| 155 |
+
# Initialize session state for chat history
|
| 156 |
if "chat_history" not in st.session_state:
|
| 157 |
st.session_state.chat_history = []
|
| 158 |
+
|
| 159 |
+
# Sidebar with information and options
|
| 160 |
+
with st.sidebar:
|
| 161 |
+
st.title("Customer Support")
|
| 162 |
|
| 163 |
+
st.markdown("### About")
|
| 164 |
+
st.info(SYSTEM_INFO)
|
| 165 |
+
|
| 166 |
+
if st.button("Clear Conversation"):
|
| 167 |
+
st.session_state.chat_history = []
|
| 168 |
+
st.rerun()
|
| 169 |
|
| 170 |
+
# Company info quick links
|
| 171 |
+
st.markdown("---")
|
| 172 |
+
st.markdown("### Quick Information")
|
| 173 |
+
|
| 174 |
+
if st.button("Return Policy"):
|
| 175 |
+
st.info(COMPANY_INFO["return_policy"])
|
| 176 |
+
|
| 177 |
+
if st.button("Shipping Info"):
|
| 178 |
+
st.info(COMPANY_INFO["shipping"])
|
| 179 |
+
|
| 180 |
+
if st.button("Support Hours"):
|
| 181 |
+
st.info(COMPANY_INFO["support_hours"])
|
| 182 |
+
|
| 183 |
+
if st.button("Contact Information"):
|
| 184 |
+
st.info(COMPANY_INFO["contact"])
|
| 185 |
|
| 186 |
# Display chat history
|
| 187 |
for user_msg, bot_msg in st.session_state.chat_history:
|
|
|
|
| 198 |
with st.chat_message("user"):
|
| 199 |
st.write(user_input)
|
| 200 |
|
| 201 |
+
# Get bot response
|
| 202 |
with st.chat_message("assistant", avatar="🧑💼"):
|
| 203 |
+
with st.spinner("Thinking..."):
|
| 204 |
+
bot_response = get_response(user_input)
|
| 205 |
+
st.write(bot_response)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
+
# Add to chat history
|
| 208 |
+
st.session_state.chat_history.append((user_input, bot_response))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|