Convert the ebook-search web app to async end to end and add concurrency
to the protected-phrase extraction and judging pipeline so large books no
longer block the event loop or the UI.
ORM / infra:
- Add get_async_postgres_engine and factor shared URL/connect_args building
into build_postgres_url (reused by the sync and async engine builders)
- Add async FastAPI session helpers (get_async_db, AsyncDbSession) with
expire_on_commit=False to avoid implicit IO under asyncio
App:
- Use AsyncEngine/AsyncSession throughout routes, search, ingest, embeddings,
answer, rerank and LLM calls; convert handlers to async
- Share a single httpx.AsyncClient in app state for LLM requests; size the
connection pool for concurrent phrase-judging workers
- Add judge_tasks: run per-book judging as tracked background tasks so a
book already being judged isn't double-queued
Protected phrases:
- Add a process pool (pool.py) and worker-count config
(extraction/judge book/phrase workers) to parallelize candidate generation
and judging
- Split admin actions into all/missing variants for generation and judging
Config:
- Add protected_phrase_extraction_workers, phrase_judge_book_workers,
phrase_judge_phrase_workers
Introduce four ORM models and their Alembic migration to support
phrase-based query matching in the ebook RAG engine:
- EbookCandidatePhrase: high-recall phrase candidates extracted per book,
with source flags (ngram/yake/spacy/capitalized/metadata), scoring, and
LLM judge results.
- EbookProtectedPhrase: phrases accepted by the LLM judge, with canonical
id, importance, and nesting controls.
- EbookPhraseAlias: normalized aliases mapping to protected phrases.
- EbookChunkPhraseMention: precomputed phrase occurrences within chunks.
Export the new models from python.orm.richie and add a JSON_DOCUMENT
helper (JSON with JSONB postgres variant) for storing sample contexts.
Add a pipeline to load HAProxy `option httplog` lines into the Richie
database so bot/crawler traffic can be analyzed.
- model: HaproxyRequest mirroring the httplog format, with a unique
line_hash dedup key and indexes on common filter columns
- migration: create the haproxy_request table (unique line_hash + indexes)
- haproxy_logs package:
- parser: httplog line -> columns, strips the journald prefix and
hashes the normalized line
- ingest: batched, idempotent insert that skips rows whose line_hash
already exists, so re-ingesting the same logs is a no-op
- cli: ingest-only `haproxy-logs` command reading stdin or a file
- tests: parsing of a real GPTBot line and idempotent re-ingestion