[Backend] Keep self-hosted LLM first-class, including a CPU-only path #353
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in the August 2026 session lifecycle review (#319).
Why
Self-hosting must remain viable without a GPU. That is a real constraint, not a courtesy: a CPU-only deployment has a much smaller practical context window and much slower generation, and the pipeline has to be correct there, not merely runnable.
This is the strongest argument for the architecture chosen in v4.0.0. Because chronology is guaranteed by a code-side
sorted()and attribution is checked by a code-side validator, the accuracy floor does not depend on model size. A small CPU-hosted model loses more beats to validation; it does not silently produce a confidently wrong summary. That property is what makes a CPU tier honest.Proposed fix
Make the CPU path a supported, tested configuration: llama.cpp on CPU with a small model, GBNF grammar constraint for the beat schema, window sized from the declared context, and generous timeouts throughout the Celery chain so a slow local run does not read as a failure.
Measure it against the golden corpus and publish the numbers — including the beat validation rate, which is the honest quality signal — so a self-hoster can make an informed choice rather than discovering the trade-off in production.
Acceptance criteria
Landed in PR #500 (merged 2026-09-05, CI green; 2146 backend / 497 frontend tests after the final rebase).
docs/examples/docker-compose.cpu-llm.yml(llama.cpp server, Qwen2.5-3B-Instruct Q4_K_M, 8k window) plus the Bot Settings values, and the "Hardware profiles" section in OPERATIONS with the exact commands.test_beat_schema_grammar.py: the realextract_beatssends the closed schema under both llama.cpp spellings withtemperature: 0and thinking disabled; nothing was dropping it.QB_LLM_TIMEOUT_SECONDS,QB_LLM_PROSE_TIMEOUT_SECONDS,QB_ASR_TIMEOUT_SECONDSandQB_AI_PROFILE=cpu(×6 on the defaults), with the Celery task limits derived from them so a task limit can never be shorter than the calls it contains; the admin test button and the monthly canary scale too.autofollows the last preflight, but Celery limits are read at worker import, so a CPU deployment should pincpu— stated in code,.env.example, docs and the panel.Stated weakness: the private hand-verified session is not present on this machine or the dev box, so both runs used the four synthetic five-minute fixtures — neither exercised windowing. Re-running against a real session is the next sharpening step.