Nebulixlabs commited on
Commit
8203c91
·
verified ·
1 Parent(s): 5a20187

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -75
app.py CHANGED
@@ -4,11 +4,10 @@ import asyncio
4
  import os
5
  import uvicorn
6
  import re
7
- import time
8
  import multiprocessing
9
  import urllib.parse
10
  from contextlib import asynccontextmanager
11
- from fastapi import FastAPI, Query, Request, Form
12
  from fastapi.responses import JSONResponse
13
  from fastapi.middleware.cors import CORSMiddleware
14
  from huggingface_hub import hf_hub_download
@@ -16,7 +15,6 @@ from rank_bm25 import BM25Okapi
16
  import torch
17
  import aiohttp
18
  from bs4 import BeautifulSoup
19
- from langdetect import detect
20
 
21
  # DIRECT IMPORTS
22
  from transformers import pipeline
@@ -42,8 +40,6 @@ AI_RESPONSE_CACHE = {}
42
  MAX_CACHE_SIZE = 5000
43
  STOP_WORDS = {"the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "is", "are"}
44
  HF_TOKEN = os.getenv("HF_TOKEN")
45
- BANNED_IPS = set()
46
- IP_TRACKER = {}
47
 
48
  # 🔥 MASSIVE DESI & MODERN ADULT KEYWORDS LIST
49
  ADULT_KEYWORDS = {
@@ -55,30 +51,12 @@ ADULT_KEYWORDS = {
55
  "sex tape", "milf", "stepmom", "incest", "playboy", "scandal", "leaked"
56
  }
57
 
58
- # 🔥 MASSIVE LANGUAGE MAPPER (50+ Languages)
59
- LANG_MAP = {
60
- "en": "English", "hi": "Hindi", "mr": "Marathi", "bn": "Bengali", "ta": "Tamil",
61
- "te": "Telugu", "gu": "Gujarati", "ur": "Urdu", "kn": "Kannada", "ml": "Malayalam",
62
- "pa": "Punjabi", "or": "Odia", "ne": "Nepali", "fr": "French", "es": "Spanish",
63
- "de": "German", "ru": "Russian", "ja": "Japanese", "zh-cn": "Chinese (Simplified)",
64
- "zh-tw": "Chinese (Traditional)", "ar": "Arabic", "pt": "Portuguese", "it": "Italian",
65
- "ko": "Korean", "id": "Indonesian", "tr": "Turkish", "nl": "Dutch", "vi": "Vietnamese"
66
- }
67
-
68
- # --- 2. ADVANCED NLP, INDEXING & LANGUAGE ---
69
  def tokenize(text):
70
  if not text: return []
71
  tokens = re.findall(r'[a-z0-9]+', str(text).lower())
72
  return [t for t in tokens if t not in STOP_WORDS]
73
 
74
- def detect_language_name(text):
75
- try:
76
- if not text or len(text.strip()) < 10: return "Unknown"
77
- code = detect(text)
78
- return LANG_MAP.get(code, code.upper()) # Agar list me nahi mila toh code return karega
79
- except:
80
- return "Unknown"
81
-
82
  def load_and_index_dataset():
83
  global bm25_index, raw_data, suggestion_list
84
  print("📥 Loading Dataset & Building Deep Index...")
@@ -96,10 +74,14 @@ def load_and_index_dataset():
96
  print(f"⚠️ Hugging Face Download failed: {e}")
97
 
98
  tokenized_corpus = []
 
99
 
100
  if os.path.exists(dataset_path):
101
  with open(dataset_path, "r", encoding="utf-8") as f:
102
- for line in f:
 
 
 
103
  if line.strip():
104
  try:
105
  doc = json.loads(line)
@@ -108,10 +90,6 @@ def load_and_index_dataset():
108
  if url not in URL_STATS:
109
  URL_STATS[url] = {"user_visits": 0, "ai_visits": 0, "is_trusted": False}
110
 
111
- # Extract language and append to document
112
- combined_text = str(doc.get("title", "")) + " " + str(doc.get("description", ""))
113
- doc["language"] = detect_language_name(combined_text)
114
-
115
  raw_data.append(doc)
116
 
117
  title_str = doc.get("title", "")
@@ -131,7 +109,7 @@ def load_and_index_dataset():
131
  print("⚡ Building High-Speed BM25 Okapi Index...")
132
  suggestion_list = sorted(list(set(suggestion_list)))
133
  bm25_index = BM25Okapi(tokenized_corpus)
134
- print(f"🚀 Indexing Complete! {len(raw_data)} records ready.")
135
  else:
136
  print("❌ Dataset file not found!")
137
 
@@ -139,29 +117,24 @@ def load_ai_models():
139
  global ai_generator
140
  print("🤖 Loading Modern Fast Text AI Model...")
141
  try:
142
- # Chota, modern aur tezz model
143
  ai_generator = pipeline("text-generation", model="Qwen/Qwen2.5-0.5B-Instruct", device=-1, torch_dtype=torch.float32)
144
  print("✅ AI Model Loaded! System ready.")
145
  except Exception as e:
146
  print(f"❌ Model Loading Failed: {e}")
147
 
148
- # --- 3. SMART SCRIPT RAG: EARLY STOPPING CHUNKING ---
149
  def chunk_text(text, chunk_size=120):
150
  words = text.split()
151
  return [" ".join(words[i:i+chunk_size]) for i in range(0, len(words), chunk_size)]
152
 
153
  async def scrape_and_find_best_chunk(url, query):
154
- """Script website visit karegi, sirf Headers & Paragraphs legi, aur best chunk lekar ruk jayegi"""
155
  try:
156
  async with aiohttp.ClientSession() as session:
157
- # Sirf 3 second ka timeout taki endpoint na atke
158
  async with session.get(url, timeout=3) as resp:
159
  if resp.status == 200:
160
  html = await resp.text()
161
  soup = BeautifulSoup(html, "html.parser")
162
-
163
- # Sirf Paragraphs aur Headers nikal rahe hain kachra avoid karne ke liye
164
- tags_to_extract = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
165
  text = " ".join([tag.get_text(strip=True) for tag in soup.find_all(tags_to_extract)])
166
 
167
  if not text: return ""
@@ -171,24 +144,19 @@ async def scrape_and_find_best_chunk(url, query):
171
 
172
  best_chunk = ""
173
  max_score = 0
174
- # Threshold: Agar query ke aade tokens mil gaye chunk mein toh wahi ruk jao
175
  early_stop_threshold = max(1, len(query_tokens) // 2)
176
 
177
  for chunk in chunks:
178
  chunk_tokens = set(tokenize(chunk))
179
  score = len(query_tokens.intersection(chunk_tokens))
180
-
181
  if score > max_score:
182
  max_score = score
183
  best_chunk = chunk
184
-
185
- # 🔥 EARLY STOPPING: Jo kaam ka chunk mila wahi liya, aage process nahi kiya
186
  if max_score >= early_stop_threshold and max_score > 0:
187
  break
188
-
189
  return best_chunk
190
- except Exception:
191
- return "" # Scraping failed (timeout/block), ignore quietly
192
  return ""
193
 
194
  # --- 4. CORE SEARCH ALGORITHM & RANKING ---
@@ -198,8 +166,6 @@ async def perform_core_search(raw_query: str):
198
 
199
  query = raw_query.strip().lower()
200
  query_tokens = tokenize(query)
201
-
202
- # 🚨 SMART ADULT FILTER (Using 'in' operator to catch hidden adult queries)
203
  user_wants_adult = any(adult_word in query for adult_word in ADULT_KEYWORDS)
204
 
205
  scores = bm25_index.get_scores(query_tokens) if query_tokens else [1.0] * len(raw_data)
@@ -215,31 +181,33 @@ async def perform_core_search(raw_query: str):
215
  parsed_url = urllib.parse.urlparse(doc_url)
216
  domain = parsed_url.netloc.lower()
217
 
218
- # Text combine to check adult words in document
219
  doc_text = (str(doc.get("title", "")) + " " + str(doc.get("description", "")) + " " + doc_url).lower()
220
  is_adult_doc = doc.get("tag", "general").lower() in ["adult", "nsfw"] or any(aw in doc_text for aw in ADULT_KEYWORDS)
221
 
222
- # Agar user ki query normal hai, toh adult result filter ho jayega
223
  if not user_wants_adult and is_adult_doc:
224
  continue
225
 
226
  final_score = score
227
 
228
- # 🔥 DOMAIN RANKING BOOST
229
  if query.replace(" ", "") in domain.replace(" ", ""):
230
- final_score *= 10.0 # Huge boost for direct domain match
231
  elif query in doc.get("title", "").lower():
232
  final_score *= 3.0
233
 
234
- # 🔥 TRUST RANKING BOOST
235
- stats = URL_STATS.get(doc_url, {})
236
  if stats.get("is_trusted", False):
237
- final_score *= 1.5
238
-
239
- final_score += (stats.get("user_visits", 0) * 0.05)
240
 
241
  seen_urls.add(doc_url)
242
- scored_results.append((final_score, doc))
 
 
 
 
243
 
244
  scored_results.sort(key=lambda x: x[0], reverse=True)
245
  best_score = scored_results[0][0] if scored_results else 0
@@ -274,21 +242,15 @@ async def lifespan(app: FastAPI):
274
  app = FastAPI(title="Nebulix Advanced Text AI & RAG Engine", lifespan=lifespan)
275
 
276
  app.add_middleware(
277
- CORSMiddleware,
278
- allow_origins=["*"],
279
- allow_credentials=True,
280
- allow_methods=["*"],
281
- allow_headers=["*"]
282
  )
283
 
284
- # 🔥 1. AUTO-SUGGESTIONS ENDPOINT
285
  @app.get("/suggest")
286
  async def get_suggestions(q: str = Query(..., min_length=1)):
287
  q = q.lower()
288
  matches = [s for s in suggestion_list if s.startswith(q)][:7]
289
  return {"query": q, "suggestions": matches}
290
 
291
- # 🔥 2. URL TRACKING & TRUST ENDPOINTS
292
  @app.post("/track-click")
293
  async def track_url_click(url: str = Query(...)):
294
  if url not in URL_STATS:
@@ -307,22 +269,38 @@ async def pure_search(q: str = Query(...)):
307
  results, prediction, _ = await perform_core_search(q)
308
  return {"request_id": req_id, "query": q, "results_count": len(results), "results": results}
309
 
310
- # 🔥 3. SMART RAG CHAT API (Only Text & Scraped Chunks)
311
- @app.post("/aimodechat")
312
- async def ai_mode_chat(q: str = Form(...)):
313
  req_id = str(uuid.uuid4())
314
-
315
- # AI Logic to decide RAG need
316
- needs_rag = any(w in q.lower() for w in ["what", "who", "where", "how", "why", "tell me", "explain", "latest"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
318
  results, _, _ = await perform_core_search(q)
319
  rag_context = ""
320
  used_sources = []
321
 
322
  if needs_rag and results:
 
 
 
 
323
  top_urls = results[:10]
324
 
325
- # Script asynchronously scrapes multiple URLs at once
326
  tasks = [scrape_and_find_best_chunk(doc["url"], q) for doc in top_urls]
327
  scraped_chunks = await asyncio.gather(*tasks)
328
 
@@ -331,13 +309,14 @@ async def ai_mode_chat(q: str = Form(...)):
331
  doc = top_urls[i]
332
  url = doc["url"]
333
 
334
- # Trust Tracking: Mark AI visits
335
  if url not in URL_STATS: URL_STATS[url] = {"user_visits": 0, "ai_visits": 0, "is_trusted": False}
336
  URL_STATS[url]["ai_visits"] += 1
337
  if URL_STATS[url]["ai_visits"] >= 100:
338
  URL_STATS[url]["is_trusted"] = True
 
 
339
 
340
- # Sirf kaam ka chunk (Early stopped chunk) lenge ya fall back pe description
341
  if chunk:
342
  context_parts.append(f"Source [{i+1}] ({url}): {chunk}")
343
  used_sources.append(doc)
@@ -347,7 +326,7 @@ async def ai_mode_chat(q: str = Form(...)):
347
 
348
  rag_context = "\n".join(context_parts)
349
 
350
- system_prompt = "You are a Smart AI Search Assistant. Use the provided search chunks to answer. Always mention the Source URL if you use information from it."
351
  if not needs_rag:
352
  system_prompt = "You are a helpful AI Assistant. Answer the user's query conversationally."
353
 
@@ -366,7 +345,13 @@ async def ai_mode_chat(q: str = Form(...)):
366
  "results": results[:15]
367
  }
368
 
 
 
 
 
 
 
 
 
369
  if __name__ == "__main__":
370
- # app object ko direct pass karein (string "main:app" ki jagah).
371
- # reload=True bhi hata diya hai kyunki cloud (Hugging Face) pe iski jarurat nahi hoti.
372
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
4
  import os
5
  import uvicorn
6
  import re
 
7
  import multiprocessing
8
  import urllib.parse
9
  from contextlib import asynccontextmanager
10
+ from fastapi import FastAPI, Query, Form
11
  from fastapi.responses import JSONResponse
12
  from fastapi.middleware.cors import CORSMiddleware
13
  from huggingface_hub import hf_hub_download
 
15
  import torch
16
  import aiohttp
17
  from bs4 import BeautifulSoup
 
18
 
19
  # DIRECT IMPORTS
20
  from transformers import pipeline
 
40
  MAX_CACHE_SIZE = 5000
41
  STOP_WORDS = {"the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "is", "are"}
42
  HF_TOKEN = os.getenv("HF_TOKEN")
 
 
43
 
44
  # 🔥 MASSIVE DESI & MODERN ADULT KEYWORDS LIST
45
  ADULT_KEYWORDS = {
 
51
  "sex tape", "milf", "stepmom", "incest", "playboy", "scandal", "leaked"
52
  }
53
 
54
+ # --- 2. ADVANCED NLP & INDEXING ---
 
 
 
 
 
 
 
 
 
 
55
  def tokenize(text):
56
  if not text: return []
57
  tokens = re.findall(r'[a-z0-9]+', str(text).lower())
58
  return [t for t in tokens if t not in STOP_WORDS]
59
 
 
 
 
 
 
 
 
 
60
  def load_and_index_dataset():
61
  global bm25_index, raw_data, suggestion_list
62
  print("📥 Loading Dataset & Building Deep Index...")
 
74
  print(f"⚠️ Hugging Face Download failed: {e}")
75
 
76
  tokenized_corpus = []
77
+ MAX_RECORDS_TO_LOAD = 150000 # Prevent OOM Crash
78
 
79
  if os.path.exists(dataset_path):
80
  with open(dataset_path, "r", encoding="utf-8") as f:
81
+ for i, line in enumerate(f):
82
+ if i >= MAX_RECORDS_TO_LOAD:
83
+ break
84
+
85
  if line.strip():
86
  try:
87
  doc = json.loads(line)
 
90
  if url not in URL_STATS:
91
  URL_STATS[url] = {"user_visits": 0, "ai_visits": 0, "is_trusted": False}
92
 
 
 
 
 
93
  raw_data.append(doc)
94
 
95
  title_str = doc.get("title", "")
 
109
  print("⚡ Building High-Speed BM25 Okapi Index...")
110
  suggestion_list = sorted(list(set(suggestion_list)))
111
  bm25_index = BM25Okapi(tokenized_corpus)
112
+ print(f"🚀 Indexing Complete! {len(raw_data)} records ready for search.")
113
  else:
114
  print("❌ Dataset file not found!")
115
 
 
117
  global ai_generator
118
  print("🤖 Loading Modern Fast Text AI Model...")
119
  try:
 
120
  ai_generator = pipeline("text-generation", model="Qwen/Qwen2.5-0.5B-Instruct", device=-1, torch_dtype=torch.float32)
121
  print("✅ AI Model Loaded! System ready.")
122
  except Exception as e:
123
  print(f"❌ Model Loading Failed: {e}")
124
 
125
+ # --- 3. SMART SCRIPT RAG ---
126
  def chunk_text(text, chunk_size=120):
127
  words = text.split()
128
  return [" ".join(words[i:i+chunk_size]) for i in range(0, len(words), chunk_size)]
129
 
130
  async def scrape_and_find_best_chunk(url, query):
 
131
  try:
132
  async with aiohttp.ClientSession() as session:
 
133
  async with session.get(url, timeout=3) as resp:
134
  if resp.status == 200:
135
  html = await resp.text()
136
  soup = BeautifulSoup(html, "html.parser")
137
+ tags_to_extract = ['p', 'h1', 'h2', 'h3']
 
 
138
  text = " ".join([tag.get_text(strip=True) for tag in soup.find_all(tags_to_extract)])
139
 
140
  if not text: return ""
 
144
 
145
  best_chunk = ""
146
  max_score = 0
 
147
  early_stop_threshold = max(1, len(query_tokens) // 2)
148
 
149
  for chunk in chunks:
150
  chunk_tokens = set(tokenize(chunk))
151
  score = len(query_tokens.intersection(chunk_tokens))
 
152
  if score > max_score:
153
  max_score = score
154
  best_chunk = chunk
 
 
155
  if max_score >= early_stop_threshold and max_score > 0:
156
  break
 
157
  return best_chunk
158
+ except:
159
+ return ""
160
  return ""
161
 
162
  # --- 4. CORE SEARCH ALGORITHM & RANKING ---
 
166
 
167
  query = raw_query.strip().lower()
168
  query_tokens = tokenize(query)
 
 
169
  user_wants_adult = any(adult_word in query for adult_word in ADULT_KEYWORDS)
170
 
171
  scores = bm25_index.get_scores(query_tokens) if query_tokens else [1.0] * len(raw_data)
 
181
  parsed_url = urllib.parse.urlparse(doc_url)
182
  domain = parsed_url.netloc.lower()
183
 
 
184
  doc_text = (str(doc.get("title", "")) + " " + str(doc.get("description", "")) + " " + doc_url).lower()
185
  is_adult_doc = doc.get("tag", "general").lower() in ["adult", "nsfw"] or any(aw in doc_text for aw in ADULT_KEYWORDS)
186
 
 
187
  if not user_wants_adult and is_adult_doc:
188
  continue
189
 
190
  final_score = score
191
 
192
+ # 🔥 1. RELEVANCE BOOST: Domain aur Title Match
193
  if query.replace(" ", "") in domain.replace(" ", ""):
194
+ final_score *= 10.0
195
  elif query in doc.get("title", "").lower():
196
  final_score *= 3.0
197
 
198
+ # 🔥 2. CONTEXTUAL TRUST BOOST: Sirf relevant links ko trust ka fayda milega
199
+ stats = URL_STATS.get(doc_url, {"user_visits": 0, "ai_visits": 0, "is_trusted": False})
200
  if stats.get("is_trusted", False):
201
+ final_score *= 2.0 # Relevant trusted domains ko double boost
202
+
203
+ final_score += (stats.get("user_visits", 0) * 0.05) + (stats.get("ai_visits", 0) * 0.1)
204
 
205
  seen_urls.add(doc_url)
206
+
207
+ doc_copy = doc.copy()
208
+ doc_copy["trust_stats"] = stats
209
+
210
+ scored_results.append((final_score, doc_copy))
211
 
212
  scored_results.sort(key=lambda x: x[0], reverse=True)
213
  best_score = scored_results[0][0] if scored_results else 0
 
242
  app = FastAPI(title="Nebulix Advanced Text AI & RAG Engine", lifespan=lifespan)
243
 
244
  app.add_middleware(
245
+ CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]
 
 
 
 
246
  )
247
 
 
248
  @app.get("/suggest")
249
  async def get_suggestions(q: str = Query(..., min_length=1)):
250
  q = q.lower()
251
  matches = [s for s in suggestion_list if s.startswith(q)][:7]
252
  return {"query": q, "suggestions": matches}
253
 
 
254
  @app.post("/track-click")
255
  async def track_url_click(url: str = Query(...)):
256
  if url not in URL_STATS:
 
269
  results, prediction, _ = await perform_core_search(q)
270
  return {"request_id": req_id, "query": q, "results_count": len(results), "results": results}
271
 
272
+ @app.get("/aimode")
273
+ async def ai_mode_short(q: str = Query(...)):
 
274
  req_id = str(uuid.uuid4())
275
+ results, _, _ = await perform_core_search(q)
276
+ if not results:
277
+ return {"request_id": req_id, "ai_response": "I couldn't find any results.", "results": []}
278
+
279
+ context = " ".join([f"{r.get('title', '')} - {r.get('description', '')}" for r in results[:3]])
280
+ messages = [
281
+ {"role": "system", "content": "Provide a brief 2-3 sentence summary based only on the provided context."},
282
+ {"role": "user", "content": f"Query: {q}\nContext: {context}"}
283
+ ]
284
+
285
+ ai_reply = await run_ai_task(messages, 150, query_key=f"short_{q}")
286
+ return {"request_id": req_id, "ai_response": ai_reply, "results": results}
287
+
288
+
289
+ async def process_rag_chat(q: str):
290
+ req_id = str(uuid.uuid4())
291
+ needs_rag = any(w in q.lower() for w in ["what", "who", "where", "how", "why", "tell me", "explain", "latest", "best"])
292
 
293
  results, _, _ = await perform_core_search(q)
294
  rag_context = ""
295
  used_sources = []
296
 
297
  if needs_rag and results:
298
+ # 🔥 SMART SCRAPING FIX:
299
+ # Ab AI blindly 'sabse jyada visits' wali site pe nahi jayega.
300
+ # perform_core_search ne already relevant results ko top pe rakha hai,
301
+ # isliye hum sidha Top 10 Context-Relevant URLs ko hi scrape karenge.
302
  top_urls = results[:10]
303
 
 
304
  tasks = [scrape_and_find_best_chunk(doc["url"], q) for doc in top_urls]
305
  scraped_chunks = await asyncio.gather(*tasks)
306
 
 
309
  doc = top_urls[i]
310
  url = doc["url"]
311
 
312
+ # Trust Tracking: Mark AI visits for this relevant URL
313
  if url not in URL_STATS: URL_STATS[url] = {"user_visits": 0, "ai_visits": 0, "is_trusted": False}
314
  URL_STATS[url]["ai_visits"] += 1
315
  if URL_STATS[url]["ai_visits"] >= 100:
316
  URL_STATS[url]["is_trusted"] = True
317
+
318
+ doc["trust_stats"] = URL_STATS[url]
319
 
 
320
  if chunk:
321
  context_parts.append(f"Source [{i+1}] ({url}): {chunk}")
322
  used_sources.append(doc)
 
326
 
327
  rag_context = "\n".join(context_parts)
328
 
329
+ system_prompt = "You are a Smart AI Search Assistant. Answer accurately using the provided live context data. Always mention the Source URL if you use its information."
330
  if not needs_rag:
331
  system_prompt = "You are a helpful AI Assistant. Answer the user's query conversationally."
332
 
 
345
  "results": results[:15]
346
  }
347
 
348
+ @app.get("/aimodechat")
349
+ async def ai_mode_chat_get(q: str = Query(...)):
350
+ return await process_rag_chat(q)
351
+
352
+ @app.post("/aimodechat")
353
+ async def ai_mode_chat_post(q: str = Form(...)):
354
+ return await process_rag_chat(q)
355
+
356
  if __name__ == "__main__":
 
 
357
  uvicorn.run(app, host="0.0.0.0", port=7860)