ghosthets commited on
Commit
eeaf52d
·
verified ·
1 Parent(s): e05b082

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -16
app.py CHANGED
@@ -3,6 +3,7 @@ import json
3
  import re
4
  import uuid
5
  import threading
 
6
  import requests
7
  import feedparser
8
  from bs4 import BeautifulSoup
@@ -13,6 +14,9 @@ from llama_cpp import Llama
13
  app = Flask(__name__)
14
  CORS(app)
15
 
 
 
 
16
  MODEL_REPO = "unsloth/Qwen3-0.6B-GGUF"
17
  MODEL_FILE = "Qwen3-0.6B-Q4_K_M.gguf"
18
  N_CTX = 2048
@@ -68,6 +72,7 @@ def crawl_site(url):
68
  try:
69
  headers = {'User-Agent': 'Mozilla/5.0 (compatible; JuskeoBot/1.0)'}
70
  resp = requests.get(url, timeout=15, headers=headers, allow_redirects=True)
 
71
  soup = BeautifulSoup(resp.text, 'html.parser')
72
 
73
  title = soup.find('title')
@@ -276,13 +281,13 @@ def run_crawl_task(task_id, url, username, webhook):
276
 
277
  if "error" in crawl_data:
278
  tasks[task_id]['status'] = 'error'
279
- tasks[task_id]['error'] = crawl_data['error']
280
  return
281
 
282
- text_content = crawl_data.get("text_preview", "")
283
 
284
  tasks[task_id]['progress'] = 50
285
- analysis_prompt = f"Analyze this website:\n\nTitle: {crawl_data['title']}\nMeta: {crawl_data['meta_description']}\nContent: {text_content[:2000]}\nIssues: {json.dumps(crawl_data['seo_issues'])}"
286
  analysis_response = run_llm(analysis_prompt, system_prompt=SEO_PROMPT, max_new_tokens=1024)
287
  analysis = parse_json_from_llm(analysis_response)
288
  tasks[task_id]['progress'] = 70
@@ -309,12 +314,39 @@ def run_crawl_task(task_id, url, username, webhook):
309
 
310
  llm_mentions = {'chatgpt': 0, 'perplexity': 0, 'gemini': 0, 'claude': 0}
311
 
312
- tasks[task_id]['progress'] = 85
313
 
314
  blog_posts = []
315
  blog_topic = crawl_data['title'] or 'Technology Trends'
316
- blog_prompt = f"Write a comprehensive blog post about: {blog_topic}\n\nKeywords: {', '.join(keywords[:5])}\nSite Context: {crawl_data['title']}\nWrite 1500+ words in markdown."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  blog_response = run_llm(blog_prompt, system_prompt=BLOG_PROMPT, max_new_tokens=4096)
 
318
  word_count = len(blog_response.split())
319
  title_match = re.search(r'^#\s+(.+)$', blog_response, re.MULTILINE)
320
  blog_title = title_match.group(1) if title_match else blog_topic
@@ -344,9 +376,10 @@ def run_crawl_task(task_id, url, username, webhook):
344
  'keywords': [{'keyword': k, 'volume': 0, 'position': i+1} for i, k in enumerate(keywords[:20])],
345
  'pages': pages_data,
346
  'blog_posts': blog_posts,
 
347
  'schema_types': schema_types,
348
  'llm_mentions': llm_mentions,
349
- 'crawled_at': __import__('datetime').datetime.utcnow().isoformat() + 'Z',
350
  'llm_analysis': analysis,
351
  }
352
 
@@ -355,10 +388,15 @@ def run_crawl_task(task_id, url, username, webhook):
355
  tasks[task_id]['result'] = result
356
 
357
  if webhook:
358
- try:
359
- requests.post(webhook, json={'username': username, 'result': result}, timeout=30)
360
- except:
361
- pass
 
 
 
 
 
362
 
363
  except Exception as e:
364
  tasks[task_id]['status'] = 'error'
