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 += "" 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"" html += "
IDNameBusinessBudgetEmailScoreCreated
{lead['id'][:10]}...{name}{business}{budget}{email}{score}{created}
" 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 += "" 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"" html += "
IDNameIssueReferenceStatusCreated
{ticket['id'][:10]}...{name}{issue}{ref_id}{status}{created}
" 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 += "" 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"" html += "
IDIntentMessagesTimestamp
{conv['id'][:10]}...{intent}{msg_count}{timestamp}
" 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() )