krinya/smart_routing_with_render_example
0
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 