usmanovrustam commited on
Commit
d21c846
·
1 Parent(s): fbe9c72

Full Rebrand: Transitioned intelligence engine from Llama to Qwen

Browse files
Files changed (3) hide show
  1. dashboard/index.html +0 -0
  2. download_qwen.py +46 -0
  3. scraper.py +18 -18
dashboard/index.html ADDED
The diff for this file is too large to render. See raw diff
 
download_qwen.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ from tqdm import tqdm
4
+
5
+ def download_qwen():
6
+ # Using bartowski's high-quality Qwen 3.5 4B GGUF
7
+ url = "https://huggingface.co/bartowski/Qwen_Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-Instruct-Q4_K_M.gguf?download=true"
8
+ local_dir = "ml_models"
9
+ filename = "qwen-3.5-4b.gguf"
10
+ save_path = os.path.join(local_dir, filename)
11
+
12
+ if not os.path.exists(local_dir):
13
+ os.makedirs(local_dir)
14
+
15
+ if os.path.exists(save_path):
16
+ print(f"--- INFO: Qwen model already exists at {save_path} ---")
17
+ return
18
+
19
+ print(f"--- STARTING DIRECT DOWNLOAD: {filename} ---")
20
+ print("Size: ~2.5 GB. This may take 5-10 minutes.")
21
+
22
+ try:
23
+ response = requests.get(url, stream=True, timeout=30)
24
+ response.raise_for_status()
25
+ total_size = int(response.headers.get('content-length', 0))
26
+
27
+ with open(save_path, 'wb') as f, tqdm(
28
+ desc=filename,
29
+ total=total_size,
30
+ unit='B',
31
+ unit_scale=True,
32
+ unit_divisor=1024,
33
+ ) as bar:
34
+ for chunk in response.iter_content(chunk_size=8192):
35
+ if chunk:
36
+ f.write(chunk)
37
+ bar.update(len(chunk))
38
+
39
+ print(f"\n--- SUCCESS: Qwen model saved to {save_path} ---")
40
+ except Exception as e:
41
+ print(f"\n--- ERROR: Download failed: {e} ---")
42
+ if os.path.exists(save_path):
43
+ os.remove(save_path)
44
+
45
+ if __name__ == "__main__":
46
+ download_qwen()
scraper.py CHANGED
@@ -129,17 +129,17 @@ def search_app_store(query):
129
  except: return []
130
 
131
  # Neural Config
132
- LLAMA_PATH = "ml_models/qwen-3.5-4b.gguf"
133
- _Llama = None
134
 
135
- def get_llama():
136
- global _Llama
137
- if _Llama: return _Llama
138
- if not os.path.exists(LLAMA_PATH): return None
139
  try:
140
  from llama_cpp import Llama
141
- _Llama = Llama(model_path=LLAMA_PATH, n_gpu_layers=-1, verbose=False, n_ctx=2048)
142
- return _Llama
143
  except Exception: return None
144
 
145
  def get_representative_reviews(reviews, count=25):
@@ -169,8 +169,8 @@ def get_representative_reviews(reviews, count=25):
169
  return [r[:200] for r in reviews[:count]]
170
 
171
  def ai_generate_overview(neg_reviews, pos_reviews):
172
- llm = get_llama()
173
- if not llm: return "Llama engine offline. Unable to generate review overview."
174
 
175
  try:
176
  # Phase 1: Hierarchical Clustering & Mini-Summarization
@@ -192,7 +192,7 @@ Reviews to analyze:
192
  return res['choices'][0]['text'].strip()
193
  except: return ""
194
 
195
- # Batch process to ensure we don't hit Llama's context limit in one go
196
  neg_mini = get_mini_summary(neg_clusters, "negative")
197
  pos_mini = get_mini_summary(pos_clusters, "positive")
198
 
@@ -224,7 +224,7 @@ POSITIVE THEMES:
224
  return "Error generating hierarchical overview."
225
 
226
  def ai_generate_synthesis(overview_text):
227
- llm = get_llama()
228
  if not llm: return "Synthesis engine offline."
229
 
230
  prompt = f"""<|start_header_id|>system<|end_header_id|>
@@ -246,7 +246,7 @@ def ai_generate_battle_analysis(data_a, data_b):
246
  """
247
  Performs a side-by-side neural clash analysis between two competitors.
248
  """
249
- llm = get_llama()
250
  if not llm: return "Battle engine offline."
251
 
252
  name_a = data_a.get('app_name', 'App A')
@@ -282,7 +282,7 @@ INTEL ON B: {analysis_b}
282
  return "Neural Clash synthesis failed. Manual comparison required."
283
 
284
  def ai_extract_search_keywords(idea_text):
285
- llm = get_llama()
286
  if not llm:
287
  # Fallback to basic keyword extraction if no LLM
288
  words = [w for w in re.split(r'\W+', idea_text.lower()) if len(w) > 3]
@@ -302,7 +302,7 @@ App Idea: {idea_text}
302
  return ",".join(idea_text.split()[:5])
303
 
304
  def ai_validate_match(title1, title2):
305
- llm = get_llama()
306
  if not llm: return "YES" # Optimistic fallback
307
 
308
  prompt = f"""<|start_header_id|>system<|end_header_id|>
