Velayutham S commited on
Commit
8bc4e4a
·
1 Parent(s): c627503

feat: RAG integrated with FAISS from HF Dataset + Gemini embedding

Browse files
Files changed (2) hide show
  1. app.py +125 -32
  2. requirements.txt +1 -1
app.py CHANGED
@@ -1,64 +1,141 @@
1
  import gradio as gr
2
  import os
3
- from huggingface_hub import InferenceClient
 
 
 
 
4
 
5
- SYSTEM_PROMPT = """You are FeelEd Lite, a friendly Tamil-medium tutor for Grade 11 TN Board Commerce students in Tamil Nadu, India.
6
- Always answer in simple Tamil + English mix. Keep answers short and clear."""
 
 
7
 
8
- HF_TOKEN = os.environ.get("HF_TOKEN", "")
 
 
 
 
 
9
 
10
- def generate_response(user_message, mode):
11
- if not HF_TOKEN:
12
- return "❌ HF_TOKEN not set!", "none"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  if mode == "📚 Q&A":
15
- user = f"Grade 11 TN Board Commerce: {user_message}\nAnswer simply in Tamil+English:"
 
 
 
 
16
  elif mode == "📖 Story Mode":
17
- user = f"Explain as a short simple story with Tamil characters: {user_message}"
 
 
18
  else:
19
- user = f"3 TN Board exam questions with answers for Grade 11 Commerce: {user_message}"
 
 
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=350,
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]}({provider}): {str(e)[:120]}")
50
  continue
51
 
52
- return f"எலலா models-உம் fail:\n" + "\n".join(errors), "none"
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 · Grade 11 Commerce · TN Board Tutor</p>
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(placeholder="உங்கள் கேள்வியை கேளுங்கள்...", show_label=False, scale=5, lines=2)
 
 
 
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(choices=["📚 Q&A", "📖 Story Mode", "🎯 Exam Mode"], value="📚 Q&A", label="📌 Mode:")
 
 
 
 
95
  gr.Examples(
96
- examples=[["தேவை விதி என்றால் என்ன?"], ["GDP explain"], ["தொழில் முனைவோர் பண்புகள்"], ["Consumer rights"]],
 
 
 
 
 
 
97
  inputs=msg, label="💡 Examples:",
98
  )
99
- gr.HTML("""
100
  <div id='info-box'>
101
- <strong style='color:#63b3ed;'>FeelEd Lite v0.1</strong><br>
102
- 🏫 Grade 11 TN Commerce<br>
 
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
- sentence-transformers>=2.2.0
 
1
  huggingface_hub>=0.24.0
2
  faiss-cpu>=1.7.4
3
  numpy>=1.24.0
4
+ requests>=2.28.0