import os
import gradio as gr
import json
from datetime import datetime
from backend.conversation_engine import ConversationEngine
from backend.storage import Storage
# Global conversation engine instance
engine = ConversationEngine()
storage = Storage("data")
def chat_interface(message, history):
"""
Main chat interface handler compatible with Gradio 6.x.
Args:
message: User's input message
history: Chat history list of message dicts
Returns:
Updated chat history with new bot response
"""
if not message.strip():
return history
# Process message through engine
response, metadata = engine.process_message(message)
# Return in Gradio 6.x format: list of {"role": "user"/"assistant", "content": "..."}
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": response}]
def new_conversation():
"""Reset conversation for new session."""
engine.reset()
return [], "â
Conversation reset. Start fresh!"
def get_admin_leads_view():
"""Get HTML view of all leads."""
leads = storage.get_all_leads()
if not leads:
return "
No leads yet.
"
html = ""
html += "| ID | Name | Business | Budget | Email | Score | Created |
"
for lead in reversed(leads[-20:]): # Show last 20
name = lead.get("user_name", "N/A")
business = lead.get("business_type", "N/A")[:30]
budget = lead.get("budget", "N/A")[:20]
email = lead.get("email", "N/A")
score_info = lead.get("scoring", {})
score = f"{score_info.get('score', 0)} ({score_info.get('classification', 'Unknown')})"
created = lead.get("created_at", "N/A").split("T")[0]
html += f"| {lead['id'][:10]}... | {name} | {business} | {budget} | {email} | {score} | {created} |
"
html += "
"
return html
def get_admin_tickets_view():
"""Get HTML view of all support tickets."""
tickets = storage.get_all_tickets()
if not tickets:
return "No support tickets yet.
"
html = ""
html += "| ID | Name | Issue | Reference | Status | Created |
"
for ticket in reversed(tickets[-20:]): # Show last 20
name = ticket.get("user_name", "N/A")
issue = ticket.get("issue_description", "N/A")[:50]
ref_id = ticket.get("reference_id", "N/A")[:20]
status = ticket.get("status", "N/A")
created = ticket.get("created_at", "N/A").split("T")[0]
html += f"| {ticket['id'][:10]}... | {name} | {issue} | {ref_id} | {status} | {created} |
"
html += "
"
return html
def get_admin_logs_view():
"""Get HTML view of conversation logs summary."""
convs = storage.get_all_conversations()
summary = storage.get_summary()
html = f"""
System Summary
Total Conversations: {summary['total_conversations']}
Total Leads: {summary['total_leads']}
Total Support Tickets: {summary['total_tickets']}
Recent Conversations
"""
if not convs:
html += "No conversations logged yet.
"
else:
html += ""
html += "| ID | Intent | Messages | Timestamp |
"
for conv in reversed(convs[-20:]): # Show last 20
intent = conv.get("intent", "unknown")
msg_count = len(conv.get("messages", []))
timestamp = conv.get("timestamp", "N/A").split("T")[0]
html += f"| {conv['id'][:10]}... | {intent} | {msg_count} | {timestamp} |
"
html += "
"
return html
def export_leads_json():
"""Export leads as JSON."""
leads = storage.get_all_leads()
return json.dumps(leads, indent=2)
def export_tickets_json():
"""Export tickets as JSON."""
tickets = storage.get_all_tickets()
return json.dumps(tickets, indent=2)
# Create Gradio interface (theme parameter moved to launch() in Gradio 6.x)
with gr.Blocks(title="AI Support Chatbot") as demo:
gr.Markdown("# đ¤ AI Customer Support & Lead Qualification Chatbot")
gr.Markdown("Ask questions, qualify for services, or report issues. The AI will route your request appropriately.")
with gr.Tabs():
# Tab 1: Chat Interface
with gr.Tab("đŦ Chat"):
with gr.Column():
chatbot = gr.Chatbot(label="Conversation", height=500)
with gr.Row():
msg = gr.Textbox(
label="Your Message",
placeholder="Type your message here...",
lines=2
)
submit = gr.Button("Send", size="lg", variant="primary")
with gr.Row():
new_chat_btn = gr.Button("đ New Conversation", size="sm")
clear_history = gr.Button("đī¸ Clear Chat", size="sm")
# Info box
gr.Markdown("""
### How to use:
- **Lead Inquiry**: Ask about pricing, services, or your business problem
- **Support Issue**: Describe any technical problems or issues
- **General Questions**: Ask about our company and services
The AI will detect your intent and respond accordingly!
""")
# Tab 2: Admin Dashboard
with gr.Tab("đ Admin Panel"):
gr.Markdown("## Admin Dashboard")
with gr.Tabs():
with gr.Tab("đĨ Leads"):
leads_html = gr.HTML()
refresh_leads = gr.Button("đ Refresh Leads")
refresh_leads.click(get_admin_leads_view, outputs=leads_html)
leads_html.load(get_admin_leads_view, outputs=leads_html)
with gr.Tab("đī¸ Support Tickets"):
tickets_html = gr.HTML()
refresh_tickets = gr.Button("đ Refresh Tickets")
refresh_tickets.click(get_admin_tickets_view, outputs=tickets_html)
tickets_html.load(get_admin_tickets_view, outputs=tickets_html)
with gr.Tab("đ Logs & Summary"):
logs_html = gr.HTML()
refresh_logs = gr.Button("đ Refresh Logs")
refresh_logs.click(get_admin_logs_view, outputs=logs_html)
logs_html.load(get_admin_logs_view, outputs=logs_html)
with gr.Tab("đĨ Export"):
gr.Markdown("### Export Data")
with gr.Row():
with gr.Column():
gr.Markdown("**Leads Export**")
export_leads_btn = gr.Button("Export Leads as JSON")
leads_json = gr.Textbox(label="Leads JSON", lines=10, interactive=False)
export_leads_btn.click(export_leads_json, outputs=leads_json)
with gr.Column():
gr.Markdown("**Support Tickets Export**")
export_tickets_btn = gr.Button("Export Tickets as JSON")
tickets_json = gr.Textbox(label="Tickets JSON", lines=10, interactive=False)
export_tickets_btn.click(export_tickets_json, outputs=tickets_json)
# Tab 3: About
with gr.Tab("âšī¸ About"):
gr.Markdown("""
# About This Chatbot
## Features
- **Intent Detection**: Automatically classifies user messages (lead, support, general)
- **Multi-Flow Routing**: Routes conversations based on detected intent
- **Lead Qualification**: Captures lead information with automatic scoring
- **Support Ticketing**: Creates support tickets with conversation context
- **Smart Memory**: Maintains conversation context (last 10 messages)
- **FAQ Retrieval**: Answers common questions from knowledge base
- **Hallucination Control**: Grounds responses and avoids making up information
- **Lead Scoring**: Rates leads as Hot/Warm/Cold based on engagement signals
## Technology Stack
- **Frontend**: Gradio (Python)
- **Backend**: Python with modular architecture
- **AI**: Google Gemini 2.5 Flash API
- **Storage**: JSON files
## Designed for Hugging Face Spaces
This application is built to run directly on Hugging Face Spaces without additional infrastructure.
---
Built with â¤ī¸ for customer support & lead qualification
""")
# Event handlers
def handle_chat_submit(message, history):
"""Handle chat message submission and save to log."""
new_history = chat_interface(message, history)
# Save to conversation log
if message.strip():
try:
storage.save_conversation({
"intent": engine.current_intent,
"user_name": engine.memory.user_name,
"message_count": len(new_history),
"timestamp": datetime.now().isoformat()
})
except:
pass # Silently fail if logging fails
return new_history, ""
submit.click(handle_chat_submit, [msg, chatbot], [chatbot, msg])
msg.submit(handle_chat_submit, [msg, chatbot], [chatbot, msg])
new_chat_btn.click(new_conversation, outputs=[chatbot, gr.State()])
clear_history.click(lambda: [], outputs=chatbot)
if __name__ == "__main__":
# Check for API key
if not os.getenv("GEMINI_API_KEY"):
print("â ī¸ WARNING: GEMINI_API_KEY not set. The chatbot will use pattern-based fallback.")
print("Set the GEMINI_API_KEY environment variable to enable AI-powered responses.")
# Launch the app (theme parameter for Gradio 6.x)
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
theme=gr.themes.Soft()
)