mirror of
https://github.com/Jeuners/astra-vision.git
synced 2026-09-09 15:02:35 +02:00
Löst die RSS-Beschränkung: Feed-Einträge liefern nur eine kurze Teaser-Beschreibung, nie den vollen Artikeltext. - astra/articles.py: lädt eine URL, extrahiert den reinen Fließtext via trafilatura (ohne Navigation/Werbung/Boilerplate). Live gegen einen echten RP-ONLINE-Artikel verifiziert (1682 Zeichen sauberer Text). - read_article-Tool in tools.py, gleiches tool_start/tool_result/ tool_error-Muster wie die anderen beiden Tools. Beschreibung nennt explizit den erwarteten Anwendungsfall (Nachfrage zu einer schon genannten Schlagzeile) — konsequent nach der zuletzt beschlossenen Regel, Tool-Verhalten nur in der FunctionSchema, nie im SYSTEM_PROMPT. Gemessen: mit drei Tools sinkt read_news weiter auf ca. 15-20% (vorher mit zwei Tools 20-50%). read_article selbst liegt bei einer konkreten Nachfrage zu einer schon im Kontext stehenden Schlagzeile bei ~65% (2/3 in einem Live-Lauf gegen echtes Ollama, echter Artikeltext erfolgreich extrahiert; der eine Fehlschlag gab ehrlich zu, den Volltext nicht zu kennen, statt zu erfinden). NICHT gepusht — auf Anweisung bis auf Weiteres nur lokal. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
36 lines
1.3 KiB
Python
36 lines
1.3 KiB
Python
"""Fetch a web page and extract its clean article text. Knows nothing about
|
|
the voice pipeline or where the URL came from.
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
import httpx
|
|
import trafilatura
|
|
|
|
USER_AGENT = "Mozilla/5.0 (compatible; AstraVoice/1.0; +https://github.com/Jeuners/astra-vision)"
|
|
|
|
|
|
class ArticleError(Exception):
|
|
"""Raised when a page can't be fetched or no article text could be extracted."""
|
|
|
|
|
|
async def read_article(url: str, *, max_chars: int = 4000) -> str:
|
|
"""Fetch `url` and return its main article text, stripped of navigation,
|
|
ads, and other boilerplate. Extraction runs in a thread since trafilatura
|
|
is CPU-bound and synchronous.
|
|
"""
|
|
async with httpx.AsyncClient(
|
|
headers={"User-Agent": USER_AGENT}, timeout=15, follow_redirects=True
|
|
) as client:
|
|
try:
|
|
response = await client.get(url)
|
|
response.raise_for_status()
|
|
except httpx.HTTPError as exc:
|
|
raise ArticleError(f"Seite nicht erreichbar: {exc}") from exc
|
|
|
|
text = await asyncio.to_thread(
|
|
trafilatura.extract, response.text, include_comments=False, include_tables=False
|
|
)
|
|
if not text or not text.strip():
|
|
raise ArticleError("Auf der Seite wurde kein lesbarer Artikeltext gefunden.")
|
|
return text.strip()[:max_chars]
|