bep40 commited on
Commit
d529d07
·
verified ·
1 Parent(s): ce8ef96

Fix: add /api/topic/rewrite handler and natural AI ask prompt

Browse files
Files changed (1) hide show
  1. ai_runtime_patch_fast.py +27 -29
ai_runtime_patch_fast.py CHANGED
@@ -51,7 +51,9 @@ def _bg_refresh():
51
 
52
  @app.on_event("startup")
53
  async def startup_prefetch():threading.Thread(target=_bg_refresh,daemon=True).start()
54
- threading.Thread(target=lambda:[time.sleep(600) or _bg_refresh() for _ in iter(int,1)],daemon=True).start()
 
 
55
 
56
  # ===== OVERRIDE ENDPOINTS =====
57
  app.router.routes=[r for r in app.router.routes if not (
@@ -104,7 +106,7 @@ async def post_short_comment(request:Request):
104
  db=_load_json(SHORT_COMMENTS_FILE,{});comments=db.get(vid,[]);comments.insert(0,{'text':text[:300],'ts':int(time.time())})
105
  db[vid]=comments[:100];_save_json(SHORT_COMMENTS_FILE,db);return JSONResponse({'comments':db[vid]})
106
 
107
- # ===== ARTICLE ASK AI — natural, friendly, based on content =====
108
  @app.post('/api/article/ask')
109
  async def article_ask(request:Request):
110
  body=await request.json();url=clean(body.get('url',''));question=clean(body.get('question',''));context=clean(body.get('context',''))
@@ -117,49 +119,49 @@ async def article_ask(request:Request):
117
  except Exception:pass
118
  if not raw and context:raw=context[:12000]
119
  if not raw:raw=question
120
- prompt=f"""Bạn là trợ lý thông minh của VNEWS. Người dùng đang đọc một bài viết/video và muốn hỏi bạn.
121
 
122
- Nội dung bài viết/video:
 
123
  Tiêu đề: {title}
124
- Nội dung: {raw[:10000]}
 
125
 
126
- Câu hỏi của người dùng: {question}
127
 
128
- Hãy trả lời bằng tiếng Việt một cách TỰ NHIÊN, thân thiện hữu ích:
129
- - Dựa vào nội dung bài viết/video ở trên làm ngữ cảnh chính.
130
- - Giải thích rõ ràng, dễ hiểu như đang trò chuyện.
131
- - Nếu câu hỏi liên quan đến bài, hãy trả lời chi tiết dựa trên thông tin trong bài.
132
- - Nếu câu hỏi vượt ra ngoài bài nhưng liên quan đến chủ đề, bạn vẫn có thể bổ sung kiến thức chung để giúp người đọc hiểu hơn, nhưng nói rõ đó kiến thức bổ sung.
133
- - Tránh trả lời kiểu "bài viết không đề cập" một cách khô khan. Thay vào đó hãy nói "Trong bài này chưa nói đến phần đó, nhưng theo những tôi biết thì..." nếu bạn thể bổ sung.
134
  """
135
  ans=await f5.base.qwen_generate(prompt,max_tokens=1200)
136
  if not ans:ans='AI chưa trả lời được lúc này. Bạn thử hỏi lại nhé.'
137
  return JSONResponse({'answer':ans,'title':title})
138
 
139
- # ===== TOPIC REWRITE (dedicated endpoint for topic posts) =====
140
  @app.post('/api/topic/rewrite')
141
  async def topic_rewrite(request:Request):
142
- """Rewrite/summarize a topic post's content and source articles into a new wall post."""
143
  body=await request.json();post_id=str(body.get('post_id','')).strip()
144
  if not post_id:return JSONResponse({'error':'missing post_id'},status_code=400)
145
  posts=f5.base._load_ai_wall();p=next((x for x in posts if str(x.get('id'))==post_id),None)
146
- if not p:return JSONResponse({'error':'post not found'},status_code=404)
147
- # Collect all available content: post text + source details content
148
- all_content=p.get('text') or ''
149
  for s in (p.get('source_details') or []):
150
- all_content+='\n\n'+s.get('content','')
151
  title=p.get('title') or 'Bài viết AI'
152
- prompt=f"""Bạn là biên tập viên VNEWS. Hãy VIẾT LẠI bài dưới đây thành một bản tóm tắt mới, ngắn gọn, dễ đọc, đăng lên Tường AI.
153
 
154
  Tiêu đề gốc: {title}
155
- Nội dung gốc và nguồn:
156
  {all_content[:16000]}
157
 
158
  Yêu cầu:
159
- - Viết lại bằng ngôn ngữ tự nhiên, mạch lạc.
160
- - Giữ đúng sự thật, không bịa.
161
  - 1 tiêu đề mới hấp dẫn.
162
- - 2 đoạn ngắn hoặc 4-5 ý chính.
 
163
  - Cuối bài ghi nguồn tham khảo.
164
  """
165
  text=None
@@ -169,7 +171,7 @@ Yêu cầu:
169
  text=f"Tóm tắt: {title}\n\n{all_content[:1500]}\n\nNguồn tham khảo: VNEWS AI"
170
  new_post=f5.base.make_post('Rewrite: '+title,text,p.get('img',''),'','rewrite_topic',sources=p.get('sources',[]))
171
  new_post['images']=p.get('images',[])
172
- posts=f5.base._load_ai_wall();posts.insert(0,new_post);f5.base._save_ai_wall(posts)
173
  return JSONResponse({'post':new_post})
174
 
175
  # ===== TOPIC POST =====
@@ -235,7 +237,6 @@ h+=`</div>`;
235
  document.getElementById('view-article').innerHTML=h;
236
  let art=document.querySelector('.article-view');if(art&&p.source_details)renderSourceDetails(p,art);
237
  window.scrollTo(0,0);}
238
-
239
  window.readAIWallPost=readAIWallPost;window.aiReadWallPatched=window.aiReadWall=window.readWallPost=function(i){readAIWallPost(i)};
240
 
241
  window.rewriteTopicPost=async function(postId){
@@ -247,12 +248,10 @@ window.askAIWall=async function(i){
247
  let q=document.getElementById('article-ai-q')?.value.trim();if(!q)return alert('Nhập câu hỏi');
248
  let ans=document.getElementById('article-ai-ans');ans.textContent='Đang hỏi AI...';
249
  let wall=(await fetch('/api/ai_wall').then(r=>r.json()).catch(()=>({posts:[]}))).posts||[];
250
- let p=wall[i]||{};let context=(p.text||'');
251
- for(let s of (p.source_details||[]))context+='\n'+s.content;
252
  let url=(p.source_details||[]).find(s=>s.url)?.url||(p.sources||[]).find(s=>s.url)?.url||'';
253
  try{let r=await fetch('/api/article/ask',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url,question:q,context:context.slice(0,12000)})});let j=await r.json();ans.textContent=j.answer||'Không có trả lời';}catch(e){ans.textContent='Lỗi: '+e.message}};
