Commit graph

2 commits

Author SHA1 Message Date
oskar b1b6692382 feat(kb-query): domyslny mode /search = hybrid (DoD (d) fazy mailowej)
Eval na pelnym korpusie 2026-08-06 (187 025 zembedowanych chunkow mailowych
w HNSW) dal PASS: kryterium 1 (regresja paperless) bez degradacji zadnego
istniejacego hitu we flat ani w hybrid, mailowe hit@3 5/5. Koszt hybrydy to
jedno dodatkowe zapytanie SQL na wyszukiwanie. Surowe wyniki:
eval-http-2026-08-06.json / eval-direct-2026-08-06.json w ~/kb/mail/ingest-logs
na PIHA (niecommitowane, artefakt runu).

- app/main.py: Query("cascade") -> Query("hybrid"); pattern bez zmian, wiec
  jawne ?mode=cascade i ?mode=flat dzialaja dokladnie jak dotad.
- app/static/app.js: przy odznaczonym "tryb flat (debug)" UI nie wysyla juz
  parametru mode w ogole -- dziedziczy default API. Default zdefiniowany
  w jednym miejscu (serwer), nie zduplikowany w JS.
- testy: nowa klasa TestSearchEndpointModeDefault (TestClient bez lifespan,
  fake pool/router) sprawdza kontrakt HTTP -- brak mode => tor hybrid
  (weryfikowany po obecnosci koperty gmail osiagalnej wylacznie galezia
  hybrid, nie po samej etykiecie), jawne mode=flat / mode=cascade => stare
  tory, nieznany mode => 422. Frontend: buildSearchUrl pomija mode gdy brak.
- docs: kb/services/kb-query.md (tabela trybow + endpoint + przyklad
  odpowiedzi + opis przelacznika w UI), env.example/service.yaml (komentarze
  SUMMARY_MODEL; default mode nie jest konfigurowalny przez env),
  kb/phases/kb-m5-faza-mailowa.md (DoD (d) SPELNIONE 2026-08-06 + wzmianki
  w Kroku 3, Wyniku bramki, decyzjach Etapu B i tabeli planu).

Weryfikacja: pytest services/kb-query -> 46 passed; node --test
tests/frontend/app.test.js -> 6/6; docker build OK + smoke run (uvicorn
startuje, bez KB_DSN swiadomie konczy sie RuntimeError z env.example).
Deploy NIE wykonany -- operator wdraza z mastera na PIHA po mergu.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 13:30:06 +02:00
oskar a73a986bbe feat(kb-query): add search frontend (module 5 phase 4, plan §7, Krok 4)
Krok 4 of the phase-4 plan done ahead of the local-embed-fallback step
(Krok 2, deliberately deferred -- embed stays a plain SOLARIA call, per
task instruction): one FastAPI process now serves both the /search API
and the UI, no separate frontend build (plan §2 decision 4).

- GET / renders a Jinja2 shell; app/static/app.js (vanilla, no build) and
  style.css are the whole client. Query -> /search, results grouped by
  envelope_id client-side (chunks sorted by dist, <details> fragments).
- Colour thresholds per plan §7: dist<0.45 green, 0.45-0.55 yellow (still
  shown with a warning), >0.55 never rendered as an individual result; if
  a query ends up with nothing renderable, one "Brak odpowiedzi w KB"
  message replaces the list, carrying the best observed dist.
- Paperless hits link out; gmail hits get a "kopiuj Message-ID" button
  (there's nothing to link to yet, plan §2 decision 3) plus header
  metadata. Cascade/flat toggle defaults to cascade. Footer shows
  sol_status, refreshed from /healthz on load and after each search.
- /search gained additive summary/summary_tags fields (document_summary,
  haiku track) so the UI can show a document summary as each result
  group's header -- non-breaking, existing response shape untouched.
- Tests: app/db.py + app/search.py unit tests (mocked DB/HTTP, no live
  deps) cover the new summary join; tests/test_frontend.py drives GET /
  and /static/* via TestClient without running the DB-requiring lifespan;
  tests/frontend/app.test.js (Node's built-in test runner, no framework)
  covers query-URL encoding, threshold colouring, and envelope grouping.
- Verified live: docker build + container against kb-postgres@PIHA over
  LAN and Ollama@SOLARIA over Tailscale -- GET / (HTML), /static/app.js,
  /healthz, and /search (cascade + flat) all round-tripped correctly,
  including real summary/summary_tags data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 16:49:36 +02:00