Nebulixlabs commited on
Commit
dc2631b
·
verified ·
1 Parent(s): 56119d9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -182
app.py CHANGED
@@ -6,6 +6,7 @@ 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
@@ -17,47 +18,42 @@ import aiohttp
17
  from bs4 import BeautifulSoup
18
  from transformers import pipeline
19
 
20
- # --- 1. SETUP & CPU ALLOCATION ---
21
  TOTAL_CORES = multiprocessing.cpu_count()
22
- AI_CORES = max(1, int(TOTAL_CORES * 0.4))
23
  torch.set_num_threads(AI_CORES)
24
- print(f"🖥️ Total Cores: {TOTAL_CORES} | Allocating {AI_CORES} cores strictly to AI.")
25
 
26
  raw_data = []
27
- suggestion_list = []
28
  bm25_index = None
29
- MAX_CONCURRENT_AI = max(1, AI_CORES)
30
  ai_semaphore = asyncio.Semaphore(MAX_CONCURRENT_AI)
31
  ai_generator = None
32
-
33
- # --- TRACKING & TRUST SYSTEM ---
34
- URL_STATS = {} # Format: { "https://example.com": {"user_visits": 0, "ai_visits": 0, "is_trusted": False} }
35
-
36
- # --- AI CACHING SYSTEM ---
37
  AI_RESPONSE_CACHE = {}
38
  MAX_CACHE_SIZE = 5000
39
- STOP_WORDS = {"the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "is", "are"}
 
 
40
  HF_TOKEN = os.getenv("HF_TOKEN")
41
 
42
- # 🔥 MASSIVE DESI & MODERN ADULT KEYWORDS LIST
43
  ADULT_KEYWORDS = {
44
  "porn", "sex", "nude", "nsfw", "xxx", "adult", "erotic", "onlyfans",
45
  "mms", "chudai", "bhabhi", "devar", "savita bhabhi", "xvideos", "pornhub",
46
- "brazzers", "mia khalifa", "bf video", "blue film", "kamasutra", "nude",
47
  "boobs", "tits", "ass", "booty", "naked", "desi porn", "hot desi", "xnxx",
48
  "kacha badam", "ullu", "kooku", "mastram", "gandi baat", "nangi", "nanga",
49
  "sex tape", "milf", "stepmom", "incest", "playboy", "scandal", "leaked"
50
  }
51
 
52
- # --- 2. ADVANCED NLP & INDEXING ---
53
  def tokenize(text):
54
  if not text: return []
55
- tokens = re.findall(r'[a-z0-9]+', str(text).lower())
56
- return [t for t in tokens if t not in STOP_WORDS]
57
 
58
  def load_and_index_dataset():
59
- global bm25_index, raw_data, suggestion_list
60
- print("📥 Loading Dataset & Building Deep Index...")
61
  dataset_path = "dataset.jsonl"
62
 
63
  if HF_TOKEN:
@@ -68,184 +64,156 @@ def load_and_index_dataset():
68
  repo_type="dataset",
69
  token=HF_TOKEN
70
  )
71
- except Exception as e:
72
- print(f"⚠️ Hugging Face Download failed: {e}")
73
 
74
  tokenized_corpus = []
75
- MAX_RECORDS_TO_LOAD = 150000 # Prevent OOM Crash
76
-
77
  if os.path.exists(dataset_path):
78
  with open(dataset_path, "r", encoding="utf-8") as f:
79
  for i, line in enumerate(f):
80
- if i >= MAX_RECORDS_TO_LOAD:
81
- break
82
-
83
  if line.strip():
84
  try:
85
  doc = json.loads(line)
86
  url = doc.get("url", "")
87
-
88
- if url not in URL_STATS:
89
- URL_STATS[url] = {"user_visits": 0, "ai_visits": 0, "is_trusted": False}
90
-
91
- raw_data.append(doc)
92
-
93
  title_str = doc.get("title", "")
94
- if title_str and len(title_str) < 60:
95
- suggestion_list.append(title_str.lower())
96
 
