Commit graph

8 commits

Author SHA1 Message Date
Jeuner
4b833f3785 Branding: App heißt MABOTO (Marktbeobachtungstool) + Icon/Favicon
- Eigenes Icon: Lupe über aufsteigenden Preis-Balken (Beobachtung + Markt)
  in Markengrün, als skalierbares SVG. Favicon via /favicon.svg-Route
  (image/svg+xml, gecached) + Inline-Motiv im App-Bar-Logo.
- App-Bar-Wortmarke „MABOTO / Marktbeobachtung", Seitentitel, Intro-Kopf
  und FastAPI-App-Titel umbenannt; README-H1 + Doku-Screenshots neu.
- Tests: Index prüft jetzt auf MABOTO, neuer favicon.svg-Test (77 grün).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 23:09:32 +02:00
Jeuner
e1c7afef7e Ergebnisansicht: Sidebar + Header, Kategorie-Chip, manuelle Korrektur
- Layout: Grid sidebar|main. Linke Seitenleiste mit den Produktgruppen
  (Counts + unsicher-Badges, Klick springt zur Gruppe), sticky Header (Ort,
  Anzahl, Cache-Statistik, Modell, unsicher, Filter).
- Kategorie pro Angebot als Chip sichtbar; Chip ist zugleich Korrektur-Anker.
- POST /api/korrektur {titel,marke,gruppe,plz}: schreibt die manuelle Gruppe
  (modell='manuell') in den Produkt-Cache -- die hochwertigste Cache-Quelle;
  patcht den UI-Ergebnis-Cache der PLZ (Angebot wandert, unsicher-Flag weg).
  Kein LLM, kein Fetch; Whitelist erzwungen. Frontend hängt das Angebot
  client-seitig um, ohne neuen Lauf.
- +6 Tests (gültig/400/400, modell=manuell, manuelle Zuordnung -> Cache-Hit
  kein LLM, Ergebnis-Cache-Patch). 76 Tests grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 19:42:17 +02:00
Jeuner
077a877480 Produkt->Kategorie-Cache: bekannte Produkte ohne LLM (SQLite, modellübergreifend)
Neuer produktcache.py (Stufe 2): speichert pro Produkt (Titel+Marke, mengen-
invariant) die einmal ermittelte Gruppe in SQLite, bulk-load ins dict -> O(1).
Schnitt gewahrt: kein LLM-Import, nur Gruppe (nie Angebotsdaten), Whitelist
beim Lesen+Schreiben, nur SICHERE Zuordnungen gecacht.

kategorisiere(cache=, statistik=): Lookup vor dem LLM, Dedup im Lauf (ein
Produkt = ein Posten), Write-Back danach. Parallel-/id-Logik unverändert.
als_struktur/web/cli verdrahtet (Statistik 'X aus Cache · Y neu', --no-cache).

Live verifiziert (1903 Angebote PLZ 60487): Lauf 1 (gemini) 1551 neu; Lauf 2
(deepseek, anderes Modell) nur 110 neu, 1765 aus Cache -> ~93% weniger LLM-Calls,
modellübergreifend. +12 Tests (Round-Trip, Whitelist, Hit-vermeidet-Call, Dedup,
nur-sichere, Schnitt). 70 Tests grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 18:37:12 +02:00
Jeuner
2029eb9fcf Kategorisierung parallelisieren (bis zu 8 Batches gleichzeitig)
Die 77 LLM-Calls liefen bisher sequenziell -> bei langsamer Modell-Latenz
minutenlang. Jetzt ThreadPoolExecutor (parallel=8); id-basiertes Mapping ist
reihenfolge-unabhängig, Logik unverändert. Voller deepseek-Lauf: 162s statt
sequenziell ~20min bei der heutigen Latenz (~16s/Call). Schnelle Modelle
(gemini-flash) entsprechend ~15-20s. +1 Test (parallel ordnet alle Batches
vollständig zu). 58 Tests grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 18:15:14 +02:00
Jeuner
2ffb89a6d2 Ollama-Konfig: Persistenz (localStorage) + E2E-Test, ehrliche Modell-Grenze
Goal 'Ollama-Konfig bleibt bestehen & klappt', mit Tests:
- Persistenz-Fix: Anbieter + Modell in localStorage gemerkt, init() stellt sie
  wieder her (URL-Param > gemerkt > Default). Behebt das Zurückspringen auf
  OpenRouter nach Reload.