@@ -374,10 +374,10 @@ def run_full_scan(gp_url=None, as_url=None):
374
 
375
  app_description = gp_desc or as_desc
376
 
377
- # Llama cross-check for different apps
378
  if gp_title and as_title:
379
  ans = ai_validate_match(gp_title, as_title)
380
- print(f"[AI Validation] Comparing '{gp_title}' vs '{as_title}' -> Llama said: {ans}")
381
  if "NO" in ans and "YES" not in ans:
382
  return {"mismatch_error": True, "gp_title": gp_title, "as_title": as_title}
383
 
@@ -402,7 +402,7 @@ def run_full_scan(gp_url=None, as_url=None):
402
  # 2. Positive Highlights (Positive 4-5)
403
  pos_reviews = df[(df['rating'] >= 4) & (df['review'].str.len() > 10)]['review'].dropna().tolist()
404
 
405
- # 3. Generating General Llama Overview
406
  general_overview = ai_generate_overview(neg_reviews, pos_reviews)
407
 
408
  # 3. Sentiment Timeline (Weekly)
 
129
  except: return []
130
 
131
  # Neural Config
132
+ QWEN_PATH = "ml_models/qwen-3.5-4b.gguf"
133
+ _Qwen = None
134
 
135
+ def get_qwen():
136
+ global _Qwen
137
+ if _Qwen: return _Qwen
138
+ if not os.path.exists(QWEN_PATH): return None
139
  try:
140
  from llama_cpp import Llama
141
+ _Qwen = Llama(model_path=QWEN_PATH, n_gpu_layers=-1, verbose=False, n_ctx=2048)
142
+ return _Qwen
143
  except Exception: return None
144
 
145
  def get_representative_reviews(reviews, count=25):
 
169
  return [r[:200] for r in reviews[:count]]
170
 
171
  def ai_generate_overview(neg_reviews, pos_reviews):
172
+ llm = get_qwen()
173
+ if not llm: return "Qwen engine offline. Unable to generate review overview."
174
 
175
  try:
176
  # Phase 1: Hierarchical Clustering & Mini-Summarization
 
192
  return res['choices'][0]['text'].strip()
193
  except: return ""
194
 
195
+ # Batch process to ensure we don't hit Qwen's context limit in one go
196
  neg_mini = get_mini_summary(neg_clusters, "negative")
197
  pos_mini = get_mini_summary(pos_clusters, "positive")
198
 
 
224
  return "Error generating hierarchical overview."
225
 
226
  def ai_generate_synthesis(overview_text):
227
+ llm = get_qwen()
228
  if not llm: return "Synthesis engine offline."
229
 
230
  prompt = f"""<|start_header_id|>system<|end_header_id|>
 
246
  """
247
  Performs a side-by-side neural clash analysis between two competitors.
248
  """
249
+ llm = get_qwen()
250
  if not llm: return "Battle engine offline."
251
 
252
  name_a = data_a.get('app_name', 'App A')
 
282
  return "Neural Clash synthesis failed. Manual comparison required."
283
 
284
  def ai_extract_search_keywords(idea_text):
285
+ llm = get_qwen()
286
  if not llm:
287
  # Fallback to basic keyword extraction if no LLM
288
  words = [w for w in re.split(r'\W+', idea_text.lower()) if len(w) > 3]
 
302
  return ",".join(idea_text.split()[:5])
303
 
304
  def ai_validate_match(title1, title2):
305
+ llm = get_qwen()
306
  if not llm: return "YES" # Optimistic fallback
307
 
308
  prompt = f"""<|start_header_id|>system<|end_header_id|>
 
374
 
375
  app_description = gp_desc or as_desc
376
 
377
+ # Qwen cross-check for different apps
378
  if gp_title and as_title:
379
  ans = ai_validate_match(gp_title, as_title)
380
+ print(f"[AI Validation] Comparing '{gp_title}' vs '{as_title}' -> Qwen said: {ans}")
381
  if "NO" in ans and "YES" not in ans:
382
  return {"mismatch_error": True, "gp_title": gp_title, "as_title": as_title}
383
 
 
402
  # 2. Positive Highlights (Positive 4-5)
403
  pos_reviews = df[(df['rating'] >= 4) & (df['review'].str.len() > 10)]['review'].dropna().tolist()
404
 
405
+ # 3. Generating General Qwen Overview
406
  general_overview = ai_generate_overview(neg_reviews, pos_reviews)
407
 
408
  # 3. Sentiment Timeline (Weekly)