97
- title_tokens = tokenize(title_str)
98
- desc_tokens = tokenize(doc.get("description", ""))
99
- url_tokens = tokenize(url)
 
 
 
 
 
 
 
 
 
 
100
 
101
- boosted_tokens = (title_tokens * 5) + (desc_tokens * 3) + url_tokens
102
- tokenized_corpus.append(boosted_tokens)
103
- except:
104
- pass
 
 
 
 
 
 
 
 
105
 
106
  if tokenized_corpus:
107
- print("⚡ Building High-Speed BM25 Okapi Index...")
108
- suggestion_list = sorted(list(set(suggestion_list)))
109
  bm25_index = BM25Okapi(tokenized_corpus)
110
- print(f"🚀 Indexing Complete! {len(raw_data)} records ready for search.")
111
- else:
112
- print("❌ Dataset file not found!")
113
 
114
  def load_ai_models():
115
  global ai_generator
116
- print("🤖 Loading Small & Fast 360M Text AI Model...")
117
  try:
118
- # 🔥 Using a small 360M parameter model for strict fast performance
119
  ai_generator = pipeline(
120
  "text-generation",
121
- model="HuggingFaceTB/SmolLM2-360M-Instruct",
122
  device=-1,
123
- torch_dtype=torch.float32
 
124
  )
125
- print("✅ Small AI Model Loaded! System ready.")
126
  except Exception as e:
127
  print(f"❌ Model Loading Failed: {e}")
128
 
129
- # --- 3. SMART SCRIPT RAG ---
130
- def chunk_text(text, chunk_size=120):
131
  words = text.split()
132
  return [" ".join(words[i:i+chunk_size]) for i in range(0, len(words), chunk_size)]
133
 
134
  async def scrape_and_find_best_chunk(url, query):
135
  try:
 
136
  async with aiohttp.ClientSession() as session:
137
- async with session.get(url, timeout=3) as resp:
138
  if resp.status == 200:
139
  html = await resp.text()
140
  soup = BeautifulSoup(html, "html.parser")
141
- tags_to_extract = ['p', 'h1', 'h2', 'h3']
142
- text = " ".join([tag.get_text(strip=True) for tag in soup.find_all(tags_to_extract)])
143
-
144
  if not text: return ""
145
 
146
  chunks = chunk_text(text)
147
  query_tokens = set(tokenize(query))
148
 
