Refactor code structure for improved readability and maintainability
Browse files- .gitignore +47 -0
- Dockerfile +29 -0
- README.md +72 -4
- app.py +368 -0
- chatbot_workflow_graph.png +0 -0
- pyproject.toml +20 -0
- requirements.txt +2 -0
- robot_favicon.png +0 -0
- uv.lock +0 -0
.gitignore
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.env
|
| 2 |
+
# Always ignore Python cache files
|
| 3 |
+
__pycache__/
|
| 4 |
+
**/__pycache__/
|
| 5 |
+
*.pyc
|
| 6 |
+
*.pyo
|
| 7 |
+
*.pyd
|
| 8 |
+
.Python
|
| 9 |
+
# SQLite and DB files
|
| 10 |
+
*.db
|
| 11 |
+
*.sqlite
|
| 12 |
+
# Output files
|
| 13 |
+
outputs/*.txt
|
| 14 |
+
outputs/*.json
|
| 15 |
+
# Log files
|
| 16 |
+
*.log
|
| 17 |
+
# IDE and OS files
|
| 18 |
+
.vscode/
|
| 19 |
+
.idea/
|
| 20 |
+
.DS_Store
|
| 21 |
+
# Jupyter Notebook checkpoints
|
| 22 |
+
.ipynb_checkpoints/
|
| 23 |
+
# Virtual environments
|
| 24 |
+
.venv/
|
| 25 |
+
venv/
|
| 26 |
+
# MacOS system files
|
| 27 |
+
.AppleDouble
|
| 28 |
+
.LSOverride
|
| 29 |
+
# Test and coverage outputs
|
| 30 |
+
htmlcov/
|
| 31 |
+
.coverage
|
| 32 |
+
.mypy_cache/
|
| 33 |
+
.pytest_cache/
|
| 34 |
+
coverage.xml
|
| 35 |
+
# Misc
|
| 36 |
+
*.egg-info/
|
| 37 |
+
dist/
|
| 38 |
+
build/
|
| 39 |
+
|
| 40 |
+
template_basic/rag_input_documents/csv/*
|
| 41 |
+
template_basic/rag_input_documents/markdown/*
|
| 42 |
+
template_basic/rag_input_documents/pdf/*
|
| 43 |
+
|
| 44 |
+
template_basic/rag_storage/*
|
| 45 |
+
|
| 46 |
+
.github/copilot-instructions.md
|
| 47 |
+
.github/settings.json
|
Dockerfile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
| 2 |
+
# you will also find guides on how best to write your Dockerfile
|
| 3 |
+
|
| 4 |
+
FROM python:3.12-slim
|
| 5 |
+
|
| 6 |
+
# Set working directory
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
# Create user
|
| 10 |
+
RUN useradd -m -u 1000 user
|
| 11 |
+
|
| 12 |
+
# Copy dependency files first (for better caching)
|
| 13 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 14 |
+
|
| 15 |
+
# Install dependencies using pip (more reliable for HF Spaces)
|
| 16 |
+
RUN pip install --no-cache-dir --upgrade pip
|
| 17 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 18 |
+
|
| 19 |
+
# Copy application files
|
| 20 |
+
COPY --chown=user . /app
|
| 21 |
+
|
| 22 |
+
# Switch to user
|
| 23 |
+
USER user
|
| 24 |
+
|
| 25 |
+
# Expose port
|
| 26 |
+
EXPOSE 7860
|
| 27 |
+
|
| 28 |
+
# Run the Gradio app
|
| 29 |
+
CMD ["python", "app.py"]
|
README.md
CHANGED
|
@@ -1,10 +1,78 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: purple
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: AI Chatbot with Smart Routing
|
| 3 |
+
emoji: π€
|
| 4 |
+
colorFrom: blue
|
| 5 |
colorTo: purple
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
app_port: 7860
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# π€ Financial AI Chatbot with Smart Routing & RAG
|
| 13 |
+
|
| 14 |
+
**A demo GenAI app that demonstrates smart routing using LangChain**
|
| 15 |
+
|
| 16 |
+
## π Try It Live
|
| 17 |
+
- **π― Live Demo**: [Financial AI Chatbot](https://huggingface.co/spaces/krinya/smart_rooting_on_render_example) β **Try it here!**
|
| 18 |
+
- **π» Frontend Code**: [`app.py`](https://huggingface.co/spaces/krinya/smart_rooting_on_render_example/blob/main/app.py) - Gradio interface code
|
| 19 |
+
- **π Backend API**: [Deployed on Render](https://gen-ai-demo-rag-bot.onrender.com/docs)
|
| 20 |
+
- **π Backend API Code**: [GitHub Repository](https://github.com/krinya/gen_ai_demo_rag_bot/tree/main)
|
| 21 |
+
|
| 22 |
+
## π― What This Demonstrates
|
| 23 |
+
|
| 24 |
+
This project shows **how to create a complete GenAI product**:
|
| 25 |
+
|
| 26 |
+
### 1. π§ Smart Routing with LangChain
|
| 27 |
+
Intelligently routes financial questions about **5 major companies** (Apple, Google, Amazon, Tesla, Intel):
|
| 28 |
+
- π **FAQ Route**: Quick facts (CEO names, founding dates)
|
| 29 |
+
- π **RAG Route**: Financial data from 2024 annual reports (revenue, profits)
|
| 30 |
+
- π§ **LLM Route**: General explanations and financial concepts
|
| 31 |
+
|
| 32 |
+
### 2. π RAG Implementation
|
| 33 |
+
- **Vector Storage**: ChromaDB with processed financial documents (full annual reports)
|
| 34 |
+
- **Retrieval System**: Semantic search for relevant information
|
| 35 |
+
- **Smart Fallbacks**: Multiple sources with quality scoring
|
| 36 |
+
|
| 37 |
+
### 3. ποΈ Production Architecture
|
| 38 |
+
- **Backend**: Python FastAPI with LangChain, deployed on Render
|
| 39 |
+
- **Frontend**: Gradio UI deployed on Hugging Face Spaces
|
| 40 |
+
- **Separation**: Backend API + Frontend UI for scalability
|
| 41 |
+
|
| 42 |
+
## π οΈ How This Shows GenAI Product Development
|
| 43 |
+
|
| 44 |
+
**Complete workflow: Backend β Deploy β Frontend**
|
| 45 |
+
|
| 46 |
+
1. **Write Backend** (Python + LangChain)
|
| 47 |
+
- FastAPI with smart routing logic
|
| 48 |
+
- RAG pipeline with vector storage
|
| 49 |
+
- Deploy on Render cloud platform
|
| 50 |
+
|
| 51 |
+
2. **Create Frontend** (Gradio + Hugging Face)
|
| 52 |
+
- Interactive chat interface
|
| 53 |
+
- Real-time routing insights
|
| 54 |
+
- Deploy on Hugging Face Spaces
|
| 55 |
+
|
| 56 |
+
3. **Connect & Scale**
|
| 57 |
+
- Backend API serves multiple frontends
|
| 58 |
+
- Docker containerization
|
| 59 |
+
- Production-ready architecture
|
| 60 |
+
|
| 61 |
+
## π§ Tech Stack
|
| 62 |
+
|
| 63 |
+
- **AI**: OpenAI GPT-4o-mini + LangChain orchestration
|
| 64 |
+
- **Backend**: Python FastAPI deployed on Render
|
| 65 |
+
- **Frontend**: Gradio deployed on Hugging Face Spaces
|
| 66 |
+
- **Storage**: ChromaDB vector database
|
| 67 |
+
- **Data**: 2024 financial reports (Apple, Google, Amazon, Tesla, Intel)
|
| 68 |
+
|
| 69 |
+
## οΏ½ Example Queries
|
| 70 |
+
|
| 71 |
+
Try these in the live demo:
|
| 72 |
+
- "Who is the CEO of Tesla?" β FAQ route
|
| 73 |
+
- "What was Apple's revenue in 2024?" β RAG route
|
| 74 |
+
- "How do you calculate P/E ratio?" β LLM route
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
**π― Key Learning**: This demonstrates the complete GenAI development stack from data processing to production deployment!
|
app.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Financial AI Chatbot with Smart Routing & RAG - Gradio Frontend
|
| 3 |
+
|
| 4 |
+
This Gradio application demonstrates a complete GenAI product development workflow,
|
| 5 |
+
showcasing smart routing capabilities of an AI chatbot for financial Q&A.
|
| 6 |
+
|
| 7 |
+
Key Features:
|
| 8 |
+
- Smart routing between FAQ, RAG, and LLM responses
|
| 9 |
+
- Real-time routing insights and answer quality scoring
|
| 10 |
+
- Production-ready architecture with separated backend/frontend
|
| 11 |
+
- Interactive examples for different routing scenarios
|
| 12 |
+
|
| 13 |
+
Backend API: Deployed on Render with FastAPI + LangChain
|
| 14 |
+
Frontend UI: This Gradio interface deployed on Hugging Face Spaces
|
| 15 |
+
Data: 2024 financial reports from 5 major companies (Apple, Google, Amazon, Tesla, Intel)
|
| 16 |
+
|
| 17 |
+
For complete technical details and implementation guide, see:
|
| 18 |
+
https://huggingface.co/spaces/krinya/smart_rooting_on_render_example/blob/main/README.md
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
import gradio as gr
|
| 22 |
+
import requests
|
| 23 |
+
import uuid
|
| 24 |
+
from datetime import datetime
|
| 25 |
+
from typing import Dict, List, Tuple, Optional
|
| 26 |
+
import time
|
| 27 |
+
|
| 28 |
+
API_BASE_URL = "https://gen-ai-demo-rag-bot.onrender.com"
|
| 29 |
+
CHAT_ENDPOINT = f"{API_BASE_URL}/chat"
|
| 30 |
+
HEALTH_ENDPOINT = f"{API_BASE_URL}/health"
|
| 31 |
+
DOCS_ENDPOINT = f"{API_BASE_URL}/docs"
|
| 32 |
+
|
| 33 |
+
EXAMPLE_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 |
+
|
| 39 |
+
ROUTING_COLORS = {
|
| 40 |
+
"faq": "π #4CAF50",
|
| 41 |
+
"rag": "π #2196F3",
|
| 42 |
+
"llm": "π§ #FF9800",
|
| 43 |
+
"general": "π #9E9E9E"
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
def 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 starts
|
| 50 |
+
(Render free tier can take a while on the first request). Returns a
|
| 51 |
+
(bool, message) tuple where bool indicates healthy.
|
| 52 |
+
"""
|
| 53 |
+
last_err = None
|
| 54 |
+
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 = e
|
| 67 |
+
if attempt < retries:
|
| 68 |
+
time.sleep(backoff_secs * attempt)
|
| 69 |
+
continue
|
| 70 |
+
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 |
+
|
| 76 |
+
def 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_id
|
| 82 |
+
}
|
| 83 |
+
response = requests.post(
|
| 84 |
+
CHAT_ENDPOINT,
|
| 85 |
+
json=payload,
|
| 86 |
+
headers={"Content-Type": "application/json"},
|
| 87 |
+
timeout=150
|
| 88 |
+
)
|
| 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 |
+
|
| 107 |
+
def 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 |
+
|
| 132 |
+
def 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 |
+
|
| 145 |
+
def 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_id
|
| 149 |
+
|
| 150 |
+
# Send message to API with persistent session ID
|
| 151 |
+
api_response = send_message_to_api(message, session_id)
|
| 152 |
+
|
| 153 |
+
# Extract response and routing info
|
| 154 |
+
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 information
|
| 159 |
+
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 format
|
| 164 |
+
history.append({"role": "user", "content": message})
|
| 165 |
+
history.append({"role": "assistant", "content": bot_response})
|
| 166 |
+
|
| 167 |
+
return history, "", routing_display, session_id
|
| 168 |
+
|
| 169 |
+
def load_example(example_text: str) -> str:
|
| 170 |
+
"""Load an example query into the input box"""
|
| 171 |
+
return example_text
|
| 172 |
+
|
| 173 |
+
def create_gradio_interface():
|
| 174 |
+
"""Create and configure the Gradio interface"""
|
| 175 |
+
|
| 176 |
+
# Check API health at startup
|
| 177 |
+
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 |
+
# Header
|
| 185 |
+
gr.Markdown("""
|
| 186 |
+
# π€ Financial AI Chatbot with Smart Routing & RAG
|
| 187 |
+
|
| 188 |
+
**A demo GenAI app that demonstrates smart routing using LangChain - showing how to create a complete GenAI product**
|
| 189 |
+
|
| 190 |
+
## π― What This Demonstrates
|
| 191 |
+
|
| 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 LangChain
|
| 195 |
+
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 concepts
|
| 199 |
+
|
| 200 |
+
### π RAG Implementation
|
| 201 |
+
- **Vector Storage**: ChromaDB with processed financial documents (full annual reports)
|
| 202 |
+
- **Retrieval System**: Semantic search for relevant information
|
| 203 |
+
- **Smart Fallbacks**: Multiple sources with quality scoring
|
| 204 |
+
|
| 205 |
+
### ποΈ Backend and Frontend Architecture
|
| 206 |
+
- **Backend**: Python FastAPI with LangChain, deployed on Render
|
| 207 |
+
- **Frontend**: Gradio UI deployed on Hugging Face Spaces using Docker containerization
|
| 208 |
+
- **Separation**: Backend API + Frontend UI
|
| 209 |
+
|
| 210 |
+
**π§ Tech Stack**: OpenAI GPT-4o-mini + LangChain orchestration, Python FastAPI, ChromaDB vector database, Docker containerization
|
| 211 |
+
|
| 212 |
+
**π Learn More**: [README with technical details](https://huggingface.co/spaces/krinya/smart_rooting_on_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 Diagram
|
| 217 |
+
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=False
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
# API Health Status
|
| 229 |
+
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=1
|
| 258 |
+
)
|
| 259 |
+
send_btn = gr.Button("Send π€", scale=1, variant="primary")
|
| 260 |
+
|
| 261 |
+
# Example queries below chat interface
|
| 262 |
+
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 controls
|
| 280 |
+
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 bottom
|
| 291 |
+
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 info
|
| 297 |
+
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-4o-mini β’ ChromaDB β’ FastAPI β’ Render β’ Gradio β’ Hugging Face Spaces β’ CI/CD
|
| 303 |
+
""")
|
| 304 |
+
|
| 305 |
+
# Event handlers
|
| 306 |
+
def clear_chat():
|
| 307 |
+
return [], ""
|
| 308 |
+
|
| 309 |
+
def new_session():
|
| 310 |
+
return str(uuid.uuid4()), [], ""
|
| 311 |
+
|
| 312 |
+
# Button click events
|
| 313 |
+
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 loading
|
| 324 |
+
def chat_wrapper(message, history, session_id, show_routing):
|
| 325 |
+
# Show loading message
|
| 326 |
+
if message.strip():
|
| 327 |
+
# Add user message and loading response immediately
|
| 328 |
+
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_id
|
| 333 |
+
|
| 334 |
+
# Get actual response
|
| 335 |
+
result_history, empty_input, routing_info, updated_session = chat_with_bot(message, history, session_id, show_routing)
|
| 336 |
+
yield result_history, "", routing_info, updated_session
|
| 337 |
+
else:
|
| 338 |
+
yield history, "", "", session_id
|
| 339 |
+
|
| 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 interface
|
| 353 |
+
|
| 354 |
+
if __name__ == "__main__":
|
| 355 |
+
# Create and launch the interface
|
| 356 |
+
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 configuration
|
| 361 |
+
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=None
|
| 368 |
+
)
|
chatbot_workflow_graph.png
ADDED
|
pyproject.toml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "smart-rooting-chatbot"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "AI Chatbot with Smart Routing & RAG"
|
| 5 |
+
readme = "README.md"
|
| 6 |
+
requires-python = ">=3.10"
|
| 7 |
+
dependencies = [
|
| 8 |
+
"gradio==5.42.0",
|
| 9 |
+
"requests>=2.31.0",
|
| 10 |
+
]
|
| 11 |
+
|
| 12 |
+
[build-system]
|
| 13 |
+
requires = ["hatchling"]
|
| 14 |
+
build-backend = "hatchling.build"
|
| 15 |
+
|
| 16 |
+
[tool.hatch.build.targets.wheel]
|
| 17 |
+
packages = ["."]
|
| 18 |
+
|
| 19 |
+
[tool.uv]
|
| 20 |
+
dev-dependencies = []
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio==5.42.0
|
| 2 |
+
requests==2.31.0
|
robot_favicon.png
ADDED
|
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|