Loading…
Reference in a new issue
No description provided.
Delete branch "feat/353-354-self-hosted-profiles"
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?
Closes #353. Closes #354.
Self-hosting without a GPU is now a supported, measured configuration rather than a hope, and a self-hoster can find out what their hardware will do before the first three-hour session rather than after it.
What
Timeouts are configuration (
app/config.py, one accessorai_timeouts()):QB_LLM_TIMEOUT_SECONDS,QB_LLM_PROSE_TIMEOUT_SECONDS,QB_ASR_TIMEOUT_SECONDS, andQB_AI_PROFILE∈ {auto,gpu,cpu}.cpumultiplies the defaults by 6; an explicitly set value is used as written. Celery task limits derive from the timeouts (task_limits(): ongputhis reproduces #398's 6 h exactly; the invariant is that a task limit can never be shorter than the calls it contains). The admin test button and the monthly canary scale too — both could only ever report a working CPU endpoint as broken.autofollows the last preflight; the Celery limits are read at worker import, so a CPU deployment should pincpu(stated in code,.env.example, docs and the panel). Two seams fixed on the way: #356'scall_lease_ttlwas handed base constants at three sites (a lease would have expired mid-call on the cpu profile), and #350's adapter had its own hardcoded 600 s.Grammar-constrained beats on llama.cpp, proven on the wire:
test_beat_schema_grammar.pydrives the realextract_beatsthrough the conformance fake and asserts the closed schema under both spellings,temperature: 0, thinking disabled. Nothing was dropping it.Admin preflight —
POST /api/admin/ai/preflight(stored underai_preflight,GETreads it): ASR/healthreachability and declared capabilities, real-time factor pooled from the last 10session_usagerows or "not yet measured" (never a synthetic benchmark); one fixed 128-token LLM probe → tokens/s; the declared window and chunk count; the capability gaps; a throughput class by band and an expected processing time for a 3.5 h / 5-speaker session (prefill charged at 10× generation — measured ratios were 31×/11×/2.8×; validated to within ~30 % on three configurations).AdminHardwareProfilepanel with a Run button.Bands, refined from measurement:
< 10/10–40/> 40tok/s, named by throughput not silicon — a GPU-less 9800X3D measures 15.6 and does not need the long timeouts, so "CPU-class" would have been wrong in both directions; only the bottom band maps to thecputimeouts. Lower bound raised from 8 because one beat extraction on the CPU recipe (~5.7k prompt + ~0.8k completion tokens) crowds the 300 s default at 10 tok/s.Measured (
python -m evals --provider live --record, four synthetic fixtures, one run each,history.jsonltagged47c31d7-353-{gpu,cpu}):Only coverage degrades on the small model: chronology and attribution come from code-side sort and validator, and beat validation holds because the schema is a decoding grammar. The CPU summary is shorter, not wronger — the issue's thesis, measured. A generic QEMU vCPU without AVX2 (the dev box) measured 0.3 tok/s and is documented as the floor no profile rescues.
Docs: OPERATIONS "Hardware profiles" — three recipes (CPU-only, modest GPU, workstation GPU) with model, hardware, timing and scores; the modest-GPU row honestly empty with the hardware and command that fills it;
docs/examples/docker-compose.cpu-llm.ymlas the runnable CPU-only configuration; the commands that produced every number.Weakness stated: 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, and the coverage figures describe a short transcript. ASR real-time factor is not yet measured anywhere (no
session_usagerows exist yet).Rebased over #350 and #356 (config and import conflicts, both sides kept).
Verification
🤖 Generated with Claude Code
93caca72e70981e3fe57