@@ -383,9 +421,7 @@ def index():
383
  'error': None,
384
  }
385
 
386
- t = threading.Thread(target=run_crawl_task, args=(task_id, url, username, webhook))
387
- t.daemon = True
388
- t.start()
389
 
390
  return f'''<html><head><meta http-equiv="refresh" content="0;url=/status?task_id={task_id}"></head><body>
391
  <script>let t="{task_id}";window.location.href="/status?task_id="+t;</script>
@@ -401,11 +437,14 @@ def task_status():
401
  return jsonify({"status": "error", "message": "Unknown task"}), 404
402
 
403
  task = tasks[task_id]
404
- return jsonify({
405
  "status": task['status'],
406
  "progress": task['progress'],
407
  "pages_crawled": task.get('result', {}).get('pages_crawled', 0) if task['result'] else 0,
408
- })
 
 
 
409
 
410
 
411
  @app.route('/result', methods=['GET'])
@@ -502,6 +541,9 @@ def generate_blog():
502
  if not topic:
503
  return jsonify({"error": "Topic is required"}), 400
504
 
 
 
 
505
  news_text = ""
506
  if news:
507
  news_with_content = []
@@ -720,4 +762,4 @@ except Exception as e:
720
 
721
 
722
  if __name__ == '__main__':
723
- app.run(host='0.0.0.0', port=7860, debug=False)
 
3
  import re
4
  import uuid
5
  import threading
6
+ from concurrent.futures import ThreadPoolExecutor
7
  import requests
8
  import feedparser
9
  from bs4 import BeautifulSoup
 
14
  app = Flask(__name__)
15
  CORS(app)
16
 
17
+ MAX_CONCURRENT_USERS = 20
18
+ executor = ThreadPoolExecutor(max_workers=MAX_CONCURRENT_USERS)
19
+
20
  MODEL_REPO = "unsloth/Qwen3-0.6B-GGUF"
21
  MODEL_FILE = "Qwen3-0.6B-Q4_K_M.gguf"
22
  N_CTX = 2048
 
72
  try:
73
  headers = {'User-Agent': 'Mozilla/5.0 (compatible; JuskeoBot/1.0)'}
74
  resp = requests.get(url, timeout=15, headers=headers, allow_redirects=True)
75
+ resp.raise_for_status()
76
  soup = BeautifulSoup(resp.text, 'html.parser')
77
 
78
  title = soup.find('title')
 
281
 
282
  if "error" in crawl_data:
283
  tasks[task_id]['status'] = 'error'
284
+ tasks[task_id]['error'] = "Crawl failed: " + crawl_data['error']
285
  return
286
 
287
+ text_content = crawl_data.get("body_text", "") or crawl_data.get("text_preview", "")
288
 
289
  tasks[task_id]['progress'] = 50
290
+ analysis_prompt = f"Analyze this website:\n\nTitle: {crawl_data['title']}\nMeta: {crawl_data['meta_description']}\nH1: {', '.join(crawl_data['h1'][:5])}\nH2: {', '.join(crawl_data['h2'][:10])}\nContent: {text_content[:2000]}\nIssues: {json.dumps(crawl_data['seo_issues'])}"
291
  analysis_response = run_llm(analysis_prompt, system_prompt=SEO_PROMPT, max_new_tokens=1024)
292
  analysis = parse_json_from_llm(analysis_response)
293
  tasks[task_id]['progress'] = 70
 
314
 
315
  llm_mentions = {'chatgpt': 0, 'perplexity': 0, 'gemini': 0, 'claude': 0}
316
 
317
+ tasks[task_id]['progress'] = 80
318
 
319
  blog_posts = []
320
  blog_topic = crawl_data['title'] or 'Technology Trends'
321
+
322
+ news_items = fetch_news_with_content(blog_topic, max_results=3)
323
+ news_text = ""
324
+ if news_items:
325
+ news_text = "\n\n=== REFERENCE ARTICLES ===\n"
326
+ for i, n in enumerate(news_items[:3], 1):
327
+ news_text += f"\n--- Article {i}: {n.get('title', '')} ({n.get('source', '')}) ---\n"
328
+ news_text += f"{n.get('content', '')[:2000]}\n"
329
+
330
+ blog_prompt = f"""Write a comprehensive blog post about: {blog_topic}
331
+
332
+ Keywords: {', '.join(keywords[:5])}
333
+ Site Context: {crawl_data['title']}
334
+ Site Description: {crawl_data['meta_description']}
335
+ Headings: {', '.join(crawl_data['h2'][:5])}
336
+ {news_text}
337
+
338
+ RULES:
339
+ - Write 2000-3000 words MINIMUM. Long-form authoritative article.
340
+ - Use ## for main sections (8-12 sections), ### for subsections.
341
+ - Each section 200-300 words of REAL content.
342
+ - Include data, statistics, examples from reference articles.
343
+ - Include FAQ section at the end with 5+ questions.
344
+ - Naturally place target keywords throughout.
345
+ - NO thinking tags. Output ONLY the markdown blog post."""
346
+
347
+ tasks[task_id]['progress'] = 85
348
  blog_response = run_llm(blog_prompt, system_prompt=BLOG_PROMPT, max_new_tokens=4096)
349
+ blog_response = strip_thinking(blog_response)
350
  word_count = len(blog_response.split())
351
  title_match = re.search(r'^#\s+(.+)$', blog_response, re.MULTILINE)
352
  blog_title = title_match.group(1) if title_match else blog_topic
 
376
  'keywords': [{'keyword': k, 'volume': 0, 'position': i+1} for i, k in enumerate(keywords[:20])],
377
  'pages': pages_data,
378
  'blog_posts': blog_posts,
379
+ 'news_used': news_items,
380
  'schema_types': schema_types,
381
  'llm_mentions': llm_mentions,
382
+ 'crawled_at': __import__('datetime').datetime.now(__import__('datetime').timezone.utc).isoformat(),
383
  'llm_analysis': analysis,
384
  }
385
 
 
388
  tasks[task_id]['result'] = result
389
 
390
  if webhook:
391
+ for attempt in range(3):
392
+ try:
393
+ resp = requests.post(webhook, json={'username': username, 'result': result}, timeout=30)
394
+ if resp.status_code < 400:
395
+ break
396
+ except:
397
+ if attempt < 2:
398
+ import time
399
+ time.sleep(2 * (attempt + 1))
400
 
401
  except Exception as e:
402
  tasks[task_id]['status'] = 'error'
 
421
  'error': None,
422
  }
423
 
424
+ t = executor.submit(run_crawl_task, task_id, url, username, webhook)
 
 
425
 
426
  return f'''<html><head><meta http-equiv="refresh" content="0;url=/status?task_id={task_id}"></head><body>
427
  <script>let t="{task_id}";window.location.href="/status?task_id="+t;</script>
 
437
  return jsonify({"status": "error", "message": "Unknown task"}), 404
438
 
439
  task = tasks[task_id]
440
+ resp = {
441
  "status": task['status'],
442
  "progress": task['progress'],
443
  "pages_crawled": task.get('result', {}).get('pages_crawled', 0) if task['result'] else 0,
444
+ }
445
+ if task['status'] == 'error':
446
+ resp['error'] = task.get('error', 'Unknown error')
447
+ return jsonify(resp)
448
 
449
 
450
  @app.route('/result', methods=['GET'])
 
541
  if not topic:
542
  return jsonify({"error": "Topic is required"}), 400
543
 
544
+ if not news:
545
+ news = fetch_news_with_content(topic, max_results=5)
546
+
547
  news_text = ""
548
  if news:
549
  news_with_content = []
 
762
 
763
 
764
  if __name__ == '__main__':
765
+ app.run(host='0.0.0.0', port=7860, debug=False, threaded=True)