homelab-codex-ws/jobs/documents-ingest/pyproject.toml

30 lines
694 B
TOML
Raw Normal View History

[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "documents-ingest"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"asyncpg>=0.29",
"structlog>=24.1",
"aiohttp>=3.9",
"kb-mail",
feat(kb): faza 3 krok 2 — migracja 004 (document_summary) + pilot streszczeń A/B Migracja 004: document_summary (envelope_id, summary, tags JSONB, model, embedding VECTOR(1024) + HNSW cosine, embedding_model, UNIQUE(envelope_id, model) od razu — wzorzec 002/003). Zastosowana na żywej bazie kb-postgres@PIHA. Job documents-ingest-summarize: wejście = document_chunk.text WHERE excluded_reason IS NULL per koperta source='paperless' (duplikaty przez entities[duplicate_of] pomijane w całości), wymuszony JSON {summary, tags} przez --backend ollama|anthropic, słownik tagów kontrolowany (tags-vocab.yaml) z max 3 free-form, map-reduce dla dokumentów >200k znaków (grupy ~20 chunków), bilans + idempotencja + izolacja błędów per wiersz wg wzorców rodziny jobów. Osobny --embed-summaries (bge-m3, reużywa chunk_embed.embed_chunk). Bug znaleziony i naprawiony w trakcie pilota: brak options.num_ctx w wywołaniach Ollamy powodował, że gemma3:12b używał domyślnego runtime kontekstu (~2048 tok), nie zadeklarowanego 128k — dla 71/157 dokumentów (45%, >8k znaków) treść była cicho ucinana (zweryfikowane: prompt_eval_count=2051 dla dokumentu 93k znaków). Naprawa: compute_num_ctx() liczy num_ctx z długości promptu (~3 znaki/token, cap 131072). Cały tor lokalny przeliczony od zera po naprawie. Wynik pilota (186 dok. paperless, 3 duplikaty, 26 bez aktywnych chunków → 157 oczekiwanych na tor): tor lokalny (gemma3:12b) 155/157 (2 izolowane błędy JSON po retry: paperless:24, paperless:61), tor referencyjny (claude-haiku-4-5) 157/157, 0 błędów JSON, 0 tagów ucinanych — słownik przestrzegany w 100% przypadków. Oba komplety zembedowane (bge-m3). Znaleziony przy okazji: prompt do tagów wymagał dopracowania — pierwsza wersja pozwalała modelowi zwracać tagi po angielsku spoza słownika; wzmocniona instrukcja (słownik w pierwszej kolejności, "nigdy po angielsku") poprawiła zgodność w 2/3 przypadków testowych. Porównanie A/B (~15 dok.) i weryfikacja końcowa (bilans, sanity SQL, retrieval po summary) odłożone do następnej sesji. Testy: 157 (152 nowe/summarize.py + istniejące), mocki API/Ollama/DB, bilans, idempotencja, regresja num_ctx. Co najmniej 3 decyzje wymagały zatrzymania i potwierdzenia z Oskarem w sesji (sposób podania klucza API, wybór modelu lokalnego gemma3:12b, naprawa+przeliczenie całego toru lokalnego po odkryciu buga num_ctx) — udokumentowane w transkrypcie sesji. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 21:59:21 +02:00
"PyYAML>=6.0",
"anthropic>=0.40",
]
[project.scripts]
documents-ingest = "documents_ingest.extractor:main"
documents-ingest-paperless = "documents_ingest.paperless_adapter:main"
feat(documents-ingest): chunk + embed job (module 5 phase 2 step 6) Adds documents-ingest-embed: chunks source='paperless' envelope content (paragraph-preferring, ~600 tok/chunk, ~150 tok overlap, hard char-fallback for oversized paragraphs per plan §2 decision 3), embeds each chunk via Ollama (bge-m3, dim validated against document_chunk's VECTOR(1024) on every response) and inserts into document_chunk. Lives in documents-ingest per the plan's own recommendation (§6 step 6) rather than a new package — reuses the job family's existing idempotency/stats-balance/dry-run conventions (paperless_adapter.py, gmail-header-backfill). A 5-angle multi-agent code review of the initial implementation surfaced three real bugs, fixed here: hard_split() could infinite-loop if --chunk-overlap >= --chunk-size (now guarded in both hard_split() and main()); insert_chunk() wasn't error-isolated like embed_chunk(), so a DB write failure would crash the whole run instead of being counted and skipped; and ON CONFLICT DO NOTHING's outcome was discarded, so a silently skipped row (the known gap where document_chunk's UNIQUE constraint doesn't include `model`) would have been miscounted as a successful insert - now tracked separately as chunks_conflict_skipped and treated as a run failure. Smoke-tested and run to completion live on SOLARIA against the real Ollama instance and kb-postgres@PIHA: dry-run matched the known phase-2-step-5 figures exactly (186 fetched, 26 empty_content, 2684 chunks planned), a --limit 10 apply + idempotent re-run + DB/distance sanity checks all passed, and the full 186-document run inserted 2683/2684 chunks (1 isolated error - Ollama's runtime context window rejected one pathological dot-leader table-of-contents chunk that tokenized far more densely than estimated; documented as a known limitation, not fixed here given it's a single-chunk edge case). Timing: ~0.79s/chunk average on CPU (SOLARIA's Ollama runs GPU-less per the recent GPU-reservation-disabled fix), ~35 min wall-clock for the full pilot - the real input for scoping the later mail-corpus embedding phase (plan §7's GPU-based estimate doesn't hold here). pytest: 101 passed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 20:41:00 +02:00
documents-ingest-embed = "documents_ingest.chunk_embed:main"
feat(kb): faza 3 krok 2 — migracja 004 (document_summary) + pilot streszczeń A/B Migracja 004: document_summary (envelope_id, summary, tags JSONB, model, embedding VECTOR(1024) + HNSW cosine, embedding_model, UNIQUE(envelope_id, model) od razu — wzorzec 002/003). Zastosowana na żywej bazie kb-postgres@PIHA. Job documents-ingest-summarize: wejście = document_chunk.text WHERE excluded_reason IS NULL per koperta source='paperless' (duplikaty przez entities[duplicate_of] pomijane w całości), wymuszony JSON {summary, tags} przez --backend ollama|anthropic, słownik tagów kontrolowany (tags-vocab.yaml) z max 3 free-form, map-reduce dla dokumentów >200k znaków (grupy ~20 chunków), bilans + idempotencja + izolacja błędów per wiersz wg wzorców rodziny jobów. Osobny --embed-summaries (bge-m3, reużywa chunk_embed.embed_chunk). Bug znaleziony i naprawiony w trakcie pilota: brak options.num_ctx w wywołaniach Ollamy powodował, że gemma3:12b używał domyślnego runtime kontekstu (~2048 tok), nie zadeklarowanego 128k — dla 71/157 dokumentów (45%, >8k znaków) treść była cicho ucinana (zweryfikowane: prompt_eval_count=2051 dla dokumentu 93k znaków). Naprawa: compute_num_ctx() liczy num_ctx z długości promptu (~3 znaki/token, cap 131072). Cały tor lokalny przeliczony od zera po naprawie. Wynik pilota (186 dok. paperless, 3 duplikaty, 26 bez aktywnych chunków → 157 oczekiwanych na tor): tor lokalny (gemma3:12b) 155/157 (2 izolowane błędy JSON po retry: paperless:24, paperless:61), tor referencyjny (claude-haiku-4-5) 157/157, 0 błędów JSON, 0 tagów ucinanych — słownik przestrzegany w 100% przypadków. Oba komplety zembedowane (bge-m3). Znaleziony przy okazji: prompt do tagów wymagał dopracowania — pierwsza wersja pozwalała modelowi zwracać tagi po angielsku spoza słownika; wzmocniona instrukcja (słownik w pierwszej kolejności, "nigdy po angielsku") poprawiła zgodność w 2/3 przypadków testowych. Porównanie A/B (~15 dok.) i weryfikacja końcowa (bilans, sanity SQL, retrieval po summary) odłożone do następnej sesji. Testy: 157 (152 nowe/summarize.py + istniejące), mocki API/Ollama/DB, bilans, idempotencja, regresja num_ctx. Co najmniej 3 decyzje wymagały zatrzymania i potwierdzenia z Oskarem w sesji (sposób podania klucza API, wybór modelu lokalnego gemma3:12b, naprawa+przeliczenie całego toru lokalnego po odkryciu buga num_ctx) — udokumentowane w transkrypcie sesji. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 21:59:21 +02:00
documents-ingest-summarize = "documents_ingest.summarize:main"
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]