Spaces:
Sleeping
Sleeping
Velayutham S commited on
Commit ·
8bc4e4a
1
Parent(s): c627503
feat: RAG integrated with FAISS from HF Dataset + Gemini embedding
Browse files- app.py +125 -32
- requirements.txt +1 -1
app.py
CHANGED
|
@@ -1,64 +1,141 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
if mode == "📚 Q&A":
|
| 15 |
-
user = f"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
elif mode == "📖 Story Mode":
|
| 17 |
-
user = f"
|
|
|
|
|
|
|
| 18 |
else:
|
| 19 |
-
user = f"
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
# provider + model combinations that work on free tier
|
| 22 |
combos = [
|
| 23 |
("Qwen/Qwen2.5-7B-Instruct", "novita"),
|
| 24 |
("meta-llama/Llama-3.1-8B-Instruct", "novita"),
|
| 25 |
("Qwen/Qwen2.5-3B-Instruct", "novita"),
|
| 26 |
-
("meta-llama/Llama-3.2-3B-Instruct", "novita"),
|
| 27 |
("Qwen/Qwen2.5-72B-Instruct", "nebius"),
|
| 28 |
-
("mistralai/Mistral-7B-Instruct-v0.3", "hf-inference"),
|
| 29 |
]
|
| 30 |
|
| 31 |
errors = []
|
| 32 |
for model_id, provider in combos:
|
| 33 |
try:
|
| 34 |
-
client = InferenceClient(
|
| 35 |
-
model=model_id,
|
| 36 |
-
token=HF_TOKEN,
|
| 37 |
-
provider=provider,
|
| 38 |
-
)
|
| 39 |
response = client.chat_completion(
|
| 40 |
messages=[
|
| 41 |
{"role": "system", "content": SYSTEM_PROMPT},
|
| 42 |
{"role": "user", "content": user}
|
| 43 |
],
|
| 44 |
-
max_tokens=
|
| 45 |
temperature=0.7,
|
| 46 |
)
|
| 47 |
return response.choices[0].message.content.strip(), model_id.split("/")[-1]
|
| 48 |
except Exception as e:
|
| 49 |
-
errors.append(f"{model_id.split('/')[-1]}
|
| 50 |
continue
|
| 51 |
|
| 52 |
-
return
|
| 53 |
|
| 54 |
def chat(message, history, mode):
|
| 55 |
if not message.strip():
|
| 56 |
return history, "", "🤖 Ready"
|
| 57 |
history = history or []
|
|
|
|
| 58 |
response, used_model = generate_response(message, mode)
|
| 59 |
history.append({"role": "user", "content": message})
|
| 60 |
history.append({"role": "assistant", "content": response})
|
| 61 |
-
return history, "", f"🤖 {used_model}"
|
| 62 |
|
| 63 |
CSS = """
|
| 64 |
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Tamil:wght@400;600;700&family=Inter:wght@400;500;600&display=swap');
|
|
@@ -70,13 +147,15 @@ body, .gradio-container { background: #0f0f1a !important; font-family: 'Inter',
|
|
| 70 |
#info-box { background: #16213e; border: 1px solid #2d3748; border-radius: 10px; padding: 14px; color: #718096; font-size: 0.82rem; line-height: 1.8; margin-top: 12px; }
|
| 71 |
"""
|
| 72 |
|
|
|
|
|
|
|
| 73 |
with gr.Blocks(css=CSS, title="FeelEd Lite — Tamil TN Tutor") as demo:
|
| 74 |
-
gr.HTML("""
|
| 75 |
<div id="header">
|
| 76 |
<h1>📚 FeelEd Lite</h1>
|
| 77 |
-
<p>Tamil Medium ·
|
| 78 |
<p>தமிழ் மீடியம் மாணவர்களுக்கான AI கல்வி உதவியாளர்</p>
|
| 79 |
-
<span class="model-badge">🤖 Small Model · Build Small Hackathon 2026</span>
|
| 80 |
</div>
|
| 81 |
""")
|
| 82 |
|
|
@@ -85,24 +164,38 @@ with gr.Blocks(css=CSS, title="FeelEd Lite — Tamil TN Tutor") as demo:
|
|
| 85 |
chatbot = gr.Chatbot(type="messages", height=400, show_label=False, bubble_full_width=False)
|
| 86 |
model_display = gr.Textbox(value="🤖 Ready", show_label=False, interactive=False, container=False)
|
| 87 |
with gr.Row():
|
| 88 |
-
msg = gr.Textbox(
|
|
|
|
|
|
|
|
|
|
| 89 |
with gr.Column(scale=1, min_width=90):
|
| 90 |
send_btn = gr.Button("அனுப்பு ▶", variant="primary")
|
| 91 |
clear_btn = gr.Button("🗑 Clear")
|
| 92 |
|
| 93 |
with gr.Column(scale=1, min_width=200):
|
| 94 |
-
mode = gr.Radio(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
gr.Examples(
|
| 96 |
-
examples=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
inputs=msg, label="💡 Examples:",
|
| 98 |
)
|
| 99 |
-
gr.HTML("""
|
| 100 |
<div id='info-box'>
|
| 101 |
-
<strong style='color:#63b3ed;'>FeelEd Lite v0.
|
| 102 |
-
🏫
|
|
|
|
| 103 |
🌐 Tamil + English<br>
|
| 104 |
🔒 Student privacy first<br><br>
|
| 105 |
-
<em style='color:#4a5568;'>Build Small Hackathon 2026</em>
|
| 106 |
</div>
|
| 107 |
""")
|
| 108 |
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pickle
|
| 5 |
+
import requests
|
| 6 |
+
import faiss
|
| 7 |
+
from huggingface_hub import InferenceClient, hf_hub_download
|
| 8 |
|
| 9 |
+
# ─── Config ──────────────────────────────
|
| 10 |
+
HF_TOKEN = os.environ.get("HF_TOKEN", "")
|
| 11 |
+
GEMINI_KEY = os.environ.get("GEMINI_API_KEY", "")
|
| 12 |
+
DATASET_REPO = "build-small-hackathon/feeled-lite-rag"
|
| 13 |
|
| 14 |
+
SYSTEM_PROMPT = """You are FeelEd Lite, a friendly Tamil-medium tutor for TN Board students in Tamil Nadu, India.
|
| 15 |
+
Always answer in simple Tamil + English mix (Tanglish) that a student can easily understand.
|
| 16 |
+
Use the provided textbook context to give accurate answers.
|
| 17 |
+
Keep answers short, clear, and encouraging.
|
| 18 |
+
For Story Mode: explain as a simple story with Tamil characters.
|
| 19 |
+
For Exam Mode: give TN Board exam questions with model answers."""
|
| 20 |
|
| 21 |
+
# ─── Load FAISS index ─────────────────────
|
| 22 |
+
print("Loading FAISS index from HF Dataset...")
|
| 23 |
+
faiss_index = None
|
| 24 |
+
metadata_store = []
|
| 25 |
+
|
| 26 |
+
try:
|
| 27 |
+
index_path = hf_hub_download(
|
| 28 |
+
repo_id=DATASET_REPO,
|
| 29 |
+
filename="index.faiss",
|
| 30 |
+
repo_type="dataset",
|
| 31 |
+
token=HF_TOKEN if HF_TOKEN else None,
|
| 32 |
+
)
|
| 33 |
+
meta_path = hf_hub_download(
|
| 34 |
+
repo_id=DATASET_REPO,
|
| 35 |
+
filename="metadata.pkl",
|
| 36 |
+
repo_type="dataset",
|
| 37 |
+
token=HF_TOKEN if HF_TOKEN else None,
|
| 38 |
+
)
|
| 39 |
+
faiss_index = faiss.read_index(index_path)
|
| 40 |
+
with open(meta_path, "rb") as f:
|
| 41 |
+
data = pickle.load(f)
|
| 42 |
+
metadata_store = data["metadata"]
|
| 43 |
+
print(f"✅ FAISS loaded: {faiss_index.ntotal} vectors")
|
| 44 |
+
except Exception as e:
|
| 45 |
+
print(f"⚠️ FAISS load failed: {e}")
|
| 46 |
+
|
| 47 |
+
# ─── Gemini Embed ─────────────────────────
|
| 48 |
+
def embed_query(text: str):
|
| 49 |
+
if not GEMINI_KEY:
|
| 50 |
+
return None
|
| 51 |
+
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:embedContent?key={GEMINI_KEY}"
|
| 52 |
+
body = {"content": {"parts": [{"text": text}]}, "outputDimensionality": 768}
|
| 53 |
+
try:
|
| 54 |
+
r = requests.post(url, json=body, timeout=10)
|
| 55 |
+
if r.ok:
|
| 56 |
+
return r.json().get("embedding", {}).get("values", [])
|
| 57 |
+
except:
|
| 58 |
+
pass
|
| 59 |
+
return None
|
| 60 |
+
|
| 61 |
+
# ─── RAG Search ───────────────────────────
|
| 62 |
+
def rag_search(query: str, grade: str = "", subject: str = "", top_k: int = 5) -> str:
|
| 63 |
+
if faiss_index is None or not GEMINI_KEY:
|
| 64 |
+
return ""
|
| 65 |
+
vec = embed_query(query)
|
| 66 |
+
if not vec:
|
| 67 |
+
return ""
|
| 68 |
+
q = np.array([vec], dtype=np.float32)
|
| 69 |
+
faiss.normalize_L2(q)
|
| 70 |
+
scores, indices = faiss_index.search(q, top_k * 3)
|
| 71 |
+
chunks = []
|
| 72 |
+
for score, idx in zip(scores[0], indices[0]):
|
| 73 |
+
if idx < 0 or score < 0.35:
|
| 74 |
+
continue
|
| 75 |
+
meta = metadata_store[idx]
|
| 76 |
+
if grade and meta.get("grade") != grade:
|
| 77 |
+
continue
|
| 78 |
+
text = meta.get("text", "").strip()
|
| 79 |
+
if text and len(text) > 30:
|
| 80 |
+
chunks.append(text)
|
| 81 |
+
if len(chunks) >= top_k:
|
| 82 |
+
break
|
| 83 |
+
return "\n\n".join(chunks[:top_k])
|
| 84 |
+
|
| 85 |
+
# ─── Inference ────────────────────────────
|
| 86 |
+
def generate_response(user_message, mode, grade="11", subject="Commerce"):
|
| 87 |
+
context = rag_search(user_message, grade=grade)
|
| 88 |
|
| 89 |
if mode == "📚 Q&A":
|
| 90 |
+
user = f"""Textbook Context:
|
| 91 |
+
{context if context else '(No specific context found)'}
|
| 92 |
+
|
| 93 |
+
Student Question: {user_message}
|
| 94 |
+
Answer simply in Tamil+English mix:"""
|
| 95 |
elif mode == "📖 Story Mode":
|
| 96 |
+
user = f"""Context: {context[:500] if context else ''}
|
| 97 |
+
|
| 98 |
+
Explain this topic as a short simple story with Tamil characters: {user_message}"""
|
| 99 |
else:
|
| 100 |
+
user = f"""Context: {context[:500] if context else ''}
|
| 101 |
+
|
| 102 |
+
Give 3 important TN Board exam questions with model answers for: {user_message}"""
|
| 103 |
|
|
|
|
| 104 |
combos = [
|
| 105 |
("Qwen/Qwen2.5-7B-Instruct", "novita"),
|
| 106 |
("meta-llama/Llama-3.1-8B-Instruct", "novita"),
|
| 107 |
("Qwen/Qwen2.5-3B-Instruct", "novita"),
|
|
|
|
| 108 |
("Qwen/Qwen2.5-72B-Instruct", "nebius"),
|
|
|
|
| 109 |
]
|
| 110 |
|
| 111 |
errors = []
|
| 112 |
for model_id, provider in combos:
|
| 113 |
try:
|
| 114 |
+
client = InferenceClient(model=model_id, token=HF_TOKEN, provider=provider)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
response = client.chat_completion(
|
| 116 |
messages=[
|
| 117 |
{"role": "system", "content": SYSTEM_PROMPT},
|
| 118 |
{"role": "user", "content": user}
|
| 119 |
],
|
| 120 |
+
max_tokens=400,
|
| 121 |
temperature=0.7,
|
| 122 |
)
|
| 123 |
return response.choices[0].message.content.strip(), model_id.split("/")[-1]
|
| 124 |
except Exception as e:
|
| 125 |
+
errors.append(f"{model_id.split('/')[-1]}: {str(e)[:80]}")
|
| 126 |
continue
|
| 127 |
|
| 128 |
+
return "மன்னிக்கவும், இப்போது busy. சற்று நேரம் கழித்து முயற்சிக்கவும்.", "none"
|
| 129 |
|
| 130 |
def chat(message, history, mode):
|
| 131 |
if not message.strip():
|
| 132 |
return history, "", "🤖 Ready"
|
| 133 |
history = history or []
|
| 134 |
+
rag_status = "📚 RAG: ✅" if faiss_index else "📚 RAG: ❌"
|
| 135 |
response, used_model = generate_response(message, mode)
|
| 136 |
history.append({"role": "user", "content": message})
|
| 137 |
history.append({"role": "assistant", "content": response})
|
| 138 |
+
return history, "", f"🤖 {used_model} | {rag_status}"
|
| 139 |
|
| 140 |
CSS = """
|
| 141 |
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Tamil:wght@400;600;700&family=Inter:wght@400;500;600&display=swap');
|
|
|
|
| 147 |
#info-box { background: #16213e; border: 1px solid #2d3748; border-radius: 10px; padding: 14px; color: #718096; font-size: 0.82rem; line-height: 1.8; margin-top: 12px; }
|
| 148 |
"""
|
| 149 |
|
| 150 |
+
rag_status_text = f"✅ {faiss_index.ntotal} vectors loaded" if faiss_index else "⚠️ RAG not loaded"
|
| 151 |
+
|
| 152 |
with gr.Blocks(css=CSS, title="FeelEd Lite — Tamil TN Tutor") as demo:
|
| 153 |
+
gr.HTML(f"""
|
| 154 |
<div id="header">
|
| 155 |
<h1>📚 FeelEd Lite</h1>
|
| 156 |
+
<p>Tamil Medium · Grades 9-12 · TN Board Tutor</p>
|
| 157 |
<p>தமிழ் மீடியம் மாணவர்களுக்கான AI கல்வி உதவியாளர்</p>
|
| 158 |
+
<span class="model-badge">🤖 Small Model · 📚 RAG: {rag_status_text} · Build Small Hackathon 2026</span>
|
| 159 |
</div>
|
| 160 |
""")
|
| 161 |
|
|
|
|
| 164 |
chatbot = gr.Chatbot(type="messages", height=400, show_label=False, bubble_full_width=False)
|
| 165 |
model_display = gr.Textbox(value="🤖 Ready", show_label=False, interactive=False, container=False)
|
| 166 |
with gr.Row():
|
| 167 |
+
msg = gr.Textbox(
|
| 168 |
+
placeholder="உங்கள் கேள்வியை Tamil அல்லது English-ல கேளுங்கள்...",
|
| 169 |
+
show_label=False, scale=5, lines=2,
|
| 170 |
+
)
|
| 171 |
with gr.Column(scale=1, min_width=90):
|
| 172 |
send_btn = gr.Button("அனுப்பு ▶", variant="primary")
|
| 173 |
clear_btn = gr.Button("🗑 Clear")
|
| 174 |
|
| 175 |
with gr.Column(scale=1, min_width=200):
|
| 176 |
+
mode = gr.Radio(
|
| 177 |
+
choices=["📚 Q&A", "📖 Story Mode", "🎯 Exam Mode"],
|
| 178 |
+
value="📚 Q&A", label="📌 Mode:",
|
| 179 |
+
)
|
| 180 |
+
gr.HTML("<hr style='border-color:#2d3748;margin:12px 0;'>")
|
| 181 |
gr.Examples(
|
| 182 |
+
examples=[
|
| 183 |
+
["தேவை விதி என்றால் என்ன?"],
|
| 184 |
+
["இரட்டை பதிவு முறை விளக்கு"],
|
| 185 |
+
["GDP என்றால் என்ன?"],
|
| 186 |
+
["தொழில் முனைவோர் பண்புகள்"],
|
| 187 |
+
["Consumer rights explain"],
|
| 188 |
+
],
|
| 189 |
inputs=msg, label="💡 Examples:",
|
| 190 |
)
|
| 191 |
+
gr.HTML(f"""
|
| 192 |
<div id='info-box'>
|
| 193 |
+
<strong style='color:#63b3ed;'>FeelEd Lite v0.2</strong><br>
|
| 194 |
+
🏫 Grades 9-12 TN Board<br>
|
| 195 |
+
📚 RAG: TN Textbooks<br>
|
| 196 |
🌐 Tamil + English<br>
|
| 197 |
🔒 Student privacy first<br><br>
|
| 198 |
+
<em style='color:#4a5568;'>Build Small Hackathon 2026<br>Track: Backyard AI</em>
|
| 199 |
</div>
|
| 200 |
""")
|
| 201 |
|
requirements.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
huggingface_hub>=0.24.0
|
| 2 |
faiss-cpu>=1.7.4
|
| 3 |
numpy>=1.24.0
|
| 4 |
-
|
|
|
|
| 1 |
huggingface_hub>=0.24.0
|
| 2 |
faiss-cpu>=1.7.4
|
| 3 |
numpy>=1.24.0
|
| 4 |
+
requests>=2.28.0
|