Spaces:
Sleeping
Sleeping
Fix: HOT feed chỉ hiện hashtag + rewrite timeout 15s
#1
by bep40 - opened
- app_v2_entry.py +3 -0
- patch_qwen_timeout.py +31 -0
- static/app_v2.js +13 -22
app_v2_entry.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
| 1 |
"""VNEWS v2 Entry Point - with friendly highlights"""
|
| 2 |
import sys, os, traceback
|
|
|
|
|
|
|
|
|
|
| 3 |
from main import app, HEADERS, BONGDA_HEADERS, fetch_bongda_api, HL_LEAGUES
|
| 4 |
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
|
| 5 |
from fastapi.staticfiles import StaticFiles
|
|
|
|
| 1 |
"""VNEWS v2 Entry Point - with friendly highlights"""
|
| 2 |
import sys, os, traceback
|
| 3 |
+
# Áp dụng patch sửa timeout rewrite 75s→15s
|
| 4 |
+
try: import patch_qwen_timeout
|
| 5 |
+
except: pass
|
| 6 |
from main import app, HEADERS, BONGDA_HEADERS, fetch_bongda_api, HL_LEAGUES
|
| 7 |
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
|
| 8 |
from fastapi.staticfiles import StaticFiles
|
patch_qwen_timeout.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Patch applied at Space startup: reduce Qwen timeout from 75s to 15s
|
| 2 |
+
# This file is sourced by the entry point
|
| 3 |
+
|
| 4 |
+
import re, os
|
| 5 |
+
|
| 6 |
+
PATCH_DONE_FLAG = "/app/.qwen_timeout_patched"
|
| 7 |
+
|
| 8 |
+
def apply_patch():
|
| 9 |
+
if os.path.exists(PATCH_DONE_FLAG):
|
| 10 |
+
return
|
| 11 |
+
try:
|
| 12 |
+
for fpath in ["/app/main.py", os.path.join(os.path.dirname(__file__), "main.py")]:
|
| 13 |
+
if os.path.exists(fpath):
|
| 14 |
+
with open(fpath, "r") as f:
|
| 15 |
+
content = f.read()
|
| 16 |
+
# Change timeout=75 to timeout=15
|
| 17 |
+
orig = 'r=requests.post("https://router.huggingface.co/v1/chat/completions",headers=headers,json=payload,timeout=75)'
|
| 18 |
+
new = 'r=requests.post("https://router.huggingface.co/v1/chat/completions",headers=headers,json=payload,timeout=15)'
|
| 19 |
+
if orig in content:
|
| 20 |
+
content = content.replace(orig, new)
|
| 21 |
+
with open(fpath, "w") as f:
|
| 22 |
+
f.write(content)
|
| 23 |
+
print("[patch] Applied Qwen timeout patch (75s → 15s) to", fpath)
|
| 24 |
+
break
|
| 25 |
+
# Create flag file
|
| 26 |
+
with open(PATCH_DONE_FLAG, "w") as f:
|
| 27 |
+
f.write("ok")
|
| 28 |
+
except Exception as e:
|
| 29 |
+
print("[patch] Failed to apply patch:", e)
|
| 30 |
+
|
| 31 |
+
apply_patch()
|
static/app_v2.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
| 1 |
// === VNEWS Frontend v2 - Full Functions ===
|
| 2 |
-
//
|
| 3 |
-
//
|
| 4 |
// Fix: safeJson parsing to handle non-JSON error responses gracefully
|
| 5 |
-
// Fix: timeout cho rewrite calls, không treo vô hạn
|
| 6 |
|
| 7 |
// === SAFE JSON PARSE ===
|
| 8 |
async function safeJson(resp){
|
|
@@ -10,7 +9,6 @@ async function safeJson(resp){
|
|
| 10 |
const text=await resp.text();
|
| 11 |
try{return JSON.parse(text);}
|
| 12 |
catch(e){
|
| 13 |
-
// Server returned non-JSON (e.g., HTML error page)
|
| 14 |
console.warn('Non-JSON response:', text.slice(0,200));
|
| 15 |
return {error: 'Server error', _status: resp.status, _raw: text.slice(0,200)};
|
| 16 |
}
|
|
@@ -73,34 +71,29 @@ async function loadHotTopics(){
|
|
| 73 |
const topicText=t.topic||t.label.replace(/^#/,'');
|
| 74 |
return`<button class="hot-chip" onclick="searchTopic('${topicText.replace(/'/g,"\\'")}')">${esc(t.label)}</button>`;
|
| 75 |
}).join('');
|
| 76 |
-
if(_allHotTopics&&_allHotTopics[0]){
|
| 77 |
-
const firstTopic=_allHotTopics[0].topic||_allHotTopics[0].label.replace(/^#/,'');
|
| 78 |
-
setTimeout(()=>searchTopic(firstTopic),800);
|
| 79 |
-
}
|
| 80 |
}
|
| 81 |
|
| 82 |
function searchTopic(topic){
|
| 83 |
if(!topic){topic=document.getElementById('topic-input')?.value.trim();if(!topic){alert('Nhập chủ đề');return;}}
|
| 84 |
document.getElementById('topic-input').value='';
|
| 85 |
_htTopic=topic;_htPage=0;
|
| 86 |
-
// Chỉ tìm tin của hashtag được click
|
| 87 |
showHashtagSources(topic, 0, '');
|
| 88 |
}
|
| 89 |
|
| 90 |
async function showHashtagSources(topic, page, extraTopics){
|
| 91 |
const box=document.getElementById('hashtag-box');if(!box)return;
|
| 92 |
-
|
| 93 |
-
if(page===0)box.innerHTML=`<div class="hashtag-sources"><h3>🔍 ${topicLabel}</h3><div class="hashtag-loading"><div class="hashtag-spinner"></div>Đang tìm nguồn tin...</div></div>`;
|
| 94 |
try{
|
| 95 |
let url=`/api/hashtag/sources?topic=${encodeURIComponent(topic)}&page=${page}`;
|
| 96 |
if(extraTopics) url+=`&extra_topics=${encodeURIComponent(extraTopics)}`;
|
| 97 |
const r=await fetch(url);const j=await safeJson(r);
|
| 98 |
if(j.error){box.innerHTML=`<div class="hashtag-sources"><h3>🔍 ${esc(topic)}</h3><div style="color:#e74c3c;padding:8px">Lỗi: ${esc(j.error)}</div></div>`;return;}
|
| 99 |
const sources=j.sources||[];
|
| 100 |
-
if(!sources.length&&page===0){box.innerHTML=`<div class="hashtag-sources"><h3>🔍 ${
|
| 101 |
let h='';
|
| 102 |
if(page===0){
|
| 103 |
-
h=`<div class="hashtag-sources"><h3>🔍 ${
|
| 104 |
h+=`<div style="font-size:10px;color:#888;margin-bottom:6px">${j.total} bài từ nhiều nguồn</div>`;
|
| 105 |
h+=`<div id="ht-list">`;
|
| 106 |
}
|
|
@@ -116,36 +109,34 @@ async function rewriteHashtag(topic){
|
|
| 116 |
const btn=event?.target;
|
| 117 |
if(btn){btn.disabled=true;btn.textContent='Đang xử lý...';}
|
| 118 |
try{
|
| 119 |
-
// Timeout 60s cho rewrite
|
| 120 |
const r=await fetchWithTimeout('/api/topic_post',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({topic})},60000);
|
| 121 |
const j=await safeJson(r);
|
| 122 |
-
if(!r.ok||j.error)throw new Error(j.error||'Lỗi');
|
| 123 |
toast('✅ Đã đăng Tường AI!');
|
| 124 |
if(btn)btn.textContent='✅ Đã đăng!';
|
| 125 |
}catch(e){
|
| 126 |
-
if(e.message==='timeout')
|
| 127 |
-
else
|
| 128 |
if(btn){btn.disabled=false;btn.textContent='🤖 Rewrite AI';}
|
| 129 |
}
|
| 130 |
}
|
| 131 |
|
| 132 |
-
// ===== 🔥 HOT FEED — chỉ hiển thị
|
| 133 |
async function loadHotFeed(){
|
| 134 |
const box=document.getElementById('hot-feed');if(!box)return;
|
| 135 |
-
box.innerHTML=`<div class="hot-feed-section"><div class="hot-feed-header">🔥 HOT <span style="font-size:10px;color:#f0c040;font-weight:400">Đang tải
|
| 136 |
try{
|
| 137 |
const ht=await fetch('/api/hot_topics').then(r=>safeJson(r)).catch(()=>({topics:[]}));
|
| 138 |
const allTopics=(ht.topics||[]).slice(0,10);
|
| 139 |
if(!allTopics.length){box.innerHTML='';return;}
|
| 140 |
const topicCount=allTopics.length;
|
| 141 |
-
const headerLabel=topicCount>1?`🔥 HOT <span style="font-size:11px;color:#f0c040;font-weight:600">${topicCount} hashtag trending nhất</span>`:`🔥 HOT
|
| 142 |
const topicChipsHtml=allTopics.map(t=>{
|
| 143 |
const lbl=t.label||t.topic||'';
|
| 144 |
const tp=t.topic||t.label.replace(/^#/,'');
|
| 145 |
return`<span class="hot-feed-chip" onclick="searchTopic('${esc(tp).replace(/'/g,"\\'")}')">${esc(lbl)}</span>`;
|
| 146 |
}).join('');
|
| 147 |
-
|
| 148 |
-
h+=`</div>`;box.innerHTML=h;
|
| 149 |
}catch(e){box.innerHTML='';}
|
| 150 |
}
|
| 151 |
|
|
|
|
| 1 |
// === VNEWS Frontend v2 - Full Functions ===
|
| 2 |
+
// HOT feed: chỉ hiển thị hashtag chips, click vào hashtag thì hiện tin
|
| 3 |
+
// Fix: timeout cho rewrite calls (60s), không treo vô hạn
|
| 4 |
// Fix: safeJson parsing to handle non-JSON error responses gracefully
|
|
|
|
| 5 |
|
| 6 |
// === SAFE JSON PARSE ===
|
| 7 |
async function safeJson(resp){
|
|
|
|
| 9 |
const text=await resp.text();
|
| 10 |
try{return JSON.parse(text);}
|
| 11 |
catch(e){
|
|
|
|
| 12 |
console.warn('Non-JSON response:', text.slice(0,200));
|
| 13 |
return {error: 'Server error', _status: resp.status, _raw: text.slice(0,200)};
|
| 14 |
}
|
|
|
|
| 71 |
const topicText=t.topic||t.label.replace(/^#/,'');
|
| 72 |
return`<button class="hot-chip" onclick="searchTopic('${topicText.replace(/'/g,"\\'")}')">${esc(t.label)}</button>`;
|
| 73 |
}).join('');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
}
|
| 75 |
|
| 76 |
function searchTopic(topic){
|
| 77 |
if(!topic){topic=document.getElementById('topic-input')?.value.trim();if(!topic){alert('Nhập chủ đề');return;}}
|
| 78 |
document.getElementById('topic-input').value='';
|
| 79 |
_htTopic=topic;_htPage=0;
|
| 80 |
+
// Chỉ tìm tin của 1 hashtag được click
|
| 81 |
showHashtagSources(topic, 0, '');
|
| 82 |
}
|
| 83 |
|
| 84 |
async function showHashtagSources(topic, page, extraTopics){
|
| 85 |
const box=document.getElementById('hashtag-box');if(!box)return;
|
| 86 |
+
if(page===0)box.innerHTML=`<div class="hashtag-sources"><h3>🔍 ${esc(topic)}</h3><div class="hashtag-loading"><div class="hashtag-spinner"></div>Đang tìm nguồn tin...</div></div>`;
|
|
|
|
| 87 |
try{
|
| 88 |
let url=`/api/hashtag/sources?topic=${encodeURIComponent(topic)}&page=${page}`;
|
| 89 |
if(extraTopics) url+=`&extra_topics=${encodeURIComponent(extraTopics)}`;
|
| 90 |
const r=await fetch(url);const j=await safeJson(r);
|
| 91 |
if(j.error){box.innerHTML=`<div class="hashtag-sources"><h3>🔍 ${esc(topic)}</h3><div style="color:#e74c3c;padding:8px">Lỗi: ${esc(j.error)}</div></div>`;return;}
|
| 92 |
const sources=j.sources||[];
|
| 93 |
+
if(!sources.length&&page===0){box.innerHTML=`<div class="hashtag-sources"><h3>🔍 ${esc(topic)}</h3><div style="color:#888;padding:8px">Không tìm được bài viết</div></div>`;return;}
|
| 94 |
let h='';
|
| 95 |
if(page===0){
|
| 96 |
+
h=`<div class="hashtag-sources"><h3>🔍 ${esc(topic)}</h3>`;
|
| 97 |
h+=`<div style="font-size:10px;color:#888;margin-bottom:6px">${j.total} bài từ nhiều nguồn</div>`;
|
| 98 |
h+=`<div id="ht-list">`;
|
| 99 |
}
|
|
|
|
| 109 |
const btn=event?.target;
|
| 110 |
if(btn){btn.disabled=true;btn.textContent='Đang xử lý...';}
|
| 111 |
try{
|
|
|
|
| 112 |
const r=await fetchWithTimeout('/api/topic_post',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({topic})},60000);
|
| 113 |
const j=await safeJson(r);
|
| 114 |
+
if(!r.ok||j.error)throw new Error(j.error||'Lỗi server');
|
| 115 |
toast('✅ Đã đăng Tường AI!');
|
| 116 |
if(btn)btn.textContent='✅ Đã đăng!';
|
| 117 |
}catch(e){
|
| 118 |
+
if(e.message==='timeout')toast('❌ Quá lâu, thử lại');
|
| 119 |
+
else toast('❌ '+e.message);
|
| 120 |
if(btn){btn.disabled=false;btn.textContent='🤖 Rewrite AI';}
|
| 121 |
}
|
| 122 |
}
|
| 123 |
|
| 124 |
+
// ===== 🔥 HOT FEED — chỉ hiển thị hashtag chips =====
|
| 125 |
async function loadHotFeed(){
|
| 126 |
const box=document.getElementById('hot-feed');if(!box)return;
|
| 127 |
+
box.innerHTML=`<div class="hot-feed-section"><div class="hot-feed-header">🔥 HOT <span style="font-size:10px;color:#f0c040;font-weight:400">Đang tải...</span></div><div class="hot-feed-loading"><div class="hashtag-spinner"></div></div></div>`;
|
| 128 |
try{
|
| 129 |
const ht=await fetch('/api/hot_topics').then(r=>safeJson(r)).catch(()=>({topics:[]}));
|
| 130 |
const allTopics=(ht.topics||[]).slice(0,10);
|
| 131 |
if(!allTopics.length){box.innerHTML='';return;}
|
| 132 |
const topicCount=allTopics.length;
|
| 133 |
+
const headerLabel=topicCount>1?`🔥 HOT <span style="font-size:11px;color:#f0c040;font-weight:600">${topicCount} hashtag trending nhất</span>`:`🔥 HOT`;
|
| 134 |
const topicChipsHtml=allTopics.map(t=>{
|
| 135 |
const lbl=t.label||t.topic||'';
|
| 136 |
const tp=t.topic||t.label.replace(/^#/,'');
|
| 137 |
return`<span class="hot-feed-chip" onclick="searchTopic('${esc(tp).replace(/'/g,"\\'")}')">${esc(lbl)}</span>`;
|
| 138 |
}).join('');
|
| 139 |
+
box.innerHTML=`<div class="hot-feed-section"><div class="hot-feed-header">${headerLabel}</div><div class="hot-feed-topics">${topicChipsHtml}</div></div>`;
|
|
|
|
| 140 |
}catch(e){box.innerHTML='';}
|
| 141 |
}
|
| 142 |
|