149
- best_chunk = ""
150
- max_score = 0
151
- early_stop_threshold = max(1, len(query_tokens) // 2)
152
-
153
  for chunk in chunks:
154
- chunk_tokens = set(tokenize(chunk))
155
- score = len(query_tokens.intersection(chunk_tokens))
156
  if score > max_score:
157
- max_score = score
158
- best_chunk = chunk
159
- if max_score >= early_stop_threshold and max_score > 0:
160
- break
161
  return best_chunk
162
  except:
163
  return ""
164
  return ""
165
 
166
- # --- 4. CORE SEARCH ALGORITHM & RANKING ---
167
  async def perform_core_search(raw_query: str):
168
- if not raw_data or not bm25_index:
169
- return [], "Search index not ready.", 0
170
 
171
  query = raw_query.strip().lower()
172
  query_tokens = tokenize(query)
173
- user_wants_adult = any(adult_word in query for adult_word in ADULT_KEYWORDS)
174
 
175
  scores = bm25_index.get_scores(query_tokens) if query_tokens else [1.0] * len(raw_data)
176
- scored_results = []
177
- seen_urls = set()
178
 
179
  for index, score in enumerate(scores):
180
  if score > 0.1 or not query_tokens:
181
  doc = raw_data[index]
182
- doc_url = doc.get("url", "")
183
-
184
  if doc_url in seen_urls: continue
185
-
186
- parsed_url = urllib.parse.urlparse(doc_url)
187
- domain = parsed_url.netloc.lower().replace("www.", "")
188
- path = parsed_url.path.strip("/")
189
-
190
- doc_title = doc.get("title", "").lower()
191
- doc_desc = doc.get("description", "").lower()
192
- doc_text = doc_title + " " + doc_desc + " " + doc_url
193
-
194
- is_adult_doc = doc.get("tag", "general").lower() in ["adult", "nsfw"] or any(aw in doc_text for aw in ADULT_KEYWORDS)
195
-
196
- if not user_wants_adult and is_adult_doc:
197
- continue
198
 
199
- final_score = score
200
- query_clean = query.replace(" ", "")
201
  domain_clean = domain.replace(" ", "")
 
202
 
203
- # 🔥 1. SUPER RANKING FOR DOMAINS
204
  if query_clean == domain_clean or domain_clean.startswith(query_clean + "."):
205
- if not path:
206
- final_score *= 50.0 # EXACT MATCH ROOT DOMAIN -> 50x Boost
207
- else:
208
- final_score *= 10.0 # EXACT MATCH BUT SUBPAGE -> 10x Boost
209
  elif query_clean in domain_clean:
210
- if not path:
211
- final_score *= 25.0 # PARTIAL ROOT DOMAIN -> 25x Boost
212
- else:
213
- final_score *= 5.0 # PARTIAL MATCH SUBPAGE -> 5x Boost
214
 
215
- # 🔥 2. KEYWORD & TITLE MATCH BOOST
216
- if query in doc_title:
217
- final_score *= 5.0 # EXACT query present in Title -> 5x
218
- elif all(word in doc_title for word in query_tokens):
219
- final_score *= 3.0 # All query words present in Title -> 3x
220
-
221
- if query in doc_desc:
222
- final_score *= 2.0 # EXACT query in Description -> 2x
223
 
224
- # 🔥 3. CONTEXTUAL TRUST BOOST
225
  stats = URL_STATS.get(doc_url, {"user_visits": 0, "ai_visits": 0, "is_trusted": False})
226
- if stats.get("is_trusted", False):
227
- final_score *= 2.0
228
-
229
  final_score += (stats.get("user_visits", 0) * 0.05) + (stats.get("ai_visits", 0) * 0.1)
230
 
231
  seen_urls.add(doc_url)
232
-
233
- doc_copy = doc.copy()
234
  doc_copy["trust_stats"] = stats
235
-
236
  scored_results.append((final_score, doc_copy))
237
 
238
  scored_results.sort(key=lambda x: x[0], reverse=True)
239
  best_score = scored_results[0][0] if scored_results else 0
240
  prediction = "High confidence" if best_score > 15 else "Medium confidence" if best_score > 5 else "Low confidence"
 
 
241
 
242
- final_results = [{"match_score": round(s, 2), **doc} for s, doc in scored_results[:100]]
243
- return final_results, prediction, best_score
244
-
245
- # --- AI GENERATION HELPER ---
246
  def _generate_text(messages, max_tokens: int):
247
  prompt = "\n".join([f"{m['role'].capitalize()}: {m['content']}" for m in messages]) + "\nAssistant:"
248
- result = ai_generator(prompt, max_new_tokens=max_tokens, do_sample=True, temperature=0.3, top_p=0.85)
 
 
 
 
 
 
 
249
  text_out = result[0]['generated_text'].split("Assistant:")[-1].strip()
250
  return text_out
251
 
@@ -259,37 +227,21 @@ async def run_ai_task(messages, max_tokens: int, query_key: str):
259
  AI_RESPONSE_CACHE[query_key] = response
260
  return response
261
 
262
- # --- 5. FASTAPI APP & ENDPOINTS ---
263
  @asynccontextmanager
264
  async def lifespan(app: FastAPI):
265
  asyncio.create_task(asyncio.to_thread(load_and_index_dataset))
266
  asyncio.create_task(asyncio.to_thread(load_ai_models))
267
  yield
268
 
269
- app = FastAPI(title="Nebulix Advanced Text AI & RAG Engine", lifespan=lifespan)
270
-
271
- app.add_middleware(
272
- CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]
273
- )
274
 
275
  @app.get("/suggest")
276
  async def get_suggestions(q: str = Query(..., min_length=1)):
