Files
spiele-redaktion/tests/_neuheiten.py
Flo Hartmann f27940db8e Plugin neuheiten: optionale LLM-Hilfsstufe für den Web-Quellen-Crawler
Neues Modul quellen/ki_hilfe.py (konsistent zum dedup-LLM-Muster):
- Struktur-Erkennung: bei verdächtig leerem Parser-Ergebnis schlägt das
  LLM Pagination-/Filter-Folge-URLs vor; hart gefiltert auf gleiche
  Domain, max. 20 je Seite, nicht erreichbare Vorschläge brechen den
  Lauf nicht ab.
- Feld-Extraktion: nur unsichere Treffer (fehlender Verlag, verdächtiger
  Titel); korrigiert ausschließlich titel/verlag/autor, niemals die URL.
- Env-Konfiguration SPIELE_NEUHEITEN_KI_* (AKTIV default 0,
  KONFIDENZ_MIN default 0.7), OpenAI-kompatible Chat-Completions via
  httpx mit Timeout und genau einem Retry.
- Fallback-Pflicht: ohne Konfiguration oder bei jedem Fehler läuft exakt
  der klassische Crawler; KI-Fehler blockieren den Sync nie.
- Audit: KI-Eingriffe je Quelle und Lauf ins audit-log (Modell,
  Konfidenz, korrigierte Felder, verfolgte Folge-URLs).

20 neue Tests (gemocktes HTTP/LLM): Fallback-Fälle, Konfidenz-Schwelle,
Domain-Filter, Max-20-Grenze, URL-Unveränderbarkeit, Audit.
2026-08-23 01:07:18 +00:00

90 lines
3.3 KiB
Python

"""Zugriff auf die neuheiten-Plugin-Module über den vom Plugin-Loader
verwendeten Modulnamen (`spiele_redaktion_plugins.neuheiten`).
Der Kern teilt sich eine SQLAlchemy-MetaData; deshalb darf es pro Prozess
nur eine Modul-Identität geben. Tests importieren das Plugin daher über
diesen Helper statt direkt über `plugins.neuheiten`.
"""
from __future__ import annotations
import sys
from pathlib import Path
from redaktionskern.plugin_loader import discover_plugins
_PROJEKT_WURZEL = Path(__file__).resolve().parents[1]
if "spiele_redaktion_plugins.neuheiten" not in sys.modules:
discover_plugins(
_PROJEKT_WURZEL / "plugins",
entry_point_group="spiele_redaktion.tests.keine_entry_points",
)
_neuheiten = sys.modules["spiele_redaktion_plugins.neuheiten"]
bgg = _neuheiten.bgg
sync_modul = _neuheiten.sync
models = _neuheiten.models
BggClient = bgg.BggClient
BggFehler = bgg.BggFehler
BggAuthFehler = bgg.BggAuthFehler
MELDUNG_401 = bgg.MELDUNG_401
SuchTreffer = bgg.SuchTreffer
BggSpiel = bgg.BggSpiel
parse_suche = bgg.parse_suche
parse_things = bgg.parse_things
SyncService = sync_modul.SyncService
SyncErgebnis = sync_modul.SyncErgebnis
ist_moeglicher_prototyp = sync_modul.ist_moeglicher_prototyp
speichere_oder_aktualisiere = sync_modul.speichere_oder_aktualisiere
Neuheit = models.Neuheit
STATUS_NEUHEIT = models.STATUS_NEUHEIT
# ---------- Web-Quellen (Adapter + Sync-Orchestrierung) ----------
quellen_basis = _neuheiten.quellen.basis
quellen_sync_modul = _neuheiten.quellen_sync
models = _neuheiten.models # neu mit QuellenStatus
USER_AGENT = quellen_basis.USER_AGENT
QuellenAdapter = quellen_basis.QuellenAdapter
QuellenFehler = quellen_basis.QuellenFehler
QuellenTreffer = quellen_basis.QuellenTreffer
SammelErgebnis = quellen_basis.SammelErgebnis
WebQuellenClient = quellen_basis.WebQuellenClient
jahr_aus_datumsangabe = quellen_basis.jahr_aus_datumsangabe
titel_aehnlichkeit = quellen_basis.titel_aehnlichkeit
titel_normalisieren = quellen_basis.titel_normalisieren
ki_hilfe_modul = _neuheiten.quellen.ki_hilfe
KiHilfe = ki_hilfe_modul.KiHilfe
KiKonfiguration = ki_hilfe_modul.KiKonfiguration
lade_ki_konfiguration = ki_hilfe_modul.lade_konfiguration
extrahiere_ki_json = ki_hilfe_modul.extrahiere_json
struktur_verdaechtig = ki_hilfe_modul.struktur_verdaechtig
treffer_ist_unsicher = ki_hilfe_modul.treffer_ist_unsicher
titel_ist_verdaechtig = ki_hilfe_modul.titel_ist_verdaechtig
validiere_url_vorschlaege = ki_hilfe_modul.validiere_url_vorschlaege
validiere_feld_antwort = ki_hilfe_modul.validiere_feld_antwort
MAX_FOLGE_URLS = ki_hilfe_modul.MAX_FOLGE_URLS
SpielboxQuelle = _neuheiten.quellen.spielbox.SpielboxQuelle
BrettspielboxQuelle = _neuheiten.quellen.brettspielbox.BrettspielboxQuelle
SpielEssenQuelle = _neuheiten.quellen.spielessen.SpielEssenQuelle
CliquenabendQuelle = _neuheiten.quellen.cliquenabend.CliquenabendQuelle
ADAPTER_KLASSEN = quellen_sync_modul.ADAPTER_KLASSEN
WebQuellenSyncService = quellen_sync_modul.WebQuellenSyncService
QuellenLaufZeile = quellen_sync_modul.QuellenLaufZeile
quelle_aktiv = quellen_sync_modul.quelle_aktiv
TITEL_FUZZY_SCHWELLE = quellen_sync_modul.TITEL_FUZZY_SCHWELLE
QuellenStatus = models.QuellenStatus
def sys_modules_plugin():
"""Die geladene NeuheitenPlugin-Instanz (für Migrationen im Test)."""
return sys.modules["spiele_redaktion_plugins.neuheiten"].plugin