bep40 commited on
Commit
42afe2b
·
verified ·
1 Parent(s): 76e8ad7

Fix: Xóa prompt nhà báo, cải thiện slide kết thúc câu hoàn chỉnh

Browse files
Files changed (1) hide show
  1. app_v2_entry.py +78 -37
app_v2_entry.py CHANGED
@@ -33,6 +33,23 @@ app.router.routes=[r for r in app.router.routes if not isinstance(r, Mount)]
33
 
34
  def _clean(s): return re.sub(r"\s+"," ",html_lib.unescape(str(s or""))).strip()
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  # Cache for match details (5 min TTL)
37
  _match_cache = {}
38
 
@@ -1599,7 +1616,6 @@ async def api_personal_post_preview(request: Request):
1599
  source_context += f" - {p[:300]}\n"
1600
 
1601
  prompt = (
1602
- "Bạn là nhà báo chuyên nghiệp tiếng Việt. Hãy viết bài phân tích dựa trên quan điểm cá nhân và các nguồn tin sau.\n\n"
1603
  "=== QUAN ĐIỂM CÁ NHÂN ===\n" + opinion[:2000] + "\n\n"
1604
  "=== NGUỒN TIN THAM KHẢO ===\n" + source_context + "\n\n"
1605
  "=== YÊU CẦU VIẾT BÀI THEO SLIDE ===\n"
@@ -1607,26 +1623,27 @@ async def api_personal_post_preview(request: Request):
1607
  "\n"
1608
  "SLIDE 1 - MỞ ĐẦU:\n"
1609
  "- Giới thiệu chủ đề, nêu quan điểm cá nhân (dựa vào QUAN ĐIỂM CÁ NHÂN)\n"
1610
- "- 2-4 câu, thể hiện rõ góc nhìn của bạn\n"
1611
  "\n"
1612
  "SLIDE 2-3-4-5 - PHÂN TÍCH:\n"
1613
  "- Mỗi slide dùng 1 nguồn tin CỤ THỂ, kết hợp với quan điểm cá nhân\n"
1614
  "- Dẫn chứng từ nguồn (ghi rõ tên báo: Theo VnExpress, ...)\n"
1615
- "- 2-4 câu mỗi slide, trình bày như 1 đoạn văn hoàn chỉnh\n"
1616
  "\n"
1617
  "SLIDE 6 - KẾT LUẬN:\n"
1618
  "- Tổng kết quan điểm, đưa ra nhận định cuối cùng\n"
1619
- "- 2-3 câu\n"
1620
  "\n"
1621
  "Định dạng đầu ra:\n"
1622
  "---SLIDE 1---\n"
1623
- "[nội dung đoạn văn slide 1]\n"
1624
  "---SLIDE 2---\n"
1625
- "[nội dung đoạn văn slide 2]\n"
1626
  "...v.v...\n"
1627
  "\n"
1628
  "QUAN TRỌNG:\n"
1629
  "- Mỗi slide là 1 đoạn văn HOÀN CHỈNH, 2-4 câu, đọc liền mạch\n"
 
1630
  "- Kết hợp QUAN ĐIỂM CÁ NHÂN với NỘI DUNG NGUỒN TIN\n"
1631
  "- Không gạch đầu dòng, không bullet points\n"
1632
  "- Viết liền mạch tự nhiên, giọng văn báo chí\n"
@@ -1656,7 +1673,8 @@ async def api_personal_post_preview(request: Request):
1656
  matches = re.findall(pattern, ai_text, re.DOTALL)
1657
  if matches:
1658
  for idx, (num, content) in enumerate(matches):
1659
- text = _clean(content)
 
1660
  if len(text) > 30:
1661
  img = source_images[idx] if idx < len(source_images) else ""
1662
  slides.append({"text": text, "image": img, "index": idx + 1})
@@ -1670,9 +1688,11 @@ async def api_personal_post_preview(request: Request):
1670
  p = p.strip()
1671
  if p.startswith('#') or p.startswith('---') or p.startswith('*Nguồn'):
1672
  continue
1673
- if len(p) > 50:
 
 
1674
  img = source_images[para_count] if para_count < len(source_images) else ""
1675
- slides.append({"text": p, "image": img, "index": para_count + 1})
1676
  para_count += 1
1677
  if para_count >= 6:
1678
  break
@@ -1681,11 +1701,24 @@ async def api_personal_post_preview(request: Request):
1681
  # Fallback: create from opinion
1682
  slides = [{"text": f"Quan điểm: {opinion[:300]}", "image": source_images[0] if source_images else "", "index": 1}]
1683
  if ai_text:
1684
- chunks = [ai_text[i:i+400] for i in range(0, len(ai_text), 400)]
1685
- for i, chunk in enumerate(chunks[:5]):
1686
- if len(chunk) > 50:
1687
- img = source_images[i+1] if i+1 < len(source_images) else ""
1688
- slides.append({"text": chunk, "image": img, "index": i + 2})
 
 
 
 
 
 
 
 
 
 
 
 
 
1689
 
1690
  preview = {
1691
  "title": title,
@@ -1786,20 +1819,19 @@ async def api_personal_post(request: Request):
1786
  for j, p in enumerate(src_paras[:3]):
1787
  source_context += f" - {p[:300]}\n"
1788
  prompt = (
1789
- "Bạn nhà báo tiếng Việt. Viết bài phân tích dựa trên quan điểm cá nhân và các nguồn tin sau.\n\n"
1790
- "=== QUAN ĐIỂM ===\n" + opinion[:2000] + "\n\n"
1791
  "=== NGUỒN TIN ===\n" + source_context + "\n\n"
1792
  "=== YÊU CẦU VIẾT BÀI THEO SLIDE ===\n"
1793
  "Viết bài thành 5-6 ĐOẠN VĂN NGẮN, mỗi đoạn là 1 SLIDE.\n"
1794
  "\n"
1795
- "SLIDE 1 - MỞ ĐẦU: Giới thiệu chủ đề, nêu quan điểm cá nhân (2-4 câu)\n"
1796
- "SLIDE 2-3-4-5 - PHÂN TÍCH: Mỗi slide dùng 1 nguồn tin cụ thể, kết hợp quan điểm cá nhân, ghi rõ nguồn (Theo VnExpress...), 2-4 câu, thành 1 đoạn văn hoàn chỉnh\n"
1797
- "SLIDE 6 - KẾT LUẬN: Tổng kết quan điểm, nhận định cuối cùng (2-3 câu)\n"
1798
  "\n"
1799
  "Định dạng:\n"
1800
- "---SLIDE 1---\n[đoạn văn]\n---SLIDE 2---\n[đoạn văn]\n...\n"
1801
  "\n"
1802
- "QUAN TRỌNG: Mỗi slide là 1 đoạn văn HOÀN CHỈNH, 2-4 câu. Kết hợp QUAN ĐIỂM + NGUỒN TIN. Không gạch đầu dòng. Viết liền mạch. 300-600 từ."
1803
  )
1804
  ai_text = await ai_ext.qwen_generate(prompt, max_tokens=2000)
1805
  except:
@@ -1830,7 +1862,8 @@ async def api_personal_post(request: Request):
1830
  matches = re.findall(pattern, ai_text, re.DOTALL)
1831
  if matches:
1832
  for idx, (num, content) in enumerate(matches):
1833
- text = _clean(content)
 
1834
  if len(text) > 30:
1835
  img = source_images[idx] if idx < len(source_images) else ""
1836
  slides.append({"text": text, "image": img, "index": idx + 1})
@@ -1844,9 +1877,11 @@ async def api_personal_post(request: Request):
1844
  p = p.strip()
1845
  if p.startswith('#') or p.startswith('---') or p.startswith('*Nguồn'):
1846
  continue
1847
- if len(p) > 50:
 
 
1848
  img = source_images[para_count] if para_count < len(source_images) else ""
1849
- slides.append({"text": p, "image": img, "index": para_count + 1})
1850
  para_count += 1
1851
  if para_count >= 6:
1852
  break
@@ -1854,19 +1889,25 @@ async def api_personal_post(request: Request):
1854
  if len(slides) < 2:
1855
  slides = [{"text": f"Quan điểm: {opinion[:300]}", "image": source_images[0] if source_images else "", "index": 1}]
1856
  if ai_text:
1857
- chunks = [ai_text[i:i+400] for i in range(0, len(ai_text), 400)]
1858
- for i, chunk in enumerate(chunks[:5]):
1859
- if len(chunk) > 50:
1860
- img = source_images[i+1] if i+1 < len(source_images) else ""
1861
- slides.append({"text": chunk, "image": img, "index": i + 2})
1862
- total = min(len(slide_parts), 6)
1863
- for i in range(total):
1864
- img = source_images[i] if i < len(source_images) else ""
1865
- slides.append({
1866
- "text": slide_parts[i],
1867
- "image": img,
1868
- "index": i + 1
1869
- })
 
 
 
 
 
 
1870
 
1871
  lang, emotion = detect_language_and_emotion(title, ai_text)
1872
  voice = get_voice_for_content(title, ai_text)
 
33
 
34
  def _clean(s): return re.sub(r"\s+"," ",html_lib.unescape(str(s or""))).strip()
35
 
36
+ def _ensure_sentence_complete(text):
37
+ """Ensure text ends with complete sentence (ends with . ! or ?). Trim if cut mid-sentence."""
38
+ text = _clean(text)
39
+ if not text:
40
+ return text
41
+ # Find last sentence ending
42
+ for end_char in ['.', '!', '?']:
43
+ last_pos = text.rfind(end_char)
44
+ if last_pos > len(text) * 0.5: # Keep if ending is in latter half
45
+ return text[:last_pos + 1].strip()
46
+ # If no ending found, try to find last complete sentence
47
+ sentences = re.split(r'(?<=[.!?])\s+', text)
48
+ complete = [s.strip() for s in sentences if s.strip() and len(s.strip()) > 20]
49
+ if complete[:-1]: # Return all but last incomplete
50
+ return ' '.join(complete[:-1])
51
+ return text[:150] + '.' if len(text) > 150 else text
52
+
53
  # Cache for match details (5 min TTL)
54
  _match_cache = {}
55
 
 
1616
  source_context += f" - {p[:300]}\n"
1617
 
1618
  prompt = (
 
1619
  "=== QUAN ĐIỂM CÁ NHÂN ===\n" + opinion[:2000] + "\n\n"
1620
  "=== NGUỒN TIN THAM KHẢO ===\n" + source_context + "\n\n"
1621
  "=== YÊU CẦU VIẾT BÀI THEO SLIDE ===\n"
 
1623
  "\n"
1624
  "SLIDE 1 - MỞ ĐẦU:\n"
1625
  "- Giới thiệu chủ đề, nêu quan điểm cá nhân (dựa vào QUAN ĐIỂM CÁ NHÂN)\n"
1626
+ "- 2-4 câu hoàn chỉnh, thể hiện rõ góc nhìn của bạn\n"
1627
  "\n"
1628
  "SLIDE 2-3-4-5 - PHÂN TÍCH:\n"
1629
  "- Mỗi slide dùng 1 nguồn tin CỤ THỂ, kết hợp với quan điểm cá nhân\n"
1630
  "- Dẫn chứng từ nguồn (ghi rõ tên báo: Theo VnExpress, ...)\n"
1631
+ "- 2-4 câu hoàn chỉnh mỗi slide, trình bày như 1 đoạn văn hoàn chỉnh\n"
1632
  "\n"
1633
  "SLIDE 6 - KẾT LUẬN:\n"
1634
  "- Tổng kết quan điểm, đưa ra nhận định cuối cùng\n"
1635
+ "- 2-3 câu hoàn chỉnh\n"
1636
  "\n"
1637
  "Định dạng đầu ra:\n"
1638
  "---SLIDE 1---\n"
1639
+ "[nội dung đoạn văn slide 1 - PHẢI KẾT THÚC BẰNG DẤU CHẤM, CHỤM HỎI hoặc CHẤM CÂU]\n"
1640
  "---SLIDE 2---\n"
1641
+ "[nội dung đoạn văn slide 2 - PHẢI KẾT THÚC BẰNG DẤU CHẤM, CHỤM HỎI hoặc CHẤM CÂU]\n"
1642
  "...v.v...\n"
1643
  "\n"
1644
  "QUAN TRỌNG:\n"
1645
  "- Mỗi slide là 1 đoạn văn HOÀN CHỈNH, 2-4 câu, đọc liền mạch\n"
1646
+ "- PHẢI KẾT THÚC BẰNG DẤU CHẤM (.) HOẶN TOÀN\n"
1647
  "- Kết hợp QUAN ĐIỂM CÁ NHÂN với NỘI DUNG NGUỒN TIN\n"
1648
  "- Không gạch đầu dòng, không bullet points\n"
1649
  "- Viết liền mạch tự nhiên, giọng văn báo chí\n"
 
1673
  matches = re.findall(pattern, ai_text, re.DOTALL)
1674
  if matches:
1675
  for idx, (num, content) in enumerate(matches):
1676
+ # Normalize: ensure complete sentences
1677
+ text = _ensure_sentence_complete(content)
1678
  if len(text) > 30:
1679
  img = source_images[idx] if idx < len(source_images) else ""
1680
  slides.append({"text": text, "image": img, "index": idx + 1})
 
1688
  p = p.strip()
1689
  if p.startswith('#') or p.startswith('---') or p.startswith('*Nguồn'):
1690
  continue
1691
+ # Combine multiple short paras into one slide, normalize
1692
+ p_normalized = _ensure_sentence_complete(p)
1693
+ if len(p_normalized) > 50:
1694
  img = source_images[para_count] if para_count < len(source_images) else ""
1695
+ slides.append({"text": p_normalized, "image": img, "index": para_count + 1})
1696
  para_count += 1
1697
  if para_count >= 6:
1698
  break
 
1701
  # Fallback: create from opinion
1702
  slides = [{"text": f"Quan điểm: {opinion[:300]}", "image": source_images[0] if source_images else "", "index": 1}]
1703
  if ai_text:
1704
+ # Split by sentences for better chunks
1705
+ sentences = re.split(r'(?<=[.!?])\s+', ai_text)
1706
+ current_chunk = ""
1707
+ for sent in sentences:
1708
+ sent = _ensure_sentence_complete(sent)
1709
+ if len(sent) > 10 and current_chunk:
1710
+ if len(current_chunk + sent) < 400:
1711
+ current_chunk += " " + sent
1712
+ else:
1713
+ if len(current_chunk) > 50:
1714
+ idx = len(slides)
1715
+ img = source_images[idx] if idx < len(source_images) else ""
1716
+ slides.append({"text": current_chunk, "image": img, "index": idx + 1})
1717
+ current_chunk = sent
1718
+ if len(current_chunk) > 50 and len(slides) < 6:
1719
+ idx = len(slides)
1720
+ img = source_images[idx] if idx < len(source_images) else ""
1721
+ slides.append({"text": current_chunk, "image": img, "index": idx + 1})
1722
 
1723
  preview = {
1724
  "title": title,
 
1819
  for j, p in enumerate(src_paras[:3]):
1820
  source_context += f" - {p[:300]}\n"
1821
  prompt = (
1822
+ "=== QUAN ĐIỂM NHÂN ===\n" + opinion[:2000] + "\n\n"
 
1823
  "=== NGUỒN TIN ===\n" + source_context + "\n\n"
1824
  "=== YÊU CẦU VIẾT BÀI THEO SLIDE ===\n"
1825
  "Viết bài thành 5-6 ĐOẠN VĂN NGẮN, mỗi đoạn là 1 SLIDE.\n"
1826
  "\n"
1827
+ "SLIDE 1 - MỞ ĐẦU: Giới thiệu chủ đề, nêu quan điểm cá nhân (2-4 câu hoàn chỉnh)\n"
1828
+ "SLIDE 2-3-4-5 - PHÂN TÍCH: Mỗi slide dùng 1 nguồn tin cụ thể, kết hợp quan điểm cá nhân, ghi rõ nguồn (Theo VnExpress...), 2-4 câu hoàn chỉnh, thành 1 đoạn văn hoàn chỉnh\n"
1829
+ "SLIDE 6 - KẾT LUẬN: Tổng kết quan điểm, nhận định cuối cùng (2-3 câu hoàn chỉnh)\n"
1830
  "\n"
1831
  "Định dạng:\n"
1832
+ "---SLIDE 1---\n[đoạn văn hoàn chỉnh kết thúc bằng dấu chấm]\n---SLIDE 2---\n[đoạn văn hoàn chỉnh kết thúc bằng dấu chấm]\n...\n"
1833
  "\n"
1834
+ "QUAN TRỌNG: Mỗi slide là 1 đoạn văn HOÀN CHỈNH, 2-4 câu, PHẢI KẾT THÚC BẰNG DẤU CHẤM (.). Kết hợp QUAN ĐIỂM + NGUỒN TIN. Không gạch đầu dòng. Viết liền mạch. 300-600 từ."
1835
  )
1836
  ai_text = await ai_ext.qwen_generate(prompt, max_tokens=2000)
1837
  except:
 
1862
  matches = re.findall(pattern, ai_text, re.DOTALL)
1863
  if matches:
1864
  for idx, (num, content) in enumerate(matches):
1865
+ # Normalize: ensure complete sentences
1866
+ text = _ensure_sentence_complete(content)
1867
  if len(text) > 30:
1868
  img = source_images[idx] if idx < len(source_images) else ""
1869
  slides.append({"text": text, "image": img, "index": idx + 1})
 
1877
  p = p.strip()
1878
  if p.startswith('#') or p.startswith('---') or p.startswith('*Nguồn'):
1879
  continue
1880
+ # Normalize: ensure complete sentences
1881
+ p_normalized = _ensure_sentence_complete(p)
1882
+ if len(p_normalized) > 50:
1883
  img = source_images[para_count] if para_count < len(source_images) else ""
1884
+ slides.append({"text": p_normalized, "image": img, "index": para_count + 1})
1885
  para_count += 1
1886
  if para_count >= 6:
1887
  break
 
1889
  if len(slides) < 2:
1890
  slides = [{"text": f"Quan điểm: {opinion[:300]}", "image": source_images[0] if source_images else "", "index": 1}]
1891
  if ai_text:
1892
+ # Split by sentences for better chunks
1893
+ sentences = re.split(r'(?<=[.!?])\s+', ai_text)
1894
+ current_chunk = ""
1895
+ for sent in sentences:
1896
+ sent = _ensure_sentence_complete(sent)
1897
+ if len(sent) > 10:
1898
+ if current_chunk and len(current_chunk + sent) < 400:
1899
+ current_chunk += " " + sent
1900
+ elif len(current_chunk) > 50:
1901
+ idx = len(slides)
1902
+ img = source_images[idx] if idx < len(source_images) else ""
1903
+ slides.append({"text": current_chunk, "image": img, "index": idx + 1})
1904
+ current_chunk = sent
1905
+ else:
1906
+ current_chunk = sent
1907
+ if len(current_chunk) > 50 and len(slides) < 6:
1908
+ idx = len(slides)
1909
+ img = source_images[idx] if idx < len(source_images) else ""
1910
+ slides.append({"text": current_chunk, "image": img, "index": idx + 1})
1911
 
1912
  lang, emotion = detect_language_and_emotion(title, ai_text)
1913
  voice = get_voice_for_content(title, ai_text)