277
- q = q.lower()
278
- matches = [s for s in suggestion_list if s.startswith(q)][:7]
279
  return {"query": q, "suggestions": matches}
280
 
281
- @app.post("/track-click")
282
- async def track_url_click(url: str = Query(...)):
283
- if url not in URL_STATS:
284
- URL_STATS[url] = {"user_visits": 0, "ai_visits": 0, "is_trusted": False}
285
- URL_STATS[url]["user_visits"] += 1
286
- return {"status": "success", "url": url, "visits": URL_STATS[url]["user_visits"]}
287
-
288
- @app.get("/url-stats")
289
- async def get_url_stats(url: str = Query(...)):
290
- stats = URL_STATS.get(url, {"user_visits": 0, "ai_visits": 0, "is_trusted": False})
291
- return {"url": url, "stats": stats}
292
-
293
  @app.get("/search")
294
  async def pure_search(q: str = Query(...)):
295
  req_id = str(uuid.uuid4())
@@ -300,36 +252,28 @@ async def pure_search(q: str = Query(...)):
300
  async def ai_mode_short(q: str = Query(...)):
301
  req_id = str(uuid.uuid4())
302
  results, _, _ = await perform_core_search(q)
303
- if not results:
304
- return {"request_id": req_id, "ai_response": "I couldn't find any results.", "results": []}
305
 
306
- context = " ".join([f"{r.get('title', '')} - {r.get('description', '')}" for r in results[:3]])
307
-
308
- # 🔥 AI MODE SYSTEM PROMPT (STRICT: Max 80 Words & Use Brackets for Important Terms)
309
  messages = [
310
- {
311
- "role": "system",
312
- "content": "You are a concise AI. Answer in EXACTLY 1 to 2 sentences (Max 80 words). YOU MUST ENCLOSE ANY IMPORTANT KEYWORD, NAME, OR FACTUAL DETAIL IN PARENTHESES, like (this)."
313
- },
314
  {"role": "user", "content": f"Query: {q}\nContext: {context}"}
315
  ]
316
-
317
- # Max tokens set to 80 to keep it very short (aprox 20 generated tokens logic based on your 4 word multiplier).
318
- ai_reply = await run_ai_task(messages, 80, query_key=f"short_{q}")
319
  return {"request_id": req_id, "ai_response": ai_reply, "results": results}
320
 
321
-
322
  async def process_rag_chat(q: str):
323
  req_id = str(uuid.uuid4())
324
  needs_rag = any(w in q.lower() for w in ["what", "who", "where", "how", "why", "tell me", "explain", "latest", "best"])
325
-
326
  results, _, _ = await perform_core_search(q)
 
327
  rag_context = ""
328
  used_sources = []
329
 
330
  if needs_rag and results:
331
- top_urls = results[:10]
332
-
333
  tasks = [scrape_and_find_best_chunk(doc["url"], q) for doc in top_urls]
334
  scraped_chunks = await asyncio.gather(*tasks)
335
 
@@ -337,14 +281,6 @@ async def process_rag_chat(q: str):
337
  for i, chunk in enumerate(scraped_chunks):
338
  doc = top_urls[i]
339
  url = doc["url"]
340
-
341
- if url not in URL_STATS: URL_STATS[url] = {"user_visits": 0, "ai_visits": 0, "is_trusted": False}
342
- URL_STATS[url]["ai_visits"] += 1
343
- if URL_STATS[url]["ai_visits"] >= 100:
344
- URL_STATS[url]["is_trusted"] = True
345
-
346
- doc["trust_stats"] = URL_STATS[url]
347
-
348
  if chunk:
349
  context_parts.append(f"Source [{i+1}] ({url}): {chunk}")
350
  used_sources.append(doc)
@@ -354,34 +290,21 @@ async def process_rag_chat(q: str):
354
 
355
  rag_context = "\n".join(context_parts)
356
 
