Nebulixlabs commited on
Commit
a2ffc85
·
verified ·
1 Parent(s): 8f9633b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -75
app.py CHANGED
@@ -18,7 +18,7 @@ from typing import List, Dict, Any, Tuple, Set
18
  from fastapi import FastAPI, Query, Form
19
  from fastapi.responses import JSONResponse
20
  from fastapi.middleware.cors import CORSMiddleware
21
- from huggingface_hub import hf_hub_download
22
  import aiohttp
23
  from bs4 import BeautifulSoup
24
  import torch
@@ -48,7 +48,6 @@ MAX_CONTENT_CONCURRENCY = 10
48
  content_semaphore = asyncio.Semaphore(MAX_CONTENT_CONCURRENCY)
49
 
50
  # --- 1. DETERMINISTIC PREPROCESSING & CONSTANTS ---
51
- # Expanded stop words to include question words, so they don't overpower the core query
52
  STOP_WORDS = {"the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "is", "are", "by", "this", "that", "it", "what", "who", "where", "when", "why", "how"}
53
  ADULT_KEYWORDS = {
54
  "porn", "sex", "nude", "nsfw", "xxx", "adult", "erotic", "onlyfans",
@@ -117,9 +116,17 @@ def detect_query_intent(query_tokens: List[str]) -> Dict[str, Any]:
117
 
118
  # --- 3. DEDICATED WEB CONTENT EXTRACTION ---
119
  async def extract_page_content(url: str) -> Dict[str, Any]:
 
 
 
 
 
 
 
120
  if url in CONTENT_CACHE:
121
  return CONTENT_CACHE[url]
122
 
 
123
  async with content_semaphore:
124
  try:
125
  async with aiohttp.ClientSession() as session:
@@ -174,6 +181,7 @@ def rank_chunks_for_rag(text: str, query_tokens: List[str]) -> List[str]:
174
  class HybridSearchIndex:
175
  def __init__(self):
176
  self.docs = []
 
177
  self.total_docs = 0
178
  self.vocab = set()
179
  self.df = defaultdict(int)
@@ -184,16 +192,24 @@ class HybridSearchIndex:
184
  self.b = 0.75
185
 
186
  def build(self, raw_data: List[dict]):
187
- print("📥 Building Advanced Deterministic Search Index...")
188
  total_len = 0
189
  suggestion_counter = Counter()
190
 
191
  for doc_id, doc in enumerate(raw_data):
192
  self.docs.append(doc)
 
 
193
  title_tokens = tokenize(doc.get("_fast_title", ""))
194
  desc_tokens = tokenize(doc.get("_fast_desc", ""))
195
  domain_tokens = tokenize(doc.get("_fast_domain", ""))
196
- all_tokens = title_tokens + desc_tokens + domain_tokens
 
 
 
 
 
 
197
 
198
  doc["_token_len"] = len(all_tokens)
199
  total_len += len(all_tokens)
@@ -213,7 +229,7 @@ class HybridSearchIndex:
213
  self.total_docs = len(self.docs)
214
  self.avg_doc_len = total_len / max(1, self.total_docs)
215
  self.suggestions = suggestion_counter.most_common(40000)
216
- print(f"🚀 Indexing Complete! {self.total_docs} records ready.")
217
 
218
  def idf(self, term: str) -> float:
219
  df = self.df.get(term, 0)
@@ -236,35 +252,62 @@ class HybridSearchIndex:
236
  desc = doc.get("_fast_desc", "")
237
  domain = doc.get("_fast_domain", "")
238
  url = doc.get("_fast_path", "")
 
 
 
239
 
240
  title_toks = tokenize(title, False)
241
  desc_toks = tokenize(desc, False)
 
242
  query_toks_full = tokenize(query_str, False)
243
 
244
- scores = {"bm25": 0.0, "title": 0.0, "phrase": 0.0, "exact": 0.0, "url": 0.0, "domain": 0.0, "quality": 0.0, "freshness": 0.0, "proximity": 0.0, "root_domain": 0.0}
245
 
246
  doc_len = doc.get("_token_len", 1)
 
 
 
247
  for term in query_tokens_sorted:
248
- tf = title_toks.count(term) * 2 + desc_toks.count(term)
 
 
 
 
 
 
249
  if tf > 0:
250
  tf_num = tf * (self.k1 + 1)
251
  tf_den = tf + self.k1 * (1 - self.b + self.b * (doc_len / self.avg_doc_len))
252
  scores["bm25"] += self.idf(term) * (tf_num / tf_den)
253
 
254
  q_norm = normalize_text(query_str)
255
- if q_norm and q_norm in normalize_text(title): scores["exact"] = 1.0
256
- elif q_norm and q_norm in normalize_text(desc): scores["exact"] = 0.5
 
 
 
 
 
 
257
 
258
  if len(query_toks_full) > 1:
259
  phrase_overlap = 0
260
  for i in range(len(query_toks_full) - 1):
261
  bigram = f"{query_toks_full[i]} {query_toks_full[i+1]}"
262
- if bigram in normalize_text(title): phrase_overlap += 1.0
263
- elif bigram in normalize_text(desc): phrase_overlap += 0.5
 
 
264
  scores["phrase"] = phrase_overlap / (len(query_toks_full) - 1)
265
 
266
  title_overlap = sum(1 for t in query_tokens_sorted if t in title_toks)
267
  scores["title"] = title_overlap / max(1, len(query_tokens_sorted))
 
 
 
 
 
 
268
 
269
  q_joined = "".join(query_tokens_sorted)
270
  domain_clean = domain.split('.')[0]
@@ -277,7 +320,7 @@ class HybridSearchIndex:
277
  if len(positions) > 1:
278
  scores["proximity"] = 1.0 / (1.0 + (max(positions) - min(positions)))
279
 
280
- year_match = re.search(r'\b(202[0-6])\b', url + " " + desc)
281
  if year_match: scores["freshness"] = math.exp(-(datetime.now().year - int(year_match.group(1))) / 2.0)
282
  else: scores["freshness"] = 0.3
283
 
@@ -295,22 +338,16 @@ class HybridSearchIndex:
295
 
296
  def search(self, query: str, debug: bool = False) -> Tuple[List[dict], Dict[str, Any]]:
297
  query_norm = normalize_text(query)
298
- # Tokenize (Removes standard stop words)
299
  query_tokens = tokenize(query_norm)
300
-
301
- # We append normal words that user typed but were omitted, if they exist
302
  raw_tokens = normalize_text(query).split()
303
 
304
  expanded_tokens = list(query_tokens)
305
  for t in query_tokens:
306
  if t in EXPANSION_DICT: expanded_tokens.extend(EXPANSION_DICT[t])
307
 
308
- # STRING SORTING ALGORITHM BY IMPORTANCE (Solves 'what is github' issue)
309
- # We sort tokens so the RAREST words (Highest IDF) are verified first.
310
- # Example: 'github' or '5000' will be at the very front of the array. 'what' or 'is' drops to the back.
311
  sorted_tokens = list(dict.fromkeys(expanded_tokens))
312
  if not sorted_tokens:
313
- sorted_tokens = raw_tokens # Fallback if query was literally just "what is"
314
 
315
  sorted_tokens.sort(key=lambda t: self.idf(t), reverse=True)
316
 
@@ -339,36 +376,46 @@ class HybridSearchIndex:
339
  w_phrase = WEIGHTS["W_PHRASE"] + (1.5 if intent in ("DEFINITION", "COMPARISON") else 0.0)
340
 
341
  final_score = (
342
- WEIGHTS["W_BM25"] * norm_bm25 + w_title * features["title"] + w_phrase * features["phrase"] +
343
- WEIGHTS["W_EXACT"] * features["exact"] + WEIGHTS["W_URL"] * features["url"] +
344
- w_domain * features["domain"] + w_freshness * features["freshness"] +
345
- WEIGHTS["W_QUALITY"] * features["quality"] + WEIGHTS["W_PROXIMITY"] * features["proximity"] +
346
- WEIGHTS["W_ROOT_DOMAIN"] * features["root_domain"]
 
 
 
 
 
 
 
347
  )
348
 
349
- # --- CORE TERM PENALTY (CRITICAL FIX) ---
350
- # If the #1 most important sorted word (e.g., 'github' or '5000') is completely missing from the website,
351
- # we apply a massive 90% score drop. This kills random "DNS IP" matches instantly!
352
  if len(sorted_tokens) > 0:
353
  top_term = sorted_tokens[0]
354
  doc_title = tokenize(doc.get("_fast_title", ""), False)
355
  doc_desc = tokenize(doc.get("_fast_desc", ""), False)
356
- if top_term not in doc_title and top_term not in doc_desc and top_term not in doc.get("_fast_domain", ""):
 
 
 
357
  final_score *= 0.1 # 90% Penalty
358
 
359
  if len(sorted_tokens) > 1:
360
  second_term = sorted_tokens[1]
361
- if second_term not in doc_title and second_term not in doc_desc and second_term not in doc.get("_fast_domain", ""):
362
- final_score *= 0.4 # 60% Penalty for missing second most important word
363
- # -----------------------------------------
364
-
365
  if final_score > 0.05:
366
  doc_out = {
 
367
  "title": doc.get("_fast_title", ""),
368
  "description": doc.get("_fast_desc", ""),
 
369
  "url": doc.get("url", ""),
370
  "domain": doc.get("_fast_domain", ""),
371
- "match_score": round(final_score, 4)
 
372
  }
373
  if debug:
374
  doc_out["_debug_features"] = features
@@ -392,31 +439,66 @@ class HybridSearchIndex:
392
  search_index = HybridSearchIndex()
393
 
394
  def load_and_index_dataset():
395
- dataset_path = "dataset.jsonl"
396
- if HF_TOKEN:
397
- try: dataset_path = hf_hub_download(repo_id="Nebulixlabs/Ultimate-Search-Engine-Dataset", filename="dataset.jsonl", repo_type="dataset", token=HF_TOKEN)
398
- except Exception: pass
399
-
 
 
 
 
 
 
 
400
  raw_data = []
401
- if os.path.exists(dataset_path):
402
- with open(dataset_path, "r", encoding="utf-8") as f:
403
- for i, line in enumerate(f):
404
- if i >= 150000: break
405
- if line.strip():
 
 
 
 
 
 
406
  try:
407
  doc = json.loads(line)
408
  url = doc.get("url", "")
409
- title_str = doc.get("title", "")
410
- desc_str = doc.get("description", "")
 
 
 
 
 
 
 
 
 
 
 
 
411
  parsed_url = urllib.parse.urlparse(url)
412
 
413
- doc["_fast_domain"] = parsed_url.netloc.lower().replace("www.", "")
414
  doc["_fast_path"] = parsed_url.path.strip("/")
415
  doc["_fast_title"] = title_str
416
  doc["_fast_desc"] = desc_str
417
- doc["_is_adult"] = doc.get("tag", "general").lower() in ["adult", "nsfw"] or any(aw in (title_str+desc_str+url).lower() for aw in ADULT_KEYWORDS)
 
 
 
 
 
 
418
  raw_data.append(doc)
419
- except: pass
 
 
 
 
420
  search_index.build(raw_data)
421
 
422
 
@@ -442,13 +524,9 @@ def load_ai_models():
442
 
443
  async def run_ai_task(messages, max_tokens: int, query_key: str):
444
  if not ai_generator: return "⚠️ AI Model is starting up."
 
445
 
446
- if query_key in AI_RESPONSE_CACHE:
447
- return AI_RESPONSE_CACHE[query_key]
448
-
449
- # --- FCFS / GRACEFUL DEGRADATION LOGIC ---
450
- try:
451
- await asyncio.wait_for(ai_semaphore.acquire(), timeout=0.1)
452
  except asyncio.TimeoutError:
453
  return "⚠️ AI Servers are fully occupied right now. Displaying your ultra-fast search results below! (Please try AI Mode again in a few seconds)"
454
 
@@ -498,11 +576,11 @@ async def pure_search(q: str = Query(...), content: bool = Query(False), debug:
498
  results, intent_info = await asyncio.to_thread(search_index.search, q, debug)
499
  top_20 = results[:STRICT_RESULT_LIMIT]
500
 
501
- if content and top_20:
502
- tasks = [extract_page_content(r["url"]) for r in top_20]
503
- extracted_data = await asyncio.gather(*tasks)
504
- for r, ext in zip(top_20, extracted_data):
505
- r["content"] = ext.get("content", "") if ext.get("success") else ""
506
 
507
  return {"request_id": req_id, "query": q, "results_count": len(top_20), "results": top_20}
508
 
@@ -510,39 +588,47 @@ async def process_aimode(q: str, include_content: bool, debug: bool, max_ai_toke
510
  req_id = str(uuid.uuid4())
511
  cache_key = f"ai_{normalize_text(q)}_{max_ai_tokens}"
512
 
 
 
 
513
  if cache_key in AI_RESPONSE_CACHE:
514
- results, _ = await asyncio.to_thread(search_index.search, q, debug)
 
 
 
515
  return {
516
  "request_id": req_id,
517
  "query": q,
518
  "ai_response": AI_RESPONSE_CACHE[cache_key],
519
- "results_count": len(results[:STRICT_RESULT_LIMIT]),
520
- "results": results[:STRICT_RESULT_LIMIT],
521
  "sources": [],
522
  "cached": True
523
  }
524
 
525
- results, intent_info = await asyncio.to_thread(search_index.search, q, debug)
526
- top_20 = results[:STRICT_RESULT_LIMIT]
527
  query_tokens = tokenize(q)
528
-
529
  rag_context_parts = []
530
  used_sources = []
531
 
532
  if top_20:
533
- tasks = [extract_page_content(r["url"]) for r in top_20[:3]]
534
- extracted_data = await asyncio.gather(*tasks)
535
-
536
  valid_sources_count = 0
537
- for i, (r, ext) in enumerate(zip(top_20[:3], extracted_data)):
538
- if include_content: r["content"] = ext.get("content", "") if ext.get("success") else ""
 
 
 
 
539
 
540
- if ext.get("success") and ext.get("content") and valid_sources_count < 3:
541
- best_chunks = rank_chunks_for_rag(ext["content"], query_tokens)
542
  if best_chunks:
543
  rag_context_parts.append(f"[{valid_sources_count+1}]: {' ... '.join(best_chunks)}")
544
  used_sources.append({"id": valid_sources_count+1, "title": r["title"], "url": r["url"]})
545
  valid_sources_count += 1
 
 
 
 
546
 
547
  # STRICT COMPLETION PROMPT: Guarantees it finishes its sentence within the token limit gracefully.
548
  system_prompt = (
@@ -564,7 +650,6 @@ async def process_aimode(q: str, include_content: bool, debug: bool, max_ai_toke
564
  {"role": "user", "content": combined_input}
565
  ]
566
 
567
- # Generating the answer
568
  ai_reply = await run_ai_task(messages, max_tokens=max_ai_tokens, query_key=cache_key)
569
 
570
  return {
@@ -579,12 +664,10 @@ async def process_aimode(q: str, include_content: bool, debug: bool, max_ai_toke
579
 
580
  @app.get("/aimode")
581
  async def ai_mode_get(q: str = Query(...), content: bool = Query(False), debug: bool = Query(False)):
582
- # STRICTLY forced to 20 tokens as requested
583
  return await process_aimode(q, content, debug, max_ai_tokens=20)
584
 
585
  @app.post("/aimode")
586
  async def ai_mode_post(q: str = Form(...), content: bool = Form(False), debug: bool = Form(False)):
587
- # STRICTLY forced to 20 tokens as requested
588
  return await process_aimode(q, content, debug, max_ai_tokens=20)
589
 
590
  if __name__ == "__main__":
 
18
  from fastapi import FastAPI, Query, Form
19
  from fastapi.responses import JSONResponse
20
  from fastapi.middleware.cors import CORSMiddleware
21
+ from huggingface_hub import snapshot_download
22
  import aiohttp
23
  from bs4 import BeautifulSoup
24
  import torch
 
48
  content_semaphore = asyncio.Semaphore(MAX_CONTENT_CONCURRENCY)
49
 
50
  # --- 1. DETERMINISTIC PREPROCESSING & CONSTANTS ---
 
51
  STOP_WORDS = {"the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "is", "are", "by", "this", "that", "it", "what", "who", "where", "when", "why", "how"}
52
  ADULT_KEYWORDS = {
53
  "porn", "sex", "nude", "nsfw", "xxx", "adult", "erotic", "onlyfans",
 
116
 
117
  # --- 3. DEDICATED WEB CONTENT EXTRACTION ---
118
  async def extract_page_content(url: str) -> Dict[str, Any]:
119
+ # 1. First check if content is natively loaded inside our dataset to save time
120
+ if url in search_index.url_to_doc:
121
+ doc = search_index.url_to_doc[url]
122
+ if doc.get("_fast_content"):
123
+ return {"url": url, "title": doc.get("_fast_title", ""), "content": doc.get("_fast_content", ""), "success": True}
124
+
125
+ # 2. Check general content cache
126
  if url in CONTENT_CACHE:
127
  return CONTENT_CACHE[url]
128
 
129
+ # 3. Fallback to live extraction if absolutely not in DB
130
  async with content_semaphore:
131
  try:
132
  async with aiohttp.ClientSession() as session:
 
181
  class HybridSearchIndex:
182
  def __init__(self):
183
  self.docs = []
184
+ self.url_to_doc = {}
185
  self.total_docs = 0
186
  self.vocab = set()
187
  self.df = defaultdict(int)
 
192
  self.b = 0.75
193
 
194
  def build(self, raw_data: List[dict]):
195
+ print(f"📥 Building Advanced Deterministic Search Index from {len(raw_data)} documents...")
196
  total_len = 0
197
  suggestion_counter = Counter()
198
 
199
  for doc_id, doc in enumerate(raw_data):
200
  self.docs.append(doc)
201
+ self.url_to_doc[doc.get("url", "")] = doc
202
+
203
  title_tokens = tokenize(doc.get("_fast_title", ""))
204
  desc_tokens = tokenize(doc.get("_fast_desc", ""))
205
  domain_tokens = tokenize(doc.get("_fast_domain", ""))
206
+ headings_tokens = tokenize(doc.get("_fast_headings", ""))
207
+ snippet_tokens = tokenize(doc.get("_fast_snippet", ""))
208
+
209
+ # Using partial content tokens to save memory while maximizing recall
210
+ content_tokens = tokenize(doc.get("_fast_content", ""))[:400]
211
+
212
+ all_tokens = title_tokens + desc_tokens + domain_tokens + headings_tokens + snippet_tokens + content_tokens
213
 
214
  doc["_token_len"] = len(all_tokens)
215
  total_len += len(all_tokens)
 
229
  self.total_docs = len(self.docs)
230
  self.avg_doc_len = total_len / max(1, self.total_docs)
231
  self.suggestions = suggestion_counter.most_common(40000)
232
+ print(f"🚀 Indexing Complete! {self.total_docs} records fully embedded.")
233
 
234
  def idf(self, term: str) -> float:
235
  df = self.df.get(term, 0)
 
252
  desc = doc.get("_fast_desc", "")
253
  domain = doc.get("_fast_domain", "")
254
  url = doc.get("_fast_path", "")
255
+ headings = doc.get("_fast_headings", "")
256
+ content = doc.get("_fast_content", "")
257
+ snippet = doc.get("_fast_snippet", "")
258
 
259
  title_toks = tokenize(title, False)
260
  desc_toks = tokenize(desc, False)
261
+ headings_toks = tokenize(headings, False)
262
  query_toks_full = tokenize(query_str, False)
263
 
264
+ scores = {"bm25": 0.0, "title": 0.0, "phrase": 0.0, "exact": 0.0, "url": 0.0, "domain": 0.0, "quality": 0.0, "freshness": 0.0, "proximity": 0.0, "root_domain": 0.0, "headings": 0.0, "body": 0.0}
265
 
266
  doc_len = doc.get("_token_len", 1)
267
+ content_lower = content.lower()
268
+ snippet_lower = snippet.lower()
269
+
270
  for term in query_tokens_sorted:
271
+ # TF spans across newly added dataset fields
272
+ tf = (title_toks.count(term) * 4 +
273
+ headings_toks.count(term) * 3 +
274
+ desc_toks.count(term) * 2 +
275
+ snippet_lower.count(term) * 2 +
276
+ content_lower.count(term))
277
+
278
  if tf > 0:
279
  tf_num = tf * (self.k1 + 1)
280
  tf_den = tf + self.k1 * (1 - self.b + self.b * (doc_len / self.avg_doc_len))
281
  scores["bm25"] += self.idf(term) * (tf_num / tf_den)
282
 
283
  q_norm = normalize_text(query_str)
284
+ title_norm = normalize_text(title)
285
+ desc_norm = normalize_text(desc)
286
+ content_norm = normalize_text(content)
287
+ headings_norm = normalize_text(headings)
288
+
289
+ if q_norm and q_norm in title_norm: scores["exact"] = 1.0
290
+ elif q_norm and q_norm in desc_norm: scores["exact"] = 0.5
291
+ elif q_norm and q_norm in content_norm: scores["exact"] = 0.2
292
 
293
  if len(query_toks_full) > 1:
294
  phrase_overlap = 0
295
  for i in range(len(query_toks_full) - 1):
296
  bigram = f"{query_toks_full[i]} {query_toks_full[i+1]}"
297
+ if bigram in title_norm: phrase_overlap += 1.0
298
+ elif bigram in headings_norm: phrase_overlap += 0.8
299
+ elif bigram in desc_norm: phrase_overlap += 0.5
300
+ elif bigram in content_norm: phrase_overlap += 0.3
301
  scores["phrase"] = phrase_overlap / (len(query_toks_full) - 1)
302
 
303
  title_overlap = sum(1 for t in query_tokens_sorted if t in title_toks)
304
  scores["title"] = title_overlap / max(1, len(query_tokens_sorted))
305
+
306
+ headings_overlap = sum(1 for t in query_tokens_sorted if t in headings_toks)
307
+ scores["headings"] = headings_overlap / max(1, len(query_tokens_sorted))
308
+
309
+ body_overlap = sum(1 for t in query_tokens_sorted if t in content_lower)
310
+ scores["body"] = body_overlap / max(1, len(query_tokens_sorted))
311
 
312
  q_joined = "".join(query_tokens_sorted)
313
  domain_clean = domain.split('.')[0]
 
320
  if len(positions) > 1:
321
  scores["proximity"] = 1.0 / (1.0 + (max(positions) - min(positions)))
322
 
323
+ year_match = re.search(r'\b(202[0-6])\b', url + " " + desc + " " + snippet)
324
  if year_match: scores["freshness"] = math.exp(-(datetime.now().year - int(year_match.group(1))) / 2.0)
325
  else: scores["freshness"] = 0.3
326
 
 
338
 
339
  def search(self, query: str, debug: bool = False) -> Tuple[List[dict], Dict[str, Any]]:
340
  query_norm = normalize_text(query)
 
341
  query_tokens = tokenize(query_norm)
 
 
342
  raw_tokens = normalize_text(query).split()
343
 
344
  expanded_tokens = list(query_tokens)
345
  for t in query_tokens:
346
  if t in EXPANSION_DICT: expanded_tokens.extend(EXPANSION_DICT[t])
347
 
 
 
 
348
  sorted_tokens = list(dict.fromkeys(expanded_tokens))
349
  if not sorted_tokens:
350
+ sorted_tokens = raw_tokens
351
 
352
  sorted_tokens.sort(key=lambda t: self.idf(t), reverse=True)
353
 
 
376
  w_phrase = WEIGHTS["W_PHRASE"] + (1.5 if intent in ("DEFINITION", "COMPARISON") else 0.0)
377
 
378
  final_score = (
379
+ WEIGHTS["W_BM25"] * norm_bm25 +
380
+ w_title * features["title"] +
381
+ w_phrase * features["phrase"] +
382
+ WEIGHTS["W_EXACT"] * features["exact"] +
383
+ WEIGHTS["W_URL"] * features["url"] +
384
+ w_domain * features["domain"] +
385
+ w_freshness * features["freshness"] +
386
+ WEIGHTS["W_QUALITY"] * features["quality"] +
387
+ WEIGHTS["W_PROXIMITY"] * features["proximity"] +
388
+ WEIGHTS["W_ROOT_DOMAIN"] * features["root_domain"] +
389
+ WEIGHTS["W_HEADINGS"] * features["headings"] +
390
+ WEIGHTS["W_BODY"] * features["body"]
391
  )
392
 
393
+ # Penalty for Missing Core Word
 
 
394
  if len(sorted_tokens) > 0:
395
  top_term = sorted_tokens[0]
396
  doc_title = tokenize(doc.get("_fast_title", ""), False)
397
  doc_desc = tokenize(doc.get("_fast_desc", ""), False)
398
+ doc_content = doc.get("_fast_content", "").lower()
399
+ doc_headings = tokenize(doc.get("_fast_headings", ""), False)
400
+
401
+ if top_term not in doc_title and top_term not in doc_desc and top_term not in doc.get("_fast_domain", "") and top_term not in doc_content and top_term not in doc_headings:
402
  final_score *= 0.1 # 90% Penalty
403
 
404
  if len(sorted_tokens) > 1:
405
  second_term = sorted_tokens[1]
406
+ if second_term not in doc_title and second_term not in doc_desc and second_term not in doc.get("_fast_domain", "") and second_term not in doc_content and second_term not in doc_headings:
407
+ final_score *= 0.4 # 60% Penalty
408
+
 
409
  if final_score > 0.05:
410
  doc_out = {
411
+ "doc_id": doc.get("doc_id", ""),
412
  "title": doc.get("_fast_title", ""),
413
  "description": doc.get("_fast_desc", ""),
414
+ "snippet": doc.get("_fast_snippet", ""),
415
  "url": doc.get("url", ""),
416
  "domain": doc.get("_fast_domain", ""),
417
+ "match_score": round(final_score, 4),
418
+ "_internal_content": doc.get("_fast_content", "") # Passed internally for Fast Content injection
419
  }
420
  if debug:
421
  doc_out["_debug_features"] = features
 
439
  search_index = HybridSearchIndex()
440
 
441
  def load_and_index_dataset():
442
+ print("📦 Automatically Downloading 'Nebulixlabs/web-search-dataset' ...")
443
+ dataset_path = "."
444
+ try:
445
+ dataset_path = snapshot_download(
446
+ repo_id="Nebulixlabs/web-search-dataset",
447
+ repo_type="dataset",
448
+ allow_patterns="data/*.jsonl",
449
+ token=HF_TOKEN
450
+ )
451
+ except Exception as e:
452
+ print("⚠️ Direct HF Hub fetch failed, attempting local 'data' folder mapping. Error:", e)
453
+
454
  raw_data = []
455
+ data_folder = os.path.join(dataset_path, "data")
456
+ if not os.path.exists(data_folder):
457
+ data_folder = "data" # Fallback local
458
+
459
+ if os.path.exists(data_folder):
460
+ jsonl_files = [f for f in os.listdir(data_folder) if f.endswith(".jsonl")]
461
+ for file in jsonl_files:
462
+ file_path = os.path.join(data_folder, file)
463
+ with open(file_path, "r", encoding="utf-8") as f:
464
+ for line in f:
465
+ if not line.strip(): continue
466
  try:
467
  doc = json.loads(line)
468
  url = doc.get("url", "")
469
+
470
+ # Parsing New Nested Schema
471
+ metadata = doc.get("metadata") or {}
472
+ content_dict = doc.get("content") or {}
473
+
474
+ title_str = metadata.get("title") or ""
475
+ desc_str = metadata.get("description") or ""
476
+ snippet_str = content_dict.get("snippet") or ""
477
+ main_text = content_dict.get("main_text") or ""
478
+
479
+ headings = content_dict.get("headings") or {}
480
+ h1_str = " ".join(headings.get("h1") or [])
481
+ h2_str = " ".join(headings.get("h2") or [])
482
+
483
  parsed_url = urllib.parse.urlparse(url)
484
 
485
+ doc["_fast_domain"] = doc.get("domain") or parsed_url.netloc.lower().replace("www.", "")
486
  doc["_fast_path"] = parsed_url.path.strip("/")
487
  doc["_fast_title"] = title_str
488
  doc["_fast_desc"] = desc_str
489
+ doc["_fast_snippet"] = snippet_str
490
+ doc["_fast_content"] = main_text
491
+ doc["_fast_headings"] = h1_str + " " + h2_str
492
+
493
+ combined_adult_check = f"{title_str} {desc_str} {url} {snippet_str} {main_text[:200]}".lower()
494
+ doc["_is_adult"] = any(aw in combined_adult_check for aw in ADULT_KEYWORDS)
495
+
496
  raw_data.append(doc)
497
+ if len(raw_data) >= 150000: break
498
+ except Exception:
499
+ pass
500
+ if len(raw_data) >= 150000: break
501
+
502
  search_index.build(raw_data)
503
 
504
 
 
524
 
525
  async def run_ai_task(messages, max_tokens: int, query_key: str):
526
  if not ai_generator: return "⚠️ AI Model is starting up."
527
+ if query_key in AI_RESPONSE_CACHE: return AI_RESPONSE_CACHE[query_key]
528
 
529
+ try: await asyncio.wait_for(ai_semaphore.acquire(), timeout=0.1)
 
 
 
 
 
530
  except asyncio.TimeoutError:
531
  return "⚠️ AI Servers are fully occupied right now. Displaying your ultra-fast search results below! (Please try AI Mode again in a few seconds)"
532
 
 
576
  results, intent_info = await asyncio.to_thread(search_index.search, q, debug)
577
  top_20 = results[:STRICT_RESULT_LIMIT]
578
 
579
+ for r in top_20:
580
+ if content:
581
+ r["content"] = r.get("_internal_content", "")
582
+ # Remove massive string payload to keep payload size lightweight unless user explicitly asked for it
583
+ r.pop("_internal_content", None)
584
 
585
  return {"request_id": req_id, "query": q, "results_count": len(top_20), "results": top_20}
586
 
 
588
  req_id = str(uuid.uuid4())
589
  cache_key = f"ai_{normalize_text(q)}_{max_ai_tokens}"
590
 
591
+ results, intent_info = await asyncio.to_thread(search_index.search, q, debug)
592
+ top_20 = results[:STRICT_RESULT_LIMIT]
593
+
594
  if cache_key in AI_RESPONSE_CACHE:
595
+ for r in top_20:
596
+ if include_content: r["content"] = r.get("_internal_content", "")
597
+ r.pop("_internal_content", None)
598
+
599
  return {
600
  "request_id": req_id,
601
  "query": q,
602
  "ai_response": AI_RESPONSE_CACHE[cache_key],
603
+ "results_count": len(top_20),
604
+ "results": top_20,
605
  "sources": [],
606
  "cached": True
607
  }
608
 
 
 
609
  query_tokens = tokenize(q)
 
610
  rag_context_parts = []
611
  used_sources = []
612
 
613
  if top_20:
 
 
 
614
  valid_sources_count = 0
615
+ for r in top_20[:3]:
616
+ # Directly parse context from our ultra-fast pre-loaded dataset without web scraping!
617
+ doc_content = r.get("_internal_content", "")
618
+
619
+ if include_content:
620
+ r["content"] = doc_content
621
 
622
+ if doc_content and valid_sources_count < 3:
623
+ best_chunks = rank_chunks_for_rag(doc_content, query_tokens)
624
  if best_chunks:
625
  rag_context_parts.append(f"[{valid_sources_count+1}]: {' ... '.join(best_chunks)}")
626
  used_sources.append({"id": valid_sources_count+1, "title": r["title"], "url": r["url"]})
627
  valid_sources_count += 1
628
+
629
+ # Cleanup Internal Fields globally before returning JSON Payload
630
+ for r in top_20:
631
+ r.pop("_internal_content", None)
632
 
633
  # STRICT COMPLETION PROMPT: Guarantees it finishes its sentence within the token limit gracefully.
634
  system_prompt = (
 
650
  {"role": "user", "content": combined_input}
651
  ]
652
 
 
653
  ai_reply = await run_ai_task(messages, max_tokens=max_ai_tokens, query_key=cache_key)
654
 
655
  return {
 
664
 
665
  @app.get("/aimode")
666
  async def ai_mode_get(q: str = Query(...), content: bool = Query(False), debug: bool = Query(False)):
 
667
  return await process_aimode(q, content, debug, max_ai_tokens=20)
668
 
669
  @app.post("/aimode")
670
  async def ai_mode_post(q: str = Form(...), content: bool = Form(False), debug: bool = Form(False)):
 
671
  return await process_aimode(q, content, debug, max_ai_tokens=20)
672
 
673
  if __name__ == "__main__":