254
 
255
- // === Short comments ===
256
  window.openShortComments=async function(id){
257
  let panel=document.getElementById('short-cmt-panel');
258
  let j=await fetch('/api/short/comments?id='+encodeURIComponent(id)).then(r=>r.json()).catch(()=>({comments:[]}));
@@ -267,7 +266,6 @@ document.getElementById('cmt-text').value='';}
267
 
268
  function patchShortButtons(){document.querySelectorAll('.tiktok-slide').forEach(sl=>{if(sl.dataset.cmtDone)return;sl.dataset.cmtDone='1';let id=sl.dataset.id||'';if(!id)return;let right=sl.querySelector('.tiktok-right,.short-action-panel');if(!right)return;if(right.querySelector('[data-cmt]'))return;let btn=document.createElement('button');btn.className='short-action-btn';btn.setAttribute('data-cmt','1');btn.innerHTML='<div class="ico" style="width:42px;height:42px;border-radius:50%;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;font-size:20px">💬</div><span>BL</span>';btn.onclick=function(e){e.stopPropagation();openShortComments(id);};right.appendChild(btn);});}
269
 
270
- // === Single rewrite + ask AI for regular articles ===
271
  function addSingleRewriteBtn(){let art=document.querySelector('#view-article .article-view');if(!art)return;let btns=art.querySelectorAll('[data-rewrite]');if(btns.length>1)for(let i=1;i<btns.length;i++)btns[i].remove();if(btns.length>=1)return;let actions=art.querySelector('.article-actions');if(!actions)return;let btn=document.createElement('button');btn.className='primary';btn.setAttribute('data-rewrite','1');btn.textContent='🤖 Tóm tắt AI đăng tường';btn.onclick=function(){if(typeof rewriteCurrentArticle==='function')rewriteCurrentArticle();else alert('Chức năng rewrite chưa sẵn sàng.')};actions.insertBefore(btn,actions.firstChild);}
272
  function addArticleAskBox(){let art=document.querySelector('#view-article .article-view');if(!art||art.querySelector('.article-ai-ask'))return;let box=document.createElement('div');box.className='article-ai-ask';box.innerHTML='<h3 style="font-size:14px;color:#5cb87a;margin-bottom:6px">🤖 Hỏi AI về bài viết</h3><textarea id="article-ai-question" placeholder="Hỏi bất cứ điều gì về nội dung này..."></textarea><button onclick="askArticleAI()">Hỏi AI</button><div id="article-ai-answer" class="article-ai-answer"></div>';art.appendChild(box);}