357
- # 🔥 AI CHAT SYSTEM PROMPT (Use Brackets for Important Terms)
358
- system_prompt = "You are a Smart AI Search Assistant. Provide a detailed answer. YOU MUST ENCLOSE EVERY IMPORTANT KEYWORD, ENTITY, OR FACTUAL DETAIL INSIDE PARENTHESES, like (keyword). Always mention the Source URL if you use its information."
359
- if not needs_rag:
360
- system_prompt = "You are a helpful AI Assistant. Answer the user's query conversationally, BUT ENCLOSE IMPORTANT WORDS IN PARENTHESES like (word)."
361
-
362
  messages = [
363
  {"role": "system", "content": system_prompt},
364
- {"role": "user", "content": f"Query: {q}\n\nLive Context Data:\n{rag_context}" if needs_rag else f"Query: {q}"}
365
  ]
366
 
367
- # Max tokens set to 200 for detailed chat mode.
368
- ai_reply = await run_ai_task(messages, 200, query_key=f"chat_{q}_{needs_rag}")
369
-
370
- return {
371
- "request_id": req_id,
372
- "ai_response": ai_reply,
373
- "rag_used": needs_rag,
374
- "sources": used_sources,
375
- "results": results[:15]
376
- }
377
 
378
  @app.get("/aimodechat")
379
- async def ai_mode_chat_get(q: str = Query(...)):
380
- return await process_rag_chat(q)
381
 
382
  @app.post("/aimodechat")
383
- async def ai_mode_chat_post(q: str = Form(...)):
384
- return await process_rag_chat(q)
385
 
386
  if __name__ == "__main__":
387
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
6
  import re
7
  import multiprocessing
8
  import urllib.parse
9
+ from collections import Counter
10
  from contextlib import asynccontextmanager
11
  from fastapi import FastAPI, Query, Form
12
  from fastapi.responses import JSONResponse
 
18
  from bs4 import BeautifulSoup
19
  from transformers import pipeline
20
 
21
+ # --- 1. SETUP & CPU ALLOCATION (MAX SPEED) ---
22
  TOTAL_CORES = multiprocessing.cpu_count()
23
+ AI_CORES = max(1, int(TOTAL_CORES * 0.7)) # Give 70% CPU to AI for max speed
24
  torch.set_num_threads(AI_CORES)
25
+ print(f"🖥️ Total Cores: {TOTAL_CORES} | Dedicated AI Cores: {AI_CORES}")
26
 
27
  raw_data = []
28
+ suggestion_tuples = []
29
  bm25_index = None