- E2E-Test (Playwright): Anbieter überlebt echten Reload. content-JSON-Fallback
  mit 3 Tests abgesichert. 57 Tests grün.
- Ehrlich dokumentiert (Code-Untersuchung + UI-Hinweis): kleine lokale Modelle
  (qwen2.5-coder, gemma4, qwen3.5, llama3.2) liefern kein brauchbares Batch-
  Tool-Calling -> Ergebnis 'Sonstiges/unsicher' (markiert, nicht geraten).
  Brauchbare lokale Kategorisierung braucht ein starkes tool-Modell; Cloud
  (deepseek) bleibt die verlässliche Wahl.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:34:34 +02:00
Jeuner
2e80f0d826 Ollama als lokaler Anbieter + gewähltes LLM dauerhaft sichtbar
- OllamaKategorisierer (lokaler OpenAI-kompatibler Endpoint, kein Key/Netz),
  baue_kategorisierer('ollama'), Default-Ollama-Modell.
- modelle.lade_ollama_modelle: /api/tags + /api/show (Tool-Fähigkeit), nur
  tool-fähige taugen; leere Liste wenn Ollama aus.
- web: /api/ollama-modelle, Anbieter im Kategorisier-Flow + Cache-Key,
  Modell+Anbieter im Ergebnis (als_struktur).
- UI: Anbieter-Umschalter (OpenRouter/Ollama), gewähltes Modell als Chip im
  Konfig-Kopf (auch zugeklappt) + 'kategorisiert mit … (anbieter)' im Ergebnis,
  bookmarkbarer ?modell/?anbieter/?auto-Start.
- content-JSON-Fallback fürs Tool-Parsing (manche lokale Modelle liefern die
  Antwort als Text-JSON). +6 Tests (53 gesamt).

Ehrlich: lokal installierte Modelle (qwen2.5-coder/gemma4/qwen3.5) liefern kein
brauchbares Tool-Calling -> Ergebnis dort 'Sonstiges/unsicher' (ehrlich markiert,
nicht geraten). Cloud-Default deepseek-v4-flash voll verifiziert (1903 Angebote,
modellstabil).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:54:32 +02:00
Jeuner
11f1444599 Web-UI: zweistufiger Flow (Rohdaten holen+speichern / Kategorisieren)
- Stufe 1 (/api/rohdaten): deterministischer Fetch + Persistenz pro PLZ/Woche
  in data/roh/, ohne LLM/Key. speicher.py serialisiert belegte Angebote
  verlustfrei (fehlende Felder bleiben null).
- OpenRouter-Konfig als separates Panel (gilt für Stufe 2).
- Stufe 2 (/api/kategorisieren): LLM-Schritt auf den GESPEICHERTEN Rohdaten,
  gesperrt solange keine vorliegen (400). Fetcht nicht erneut.
- Funktionales Premium-Redesign: zwei nummerierte Stufen-Karten mit Status-
  Flags, erzwungene Reihenfolge, belegte Rohliste, ehrlicher Footer.
- 47 Tests (+11: speicher Round-Trip, Endpoint-Sperre, Rohdaten offline).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:44:14 +02:00
Jeuner
39b8a98bc2 Initiale Implementierung: Angebots-Übersicht (Fetch + Kategorisierung + Web-UI)
Der Schnitt aus CLAUDE.md ist durchgehalten:
- Fetch (deterministisch, kein LLM): marktguru-Adapter mit geprüftem
  Ortsbezug (zipCode), Wochen-Cache, robots.txt-Respekt, ehrlicher Regel-4-
  Abbruch bei fehlendem Beleg statt Krücke.
- Kategorisierung (einziger LLM-Ort): geschlossene Liste + Daten-Integrität
  als Code erzwungen; austauschbar via Protokoll (OpenRouter/Anthropic),
  mit Drosselung/Retry und ehrlichem Abbruch.
- FastAPI-Web-UI als dünne Schicht: Modellauswahl (Liste/Suche/Refresh),
  Live-Fortschritt, gruppierte Ergebnisse mit Filtern, Ergebnis-Cache.
- 36 Tests gegen die Architektur-Regeln (kein Auffüllen, Abbruch, Integrität,
  geschlossene Liste, Unsicherheit, Schnitt) und die Web-Schicht.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:29:59 +02:00