273
  window.askArticleAI=async function(){let q=document.getElementById('article-ai-question')?.value.trim();if(!q)return alert('Nhập câu hỏi');let ans=document.getElementById('article-ai-answer');ans.textContent='Đang hỏi AI...';let url=(window._currentArticle&&window._currentArticle.url)||'';let context=document.querySelector('.article-view')?.innerText?.slice(0,12000)||'';try{let r=await fetch('/api/article/ask',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url,question:q,context})});let j=await r.json();ans.textContent=j.answer||'Không có trả lời';}catch(e){ans.textContent='Lỗi: '+e.message}}
 
51
 
52
  @app.on_event("startup")
53
  async def startup_prefetch():threading.Thread(target=_bg_refresh,daemon=True).start()
54
+ def _periodic():
55
+ while True:time.sleep(600);_bg_refresh()
56
+ threading.Thread(target=_periodic,daemon=True).start()
57
 
58
  # ===== OVERRIDE ENDPOINTS =====
59
  app.router.routes=[r for r in app.router.routes if not (
 
106
  db=_load_json(SHORT_COMMENTS_FILE,{});comments=db.get(vid,[]);comments.insert(0,{'text':text[:300],'ts':int(time.time())})
107
  db[vid]=comments[:100];_save_json(SHORT_COMMENTS_FILE,db);return JSONResponse({'comments':db[vid]})
108
 
109
+ # ===== ARTICLE ASK AI — natural and friendly =====
110
  @app.post('/api/article/ask')
111
  async def article_ask(request:Request):
112
  body=await request.json();url=clean(body.get('url',''));question=clean(body.get('question',''));context=clean(body.get('context',''))
 
119
  except Exception:pass
120
  if not raw and context:raw=context[:12000]
121
  if not raw:raw=question
122
+ prompt=f"""Bạn là trợ lý thông minh của VNEWS. Người dùng đang đọc một bài viết/xem video và muốn hỏi bạn.
123
 
124
+ Nội dung bài viết/video mà người dùng đang xem:
125
+ ---
126
  Tiêu đề: {title}
127
+ {raw[:10000]}
128
+ ---
129
 
130
+ Câu hỏi: {question}
131
 
132
+ Hãy trả lời bằng tiếng Việt, tự nhiên thân thiện như đang trò chuyện:
133
+ - Lấy thông tin từ nội dung bài viết/video ở trên làm sở chính.
134
+ - Giải thích dễ hiểu, dụ nếu cần.
135
+ - Nếu câu hỏi liên quan đến bài trả lời chi tiết từ nội dung bài.
136
+ - Nếu câu hỏi mở rộng hơn bổ sung kiến thức chung nhưng nói rõ "ngoài bài viết, theo hiểu biết chung thì..."
137
+ - Không trả lời khô khan, máy móc. Hãy viết như một người bạn am hiểu đang giải thích.
138
  """
139
  ans=await f5.base.qwen_generate(prompt,max_tokens=1200)
140
  if not ans:ans='AI chưa trả lời được lúc này. Bạn thử hỏi lại nhé.'
141
  return JSONResponse({'answer':ans,'title':title})
142
 
143
+ # ===== TOPIC REWRITE takes post_id, rewrites using all source content =====
144
  @app.post('/api/topic/rewrite')
145
  async def topic_rewrite(request:Request):
 
146
  body=await request.json();post_id=str(body.get('post_id','')).strip()
147
  if not post_id:return JSONResponse({'error':'missing post_id'},status_code=400)
148
  posts=f5.base._load_ai_wall();p=next((x for x in posts if str(x.get('id'))==post_id),None)
149
+ if not p:return JSONResponse({'error':'Không tìm thấy bài viết.'},status_code=404)
150
+ all_content=(p.get('text') or '')
 
151
  for s in (p.get('source_details') or []):
152
+ all_content+='\n\n--- Nguồn: '+s.get('title','')+' ---\n'+s.get('content','')
153
  title=p.get('title') or 'Bài viết AI'
154
+ prompt=f"""Bạn là biên tập viên VNEWS. Viết lại nội dung bên dưới thành BẢN TÓM TẮT MỚI, hấp dẫn, đăng lên Tường AI.
155
 
156
  Tiêu đề gốc: {title}
157
+ Nội dung gốc và nguồn tham khảo:
158
  {all_content[:16000]}
159
 
160
  Yêu cầu:
161
+ - Viết lại tự nhiên, mạch lạc, không sao chép nguyên văn.
 
162
  - 1 tiêu đề mới hấp dẫn.
163
+ - 1 đoạn mở đầu + 3-5 ý chính.
164
+ - Giữ sự thật, không bịa.
165
  - Cuối bài ghi nguồn tham khảo.
166
  """
167
  text=None
 
171
  text=f"Tóm tắt: {title}\n\n{all_content[:1500]}\n\nNguồn tham khảo: VNEWS AI"
172
  new_post=f5.base.make_post('Rewrite: '+title,text,p.get('img',''),'','rewrite_topic',sources=p.get('sources',[]))
173
  new_post['images']=p.get('images',[])
174
+ all_posts=f5.base._load_ai_wall();all_posts.insert(0,new_post);f5.base._save_ai_wall(all_posts)
175
  return JSONResponse({'post':new_post})
176
 
177
  # ===== TOPIC POST =====
 
237
  document.getElementById('view-article').innerHTML=h;
238
  let art=document.querySelector('.article-view');if(art&&p.source_details)renderSourceDetails(p,art);
239
  window.scrollTo(0,0);}
 
240
  window.readAIWallPost=readAIWallPost;window.aiReadWallPatched=window.aiReadWall=window.readWallPost=function(i){readAIWallPost(i)};
241
 
242
  window.rewriteTopicPost=async function(postId){
 
248
  let q=document.getElementById('article-ai-q')?.value.trim();if(!q)return alert('Nhập câu hỏi');
249
  let ans=document.getElementById('article-ai-ans');ans.textContent='Đang hỏi AI...';
250
  let wall=(await fetch('/api/ai_wall').then(r=>r.json()).catch(()=>({posts:[]}))).posts||[];
251
+ let p=wall[i]||{};let context=(p.text||'');for(let s of (p.source_details||[]))context+='\n'+s.content;
 
252
  let url=(p.source_details||[]).find(s=>s.url)?.url||(p.sources||[]).find(s=>s.url)?.url||'';
253
  try{let r=await fetch('/api/article/ask',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url,question:q,context:context.slice(0,12000)})});let j=await r.json();ans.textContent=j.answer||'Không có trả lời';}catch(e){ans.textContent='Lỗi: '+e.message}};