30
+ MAX_CONCURRENT_AI = max(1, AI_CORES // 2)
31
  ai_semaphore = asyncio.Semaphore(MAX_CONCURRENT_AI)
32
  ai_generator = None
33
+ URL_STATS = {}
 
 
 
 
34
  AI_RESPONSE_CACHE = {}
35
  MAX_CACHE_SIZE = 5000
36
+
37
+ TOKEN_RE = re.compile(r'[a-z0-9]+')
38
+ STOP_WORDS = {"the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "is", "are", "by", "this", "that", "it"}
39
  HF_TOKEN = os.getenv("HF_TOKEN")
40
 
 
41
  ADULT_KEYWORDS = {
42
  "porn", "sex", "nude", "nsfw", "xxx", "adult", "erotic", "onlyfans",
43
  "mms", "chudai", "bhabhi", "devar", "savita bhabhi", "xvideos", "pornhub",
44
+ "brazzers", "mia khalifa", "bf video", "blue film", "kamasutra",
45
  "boobs", "tits", "ass", "booty", "naked", "desi porn", "hot desi", "xnxx",
46
  "kacha badam", "ullu", "kooku", "mastram", "gandi baat", "nangi", "nanga",
47
  "sex tape", "milf", "stepmom", "incest", "playboy", "scandal", "leaked"
48
  }
49
 
 
50
  def tokenize(text):
51
  if not text: return []
52
+ return [t for t in TOKEN_RE.findall(str(text).lower()) if t not in STOP_WORDS]
 
53
 
54
  def load_and_index_dataset():
55
+ global bm25_index, raw_data, suggestion_tuples
56
+ print("📥 Loading Dataset & Building Fast Index...")
57
  dataset_path = "dataset.jsonl"
58
 
59
  if HF_TOKEN:
 
64
  repo_type="dataset",
65
  token=HF_TOKEN
66
  )
67
+ except Exception:
68
+ pass
69
 
70
  tokenized_corpus = []
71
+ suggestion_counter = Counter()
72
+
73
  if os.path.exists(dataset_path):
74
  with open(dataset_path, "r", encoding="utf-8") as f:
75
  for i, line in enumerate(f):
76
+ if i >= 150000: break
 
 
77
  if line.strip():
78
  try:
79
  doc = json.loads(line)
80
  url = doc.get("url", "")
 
 
 
 
 
 
81
  title_str = doc.get("title", "")
82
+ desc_str = doc.get("description", "")
 
83
 
84
+ parsed_url = urllib.parse.urlparse(url)
85
+ domain = parsed_url.netloc.lower().replace("www.", "")
86
+ clean_domain_name = domain.split('.')[0] if '.' in domain else domain
87
+
88
+ doc_text = f"{title_str} {desc_str} {url}".lower()
89
+ is_adult = doc.get("tag", "general").lower() in ["adult", "nsfw"] or any(aw in doc_text for aw in ADULT_KEYWORDS)
90
+
91
+ doc["_fast_domain"] = domain
92
+ doc["_fast_path"] = parsed_url.path.strip("/")
93
+ doc["_fast_title"] = title_str.lower()
94
+ doc["_fast_desc"] = desc_str.lower()
95
+ doc["_is_adult"] = is_adult
96
+ raw_data.append(doc)
97
 
98
+ if not is_adult:
99
+ if len(clean_domain_name) > 2 and clean_domain_name not in ADULT_KEYWORDS:
100
+ suggestion_counter[clean_domain_name] += 5
101
+
102
+ clean_t_tokens = [w for w in tokenize(title_str) if w not in ADULT_KEYWORDS]
103
+ for w in clean_t_tokens:
104
+ if len(w) > 2: suggestion_counter[w] += 1
105
+ for j in range(len(clean_t_tokens) - 1):
106
+ suggestion_counter[f"{clean_t_tokens[j]} {clean_t_tokens[j+1]}"] += 2
107
+
108
+ tokenized_corpus.append((tokenize(title_str) * 5) + (tokenize(desc_str) * 3) + tokenize(url))
109
+ except: pass
110
 
111
  if tokenized_corpus:
112
+ suggestion_tuples = suggestion_counter.most_common(40000)
 
113
  bm25_index = BM25Okapi(tokenized_corpus)
114
+ print(f"🚀 Indexing Complete! {len(raw_data)} records ready.")
 
 
115
 
116
  def load_ai_models():
117
  global ai_generator
118
+ print("🤖 Loading LiquidAI/LFM2.5-350M for BLAZING FAST inference...")
119
  try:
120
+ # Optimization: use float32 on CPU but pipeline optimized
121
  ai_generator = pipeline(
122
  "text-generation",
123
+ model="LiquidAI/LFM2.5-350M",
124
  device=-1,
125
+ torch_dtype=torch.float32,
126
+ trust_remote_code=True
127
  )
128
+ print("✅ LiquidAI Model Loaded! Superfast mode activated.")
129
  except Exception as e:
130
  print(f"❌ Model Loading Failed: {e}")
131
 
132
+ def chunk_text(text, chunk_size=80):
 
133
  words = text.split()
134
  return [" ".join(words[i:i+chunk_size]) for i in range(0, len(words), chunk_size)]
135
 
136
  async def scrape_and_find_best_chunk(url, query):
137
  try:
138
+ # 🔥 REDUCED TIMEOUT TO 1.5 SECONDS FOR SPEED
139
  async with aiohttp.ClientSession() as session:
140
+ async with session.get(url, timeout=1.5) as resp:
141
  if resp.status == 200:
142
  html = await resp.text()
143
  soup = BeautifulSoup(html, "html.parser")
144
+ text = " ".join([tag.get_text(strip=True) for tag in soup.find_all(['p', 'h1', 'h2'])])
 
 
145
  if not text: return ""
146
 
147
  chunks = chunk_text(text)
148
  query_tokens = set(tokenize(query))
149
 
150
+ best_chunk, max_score = "", 0
 
 
 
151
  for chunk in chunks:
152
+ score = len(query_tokens.intersection(tokenize(chunk)))
 
153
  if score > max_score:
154
+ max_score, best_chunk = score, chunk
155
+ if max_score >= max(1, len(query_tokens) // 2): break
 
 
156
  return best_chunk
157
  except:
158
  return ""
159
  return ""
160
 
 
161
  async def perform_core_search(raw_query: str):
162
+ if not raw_data or not bm25_index: return [], "Search index not ready.", 0
 
163
 
164
  query = raw_query.strip().lower()
165
  query_tokens = tokenize(query)
166
+ user_wants_adult = any(aw in query for aw in ADULT_KEYWORDS)
167
 
168
  scores = bm25_index.get_scores(query_tokens) if query_tokens else [1.0] * len(raw_data)
169
+ scored_results, seen_urls = [], set()
170
+ query_clean = query.replace(" ", "")
171
 
172
  for index, score in enumerate(scores):
173
  if score > 0.1 or not query_tokens:
174
  doc = raw_data[index]
175
+ doc_url = doc["url"]
 
176
  if doc_url in seen_urls: continue
177
+ if not user_wants_adult and doc["_is_adult"]: continue
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
+ final_score, domain = score, doc["_fast_domain"]
 
180
  domain_clean = domain.replace(" ", "")
181
+ doc_title, doc_desc = doc["_fast_title"], doc["_fast_desc"]
182
 
 
183
  if query_clean == domain_clean or domain_clean.startswith(query_clean + "."):
184
+ final_score *= 50.0 if not doc["_fast_path"] else 10.0
 
 
 
185
  elif query_clean in domain_clean:
186
+ final_score *= 25.0 if not doc["_fast_path"] else 5.0
 
 
 
187
 
188
+ if query in doc_title: final_score *= 5.0
189
+ elif all(w in doc_title for w in query_tokens): final_score *= 3.0
190
+ if query in doc_desc: final_score *= 2.0
 
 
 
 
 
191
 
 
192
  stats = URL_STATS.get(doc_url, {"user_visits": 0, "ai_visits": 0, "is_trusted": False})
193
+ if stats.get("is_trusted", False): final_score *= 2.0
 
 
194
  final_score += (stats.get("user_visits", 0) * 0.05) + (stats.get("ai_visits", 0) * 0.1)
195
 
196
  seen_urls.add(doc_url)
197
+ doc_copy = {k: v for k, v in doc.items() if not k.startswith("_")}
 
198
  doc_copy["trust_stats"] = stats
 
199
  scored_results.append((final_score, doc_copy))
200
 
201
  scored_results.sort(key=lambda x: x[0], reverse=True)
202
  best_score = scored_results[0][0] if scored_results else 0
203
  prediction = "High confidence" if best_score > 15 else "Medium confidence" if best_score > 5 else "Low confidence"
204
+
205
+ return [{"match_score": round(s, 2), **doc} for s, doc in scored_results[:100]], prediction, best_score
206
 
 
 
 
 
207
  def _generate_text(messages, max_tokens: int):
208
  prompt = "\n".join([f"{m['role'].capitalize()}: {m['content']}" for m in messages]) + "\nAssistant:"
209
+
210
+ # 🔥 GREEDY DECODING (do_sample=False) FOR 3X FASTER AI RESPONSES
211
+ result = ai_generator(
212
+ prompt,
213
+ max_new_tokens=max_tokens,
214
+ do_sample=False,
215
+ truncation=True
216
+ )
217
  text_out = result[0]['generated_text'].split("Assistant:")[-1].strip()
218
  return text_out
219
 
 
227
  AI_RESPONSE_CACHE[query_key] = response
228
  return response
229
 
 
230
  @asynccontextmanager
231
  async def lifespan(app: FastAPI):
232
  asyncio.create_task(asyncio.to_thread(load_and_index_dataset))
233
  asyncio.create_task(asyncio.to_thread(load_ai_models))
234
  yield
235
 
236
+ app = FastAPI(title="Nebulix Fast AI Search", lifespan=lifespan)
237
+ app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
 
 
 
238
 
239
  @app.get("/suggest")
240
  async def get_suggestions(q: str = Query(..., min_length=1)):
241
+ q = q.lower().strip()
242
+ matches = [word for word, count in suggestion_tuples if word.startswith(q)][:7]
243
  return {"query": q, "suggestions": matches}
244
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  @app.get("/search")
246
  async def pure_search(q: str = Query(...)):
247
  req_id = str(uuid.uuid4())
 
252
  async def ai_mode_short(q: str = Query(...)):
253
  req_id = str(uuid.uuid4())
254
  results, _, _ = await perform_core_search(q)
255
+ if not results: return {"request_id": req_id, "ai_response": "I couldn't find any results.", "results": []}
 
256
 
257
+ context = " ".join([f"{r.get('title', '')} - {r.get('description', '')}" for r in results[:2]])
 
 
258
  messages = [
259
+ {"role": "system", "content": "You are a concise AI. Answer in 1 short sentence. Put important keywords in (parentheses)."},
 
 
 
260
  {"role": "user", "content": f"Query: {q}\nContext: {context}"}
261
  ]
262
+ # Reduce token generation for lightning speed
263
+ ai_reply = await run_ai_task(messages, 50, query_key=f"short_{q}")
 
264
  return {"request_id": req_id, "ai_response": ai_reply, "results": results}
265
 
 
266
  async def process_rag_chat(q: str):
267
  req_id = str(uuid.uuid4())
268
  needs_rag = any(w in q.lower() for w in ["what", "who", "where", "how", "why", "tell me", "explain", "latest", "best"])
 
269
  results, _, _ = await perform_core_search(q)
270
+
271
  rag_context = ""
272
  used_sources = []
273
 
274
  if needs_rag and results:
275
+ # 🔥 REDUCED FROM 10 TO TOP 3 URLs FOR FASTER RAG
276
+ top_urls = results[:3]
277
  tasks = [scrape_and_find_best_chunk(doc["url"], q) for doc in top_urls]
278
  scraped_chunks = await asyncio.gather(*tasks)
279
 
 
281
  for i, chunk in enumerate(scraped_chunks):
282
  doc = top_urls[i]
283
  url = doc["url"]
 
 
 
 
 
 
 
 
284
  if chunk:
285
  context_parts.append(f"Source [{i+1}] ({url}): {chunk}")
286
  used_sources.append(doc)
 
290
 
291
  rag_context = "\n".join(context_parts)
292
 
293
+ system_prompt = "You are a Smart AI Search Assistant. Provide a short, direct answer. ENCLOSE IMPORTANT KEYWORDS IN (PARENTHESES). Mention Source URLs if used."
 
 
 
 
294
  messages = [
295
  {"role": "system", "content": system_prompt},
296
+ {"role": "user", "content": f"Query: {q}\nContext:\n{rag_context}" if needs_rag else f"Query: {q}"}
297
  ]
298
 
299
+ # Max tokens 120 so it generates answers faster
300
+ ai_reply = await run_ai_task(messages, 120, query_key=f"chat_{q}_{needs_rag}")
301
+ return {"request_id": req_id, "ai_response": ai_reply, "rag_used": needs_rag, "sources": used_sources, "results": results[:15]}
 
 
 
 
 
 
 
302
 
303
  @app.get("/aimodechat")
304
+ async def ai_mode_chat_get(q: str = Query(...)): return await process_rag_chat(q)
 
305
 
306
  @app.post("/aimodechat")
307
+ async def ai_mode_chat_post(q: str = Form(...)): return await process_rag_chat(q)
 
308
 
309
  if __name__ == "__main__":
310
  uvicorn.run(app, host="0.0.0.0", port=7860)