CoolFace
Apppublic

krinya/smart_routing_with_render_example

sourceHugging Facemitupdated 1y agoView on Hugging Face
0likes
app.py369 linesDownload Raw Back to root
1"""2Financial AI Chatbot with Smart Routing & RAG - Gradio Frontend3 4This Gradio application demonstrates a complete GenAI product development workflow,5showcasing smart routing capabilities of an AI chatbot for financial Q&A based on financial reports.6 7Key Features:8- Smart routing between FAQ, RAG, and LLM responses9- Real-time routing insights and answer quality scoring10- Production-ready architecture with separated backend/frontend11- Interactive examples for different routing scenarios12 13Backend API: Deployed on Render with FastAPI + LangChain14Frontend UI: This Gradio interface deployed on Hugging Face Spaces15Data: 2024 financial reports from 5 major companies (Apple, Google, Amazon, Tesla, Intel)16 17For complete technical details and implementation guide, see:18https://huggingface.co/spaces/krinya/smart_routing_with_render_example/blob/main/README.md19"""20 21import gradio as gr22import requests23import uuid24from datetime import datetime25from typing import Dict, List, Tuple, Optional26import time27 28API_BASE_URL = "https://gen-ai-demo-rag-bot.onrender.com"29CHAT_ENDPOINT = f"{API_BASE_URL}/chat"30HEALTH_ENDPOINT = f"{API_BASE_URL}/health"31DOCS_ENDPOINT = f"{API_BASE_URL}/docs"32 33EXAMPLE_QUERIES = {34    "FAQ": "Who is the CEO of Tesla?",35    "RAG": "What was Apple's revenue in 2024?", 36    "LLM": "How do you calculate price-to-earnings ratio?"37}38 39ROUTING_COLORS = {40    "faq": "๐Ÿ” #4CAF50",41    "rag": "๐Ÿ“š #2196F3",42    "llm": "๐Ÿง  #FF9800",43    "general": "๐Ÿ’ญ #9E9E9E"44}45 46def check_api_health(retries: int = 6, timeout_secs: int = 20, backoff_secs: int = 3) -> Tuple[bool, str]:47    """Check if the API is accessible.48 49    Uses a small retry loop with exponential-ish backoff to tolerate cold starts50    (Render free tier can take a while on the first request). Returns a51    (bool, message) tuple where bool indicates healthy.52    """53    last_err = None54    for attempt in range(1, retries + 1):55        try:56            response = requests.get(HEALTH_ENDPOINT, timeout=timeout_secs)57            if response.status_code == 200:58                return True, "API is online and healthy"59            else:60                return False, (61                    f"API returned status {response.status_code}. "62                    "The free Render API may take up to 1 minute to start on the first request, check the status on: {DOCS_ENDPOINT}. "63                    "Please wait a minute and try again."64                )65        except requests.exceptions.RequestException as e:66            last_err = e67            if attempt < retries:68                time.sleep(backoff_secs * attempt)69                continue70            return False, (71                f"โŒ Cannot connect to API: {str(last_err)}. "72                "The free Render API may take up to 1 minute to start on the first request. , check the status on: {DOCS_ENDPOINT}. "73                "Please wait a minute and try again."74            )75 76def send_message_to_api(message: str, session_id: str) -> Dict:77    """Send message to the chatbot API"""78    try:79        payload = {80            "message": message,81            "session_id": session_id82        }83        response = requests.post(84            CHAT_ENDPOINT,85            json=payload,86            headers={"Content-Type": "application/json"},87            timeout=15088        )89        if response.status_code == 200:90            return response.json()91        else:92            return {93                "error": f"API Error {response.status_code}: {response.text}",94                "response": "Sorry, I'm having trouble connecting to the server right now."95            }96    except requests.exceptions.Timeout:97        return {98            "error": "Request timeout",99            "response": "Sorry, the request took too long. Please try again."100        }101    except requests.exceptions.RequestException as e:102        return {103            "error": f"Connection error: {str(e)}",104            "response": "Sorry, I can't connect to the server right now."105        }106 107def format_routing_info(routing_data: Dict) -> str:108    """Format routing information for display"""109    if not routing_data:110        return "No routing information available"111    112    primary_route = routing_data.get('primary_route', 'unknown')113    answer_quality = routing_data.get('answer_quality', 'unknown')114    color_info = ROUTING_COLORS.get(primary_route.lower(), ROUTING_COLORS['general'])115    icon, color = color_info.split(' ')116    117    info_lines = [118        f"{icon} **Route:** {primary_route.upper()}",119        f"โญ **Quality:** {answer_quality.title()}"120    ]121    122    rephrase_attempts = routing_data.get('rephrase_attempts', 0)123    if rephrase_attempts > 0:124        info_lines.append(f"๐Ÿ”„ **Rephrase attempts:** {rephrase_attempts}")125    126    failed_sources = routing_data.get('failed_sources', [])127    if failed_sources:128        info_lines.append(f"โš ๏ธ **Failed sources:** {', '.join(failed_sources)}")129    130    return "\n\n".join(info_lines)131 132def format_chat_message(message: str, is_user: bool, routing_info: Optional[Dict] = None) -> str:133    timestamp = datetime.now().strftime("%H:%M")134    if is_user:135        return f"**๐Ÿ‘ค You** *({timestamp})*\n{message}"136    else:137        route_indicator = ""138        if routing_info:139            primary_route = routing_info.get('primary_route', 'general').lower()140            color_info = ROUTING_COLORS.get(primary_route, ROUTING_COLORS['general'])141            icon = color_info.split(' ')[0]142            route_indicator = f" {icon}"143        return f"**๐Ÿค– Assistant{route_indicator}** *({timestamp})*\n{message}"144 145def chat_with_bot(message: str, history: List[Dict[str, str]], session_id: str, show_routing: bool) -> Tuple[List[Dict[str, str]], str, str, str]:146    """Main chat function"""147    if not message.strip():148        return history, "", "", session_id149    150    # Send message to API with persistent session ID151    api_response = send_message_to_api(message, session_id)152    153    # Extract response and routing info154    bot_response = api_response.get('response', 'Sorry, I encountered an error.')155    metadata = api_response.get('metadata', {})156    routing_info = metadata.get('routing_info', {})157    158    # Format routing information159    routing_display = ""160    if show_routing and routing_info:161        routing_display = format_routing_info(routing_info)162    163    # Add to chat history using messages format164    history.append({"role": "user", "content": message})165    history.append({"role": "assistant", "content": bot_response})166    167    return history, "", routing_display, session_id168 169def load_example(example_text: str) -> str:170    """Load an example query into the input box"""171    return example_text172 173def create_gradio_interface():174    """Create and configure the Gradio interface"""175    176    # Check API health at startup177    is_healthy, health_status = check_api_health()178    179    with gr.Blocks(180        title="AI Chatbot with Smart Routing",181        theme=gr.themes.Default(primary_hue="blue", secondary_hue="purple")182    ) as interface:183        184        # Header185        gr.Markdown("""186        # ๐Ÿค– Financial AI Chatbot with Smart Routing & RAG187        188        **A demo GenAI app that demonstrates smart routing using LangChain - showing how to create a complete GenAI product**189        190        ## ๐ŸŽฏ What This Demonstrates191        192        This project showcases **a GenAI development workflow** from backend to frontend deployment we created an API running on Render and a frontend UI using Gradio on Hugging Face Spaces.:193        194        ### ๐Ÿง  Smart Routing with LangChain195        Intelligently routes financial questions about **5 major companies** (Apple, Google, Amazon, Tesla, Intel):196        - ๐Ÿ” **FAQ Route**: Quick facts (CEO names, founding dates, basic company info)197        - ๐Ÿ“š **RAG Route**: Detailed financial data from 2024 annual reports (revenue, profits, growth metrics)  198        - ๐Ÿง  **LLM Route**: General explanations and complex financial concepts199        200        ### ๐Ÿ“Š RAG Implementation201        - **Vector Storage**: ChromaDB with processed financial documents (full annual reports)202        - **Retrieval System**: Semantic search for relevant information203        - **Smart Fallbacks**: Multiple sources with quality scoring204        205        ### ๐Ÿ—๏ธ Backend and Frontend Architecture206        - **Backend**: Python FastAPI with LangChain, deployed on Render207        - **Frontend**: Gradio UI deployed on Hugging Face Spaces using Docker containerization208        - **Separation**: Backend API + Frontend UI209        210        **๐Ÿ”ง Tech Stack**: OpenAI GPT-5-mini + LangChain orchestration, Python FastAPI, ChromaDB vector database, Docker containerization211        212        **๐Ÿ“– Learn More**: [README with technical details](https://huggingface.co/spaces/krinya/smart_routing_with_render_example/blob/main/README.md)  213        **๐Ÿ’ป Backend API Code**: [GitHub Repository](https://github.com/krinya/gen_ai_demo_rag_bot/tree/main)214        """)215        216        # Workflow Architecture Diagram217        gr.Markdown("### ๐Ÿ“Š Chatbot Workflow Architecture")218        gr.Image(219            value="chatbot_workflow_graph.png",220            label="Chatbot Workflow Architecture Diagram",221            show_label=True,222            container=True,223            height=400,224            width=800,225            interactive=False226        )227        228        # API Health Status229        with gr.Row():230            if is_healthy:231                gr.Markdown(f"โœ… **Status**: {health_status}", container=True)232            else:233                gr.Markdown(f"โŒ **Status**: {health_status}", container=True)234        235        # Hidden session ID state (persistent across interactions)236        session_state = gr.State(value=str(uuid.uuid4()))237        238        # Chat interface (full width)239        chatbot = gr.Chatbot(240            value=[],241            label="Chat History",242            height=500,243            show_label=True,244            type="messages",245            latex_delimiters=[246                {"left": "$$", "right": "$$", "display": True},247                {"left": "\\[", "right": "\\]", "display": True},248                {"left": "\\(", "right": "\\)", "display": False}249            ]250        )251        252        with gr.Row():253            msg_input = gr.Textbox(254                placeholder="Ask about Apple, Google, Amazon, Tesla, or Intel financials...",255                label="Your Financial Question",256                scale=4,257                lines=1258            )259            send_btn = gr.Button("Send ๐Ÿ“ค", scale=1, variant="primary")260        261        # Example queries below chat interface262        gr.Markdown("### ๐Ÿ’ก Try These Examples")263        264        with gr.Row():265            for route_type, example in EXAMPLE_QUERIES.items():266                color_info = ROUTING_COLORS.get(route_type.lower(), ROUTING_COLORS['general'])267                icon, color = color_info.split(' ')268                269                example_btn = gr.Button(270                    f"{icon} {example}",271                    size="sm"272                )273                example_btn.click(274                    fn=load_example,275                    inputs=[gr.State(example)],276                    outputs=[msg_input]277                )278        279        # Settings and controls280        with gr.Row():281            show_routing = gr.Checkbox(282                value=True,283                label="Show routing insights",284                info="Display how the AI routes your questions"285            )286            clear_btn = gr.Button("๐Ÿ—‘๏ธ Clear Chat", variant="secondary")287            new_session_btn = gr.Button("๐Ÿ”„ New Session", variant="secondary")288            session_indicator = gr.Markdown("๐Ÿ’พ **Memory Active** - I'll remember our conversation")289        290        # Routing insights at the bottom291        routing_info = gr.Markdown(292            value="*Routing information will appear here after sending a message*",293            label="๐Ÿงญ Routing Insights"294        )295        296        # Footer with deployment info297        gr.Markdown("""298        ---299        **๐Ÿš€ Deployment Info**: This prototype is powered by a FastAPI backend deployed on [Render](https://render.com), 300        showcasing full-stack development knowledge.301        302        **๐Ÿ› ๏ธ Tech Stack**: LangChain โ€ข OpenAI GPT-5-mini โ€ข ChromaDB โ€ข FastAPI โ€ข Render โ€ข Gradio โ€ข Hugging Face Spaces โ€ข CI/CD303        """)304        305        # Event handlers306        def clear_chat():307            return [], ""308        309        def new_session():310            return str(uuid.uuid4()), [], ""311        312        # Button click events313        clear_btn.click(314            fn=clear_chat,315            outputs=[chatbot, routing_info]316        )317        318        new_session_btn.click(319            fn=new_session,320            outputs=[session_state, chatbot, routing_info]321        )322        323        # Chat submission events with loading324        def chat_wrapper(message, history, session_id, show_routing):325            # Show loading message326            if message.strip():327                # Add user message and loading response immediately328                loading_history = history + [329                    {"role": "user", "content": message},330                    {"role": "assistant", "content": "๐Ÿค” Thinking... be patient, free servers are slow."}331                ]332                yield loading_history, "", "๐Ÿ”„ Processing your message...", session_id333                334                # Get actual response335                result_history, empty_input, routing_info, updated_session = chat_with_bot(message, history, session_id, show_routing)336                yield result_history, "", routing_info, updated_session337            else:338                yield history, "", "", session_id339        340        send_btn.click(341            fn=chat_wrapper,342            inputs=[msg_input, chatbot, session_state, show_routing],343            outputs=[chatbot, msg_input, routing_info, session_state]344        )345        346        msg_input.submit(347            fn=chat_wrapper,348            inputs=[msg_input, chatbot, session_state, show_routing],349            outputs=[chatbot, msg_input, routing_info, session_state]350        )351        352    return interface353 354if __name__ == "__main__":355    # Create and launch the interface356    interface = create_gradio_interface()357    358    print("๐Ÿš€ Starting Gradio Chat Interface...")359    print(f"๐Ÿ”— API Endpoint: {API_BASE_URL}")360    # Launch with Hugging Face Spaces configuration361    interface.launch(362        server_name="0.0.0.0", 363        server_port=7860,364        share=False,            365        show_error=True,366        favicon_path='robot_favicon.png',367        auth=None368    )369