254
 
 
255
  window.openShortComments=async function(id){
256
  let panel=document.getElementById('short-cmt-panel');
257
  let j=await fetch('/api/short/comments?id='+encodeURIComponent(id)).then(r=>r.json()).catch(()=>({comments:[]}));
 
266
 
267
  function patchShortButtons(){document.querySelectorAll('.tiktok-slide').forEach(sl=>{if(sl.dataset.cmtDone)return;sl.dataset.cmtDone='1';let id=sl.dataset.id||'';if(!id)return;let right=sl.querySelector('.tiktok-right,.short-action-panel');if(!right)return;if(right.querySelector('[data-cmt]'))return;let btn=document.createElement('button');btn.className='short-action-btn';btn.setAttribute('data-cmt','1');btn.innerHTML='<div class="ico" style="width:42px;height:42px;border-radius:50%;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;font-size:20px">💬</div><span>BL</span>';btn.onclick=function(e){e.stopPropagation();openShortComments(id);};right.appendChild(btn);});}
268
 
 
269
  function addSingleRewriteBtn(){let art=document.querySelector('#view-article .article-view');if(!art)return;let btns=art.querySelectorAll('[data-rewrite]');if(btns.length>1)for(let i=1;i<btns.length;i++)btns[i].remove();if(btns.length>=1)return;let actions=art.querySelector('.article-actions');if(!actions)return;let btn=document.createElement('button');btn.className='primary';btn.setAttribute('data-rewrite','1');btn.textContent='🤖 Tóm tắt AI đăng tường';btn.onclick=function(){if(typeof rewriteCurrentArticle==='function')rewriteCurrentArticle();else alert('Chức năng rewrite chưa sẵn sàng.')};actions.insertBefore(btn,actions.firstChild);}
270
  function addArticleAskBox(){let art=document.querySelector('#view-article .article-view');if(!art||art.querySelector('.article-ai-ask'))return;let box=document.createElement('div');box.className='article-ai-ask';box.innerHTML='<h3 style="font-size:14px;color:#5cb87a;margin-bottom:6px">🤖 Hỏi AI về bài viết</h3><textarea id="article-ai-question" placeholder="Hỏi bất cứ điều gì về nội dung này..."></textarea><button onclick="askArticleAI()">Hỏi AI</button><div id="article-ai-answer" class="article-ai-answer"></div>';art.appendChild(box);}
271
  window.askArticleAI=async function(){let q=document.getElementById('article-ai-question')?.value.trim();if(!q)return alert('Nhập câu hỏi');let ans=document.getElementById('article-ai-answer');ans.textContent='Đang hỏi AI...';let url=(window._currentArticle&&window._currentArticle.url)||'';let context=document.querySelector('.article-view')?.innerText?.slice(0,12000)||'';try{let r=await fetch('/api/article/ask',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url,question:q,context})});let j=await r.json();ans.textContent=j.answer||'Không có trả lời';}catch(e){ans.textContent='Lỗi: '+e.message}}