-
v4.2.0
StableAll checks were successfulCI / Backend lint (ruff) (push) Successful in 27sCI / Bot/backend version sync (push) Successful in 22sRelease / Create Forgejo release (push) Successful in 12sCI / Bot tests and audit (push) Successful in 1m26sRelease / Build and push versioned images (push) Successful in 46sCI / Summarisation accuracy eval harness (stub provider) (push) Successful in 2m1sCI / Frontend tests, audit, and build (push) Successful in 2m42sCI / Docker image build (push) Successful in 4m24sCI / Backend migration, tests, and audit (push) Successful in 9m55sCI / Synthetic session harness (no GPU, no LLM) (push) Successful in 14m20sreleased this
2026-09-05 06:22:55 +00:00 | 210 commits to main since this releaseAI Provider Abstraction & BYO-AI. No commitment to any single transcription or
language-model provider: a normalised contract with swappable adapters, a
campaign can bring its own providers, self-hosting is a first-class, measured
target on both GPU and CPU, and the pipeline tells you what your configuration
cannot do instead of quietly working around it.⚠️ Five migrations —
a5b6c7d8e9fa(provenance on summary runs),
b6c7d8e9fa0b(per-session usage and cost),c7d8e9fa0b1c(per-word timings
on transcript segments),d8e9fa0b1c2d(per-campaign AI settings) and
e9fa0b1c2d3e(resolved provider identity on usage rows). All are additive —
new tables or nullable columns, nothing rewritten, nothing backfilled — and
each was verified to apply, roll back and re-apply cleanly. For a minor
upgrade this size take a snapshot first:make migrate-safedoes the
preflight, backup and migrate in one step.The bot contract is unchanged (still v1) and nothing under
/api/bot/*
changed shape, so the backend and bot images can be upgraded independently in
either order.⚠️ If you run the bundled transcription server, update it. The word
timings and the campaign-vocabulary hint in this release need its API v2;
against an older server Quest Board keeps working exactly as before and the
new "What your AI configuration can and cannot do" panel tells you to update.
The server lives outside this repository; see OPERATIONS → "Upgrading the
bundled transcription server".Six new optional
.envsettings, all with safe defaults:
QB_LLM_CONCURRENCY_LIMIT,QB_ASR_CONCURRENCY_LIMIT,QB_AI_PROFILE,
QB_LLM_TIMEOUT_SECONDS,QB_LLM_PROSE_TIMEOUT_SECONDS,
QB_ASR_TIMEOUT_SECONDS. Leave them unset unless OPERATIONS tells you
otherwise; a CPU-only deployment should pinQB_AI_PROFILE=cpu.The repository now carries ~9 MB of invented audio — a committed
synthetic 30-minute session that exercises transcription end to end in CI.
No real voices, no real content; seeCLAUDE.mdfor the carve-out.Added
-
[webapp] Each group can now bring its own AI provider, and pay its own
bill (#128). One additive migration (d8e9fa0b1c2d) adds a new
campaign_ai_settingstable; nothing existing is touched, nothing is
backfilled, and every campaign keeps using the instance's providers until
someone changes that. Transcription and summarisation have always been one
endpoint each for the whole instance, which is right when you host one group
and awkward the moment you host several — "whose OpenAI bill is this" is a
question those groups need to be able to answer separately. Admins get one new
switch in Admin → Bot Settings, off by default: "Let campaigns bring their
own AI providers". With it on, each campaign's settings page grows a panel
where its GM can point transcription and summarisation at their own endpoints
and keys. The two sides are independent, so "our own key for summaries, your
Whisper box for transcription" works; leaving either blank keeps using yours.
Keys are encrypted at rest with the same key as your own instance secrets and
are never shown again, to anyone. Every URL goes through the same checks an
admin's does, so a GM cannot aim the instance at an address on your private
network. Turning the switch back off makes every override on the instance
inert immediately without deleting any of them — switch it on again and
each group has exactly what it had. Two things worth knowing before you enable
it: a group pointing at a hosted service is uploading their session audio to
that service under their contract rather than yours, and a campaign whose own
endpoint is misconfigured fails visibly rather than quietly borrowing yours.
See "Per-campaign AI providers (bring your own)" indocs/OPERATIONS.md. -
[webapp] Quote timestamps now point at the word, not at the sentence — if
your transcription server is current (#352). One additive migration
(c7d8e9fa0b1c) adds a nullablewordscolumn totranscript_segments;
nothing existing is touched and nothing is backfilled. The bundled
transcription server used to return one time per sentence, so a quote
captioned "[01:12:40]" was really "somewhere in the twenty seconds that
started at 01:12:40" — which is fine for a one-line reply and useless for a
line thirty seconds into somebody's monologue. The current server aligns
every word, and Quest Board now reads those timings and anchors each quote to
the word it actually begins on. Alignment is not perfect and is not pretended
to be: a word the server could not place is positioned between its neighbours
and marked as an estimate, and a quote anchored to an estimated word keeps
the old sentence-level timestamp rather than a prettier invented one.
Because two versions of that server exist and you update it on your own
schedule, Quest Board now asks it what it can do — one cachedGET /health
— instead of assuming. On an older server everything behaves exactly as it
did before, with one deliberate change: it is now reported as taking no
vocabulary hints, because it accepts the campaign-names field and silently
ignores it, and Admin → Bot Settings tells you that updating the server is
the fix. Word timings and vocabulary bias apply to new sessions only —
nothing is reprocessed, and older transcripts keep their sentence-level
times. See OPERATIONS.md → "Upgrading the bundled transcription server". -
[webapp] A busy evening now makes you wait instead of making you retry
(#356). Groups play on the same two evenings, so a week of recordings lands
within a few hours of each other — against one transcriber that does one
session at a time and one AI endpoint that does two requests at a time.
Quest Board now knows those limits and queues behind them instead of pushing
past them, so a Saturday backlog costs you some patience rather than a
failed session with a rate-limit error in it. If a service turns a request
away anyway — a shared GPU box, or a hosted provider's own limit — that
session goes back in the queue and waits as long as the service asked,
instead of failing with something you could not have fixed. While a session is waiting its
page says so — "Queued behind 3 other sessions — about 20 minutes" — rather
than showing the same silent "Processing…" a genuinely stuck session shows,
which is what has been sending GMs to the Retry button and putting a second
copy of the same recording into the same queue./askand the Test LLM
button never wait: if the queue is full they say so immediately, and say
that your endpoint is not the problem. Admins get the whole picture at
GET /api/admin/ai/queue. The defaults suit the deployments this ships
with; if you run llama.cpp with--parallel 4, or several transcription
replicas behind one URL, two new optional settings say so —
QB_LLM_CONCURRENCY_LIMITandQB_ASR_CONCURRENCY_LIMITin.env. No
migration, and nothing to change if you leave them alone. -
[webapp] Transcription is now tested end to end against a session that
never happened (#433). Recording, transcribing and attributing a session is
the part of Quest Board with the most ways to go quietly wrong, and until now
it was only ever tested against mocks — nothing checked that a real speech
server, given six speakers talking over each other for half an hour, gave
every line back to the right person. There is now a complete invented session
in the repository: six speaker tracks of computer-generated speech, thirty
minutes, every word written for the test, and every check that guards your
recordings watched actually firing against it. No real voices and no real
session content are involved, and none ever will be. This costs the
repository about 9 MB of audio; the reasoning for allowing that at all, and
the rules it lives under, are inCLAUDE.md. -
[webapp] Transcription is now told your campaign's names before it
listens (#355). Your party's character names, your wiki's people, places,
factions and artifacts, your NPC cards and any alternate names you have
recorded are handed to the speech model as a hint at the start of every
recording it transcribes. Invented names are the one thing a general-purpose
speech model has never heard, and they are also the words that matter most in
a session — and once a name is wrong in the transcript, nothing further down
can fix it, because the summary, the highlights and the wiki suggestions all
read the transcript rather than the audio. You should see fewer sessions
where a character's name is spelled three different ways, or split into two
ordinary words. Nothing to configure, and nothing changes for a campaign with
an empty wiki. The list is rebuilt from your wiki each time a recording is
processed, so a name you add tonight is in effect for tonight's session, and
a name you retire stops being suggested. Entries in the wiki trash and
suggestions the bot has made but you have not accepted are deliberately left
out. -
[webapp] Admin → Bot Settings now tells you what your AI providers cannot
do, instead of quietly working around it (#358). One additive migration
(a5b6c7d8e9fa) adds a nullable column tosummarisation_runs; nothing is
backfilled and nothing existing changes. A new panel, "What your AI
configuration can and cannot do", names the resolved provider on each side and
lists every gap in plain language with the consequence you would otherwise
just experience: an endpoint that cannot enforce a JSON schema (the big one —
events can be dropped or mis-attributed from a summary with nothing failing),
a context window too small to hold a session in one prompt and how many passes
it will take instead, a provider that does not report prompt tokens so an
over-long transcript cannot be detected, an endpoint that silently discards
the start of an over-long prompt, a provider guessed from its URL rather
than configured, and transcription without word-level timestamps so quote
timings are estimated. The "Test connection" buttons report the same thing
alongside reachability, because an endpoint can answer in 300 ms and still be
unable to do any of this. Quest Board has known all of it since v4.1.1 and
told nobody, which meant a limitation of the endpoint you picked read as a
limitation of Quest Board. Summaries also now record the gaps that applied
when they were written — shown on the session as "Summarised under a limited
configuration" — and a run on a provider that cannot enforce a schema is
marked degraded up front rather than only when its output came back visibly
broken. -
[webapp] You can now have OpenAI or Groq transcribe your sessions instead
of the Whisper server you run yourself (#128, #350). No migration, and
nothing changes unless you choose it — the bundled server stays the default
and stays a fully tested path.Admin → Bot Settingsgains a transcription
provider select and a model field alongside the endpoint URL; point it at
https://api.groq.com/openai/v1orhttps://api.openai.com/v1and leave the
model blank for that host's default, or name any other server that speaks
the same API. Read this part before you switch: with a managed provider,
every per-speaker track of every recording is uploaded to a third party. If
you self-host Quest Board precisely so your table's recordings stay on your
own machine, this setting undoes that and no amount of accuracy is worth it.
If that is not why you self-host, what you get is worth having: word-level
timings that are measured rather than estimated across each sentence, so
jumping to a quote lands on the word rather than near it; no GPU to own or
keep running; and a three-hour session transcribed in minutes on Groq's turbo
model. The "What your AI configuration can and cannot do" panel makes the
difference concrete — the bundled server lists two gaps there and this lists
none. Both services cap one request at 25 MB, which is under fourteen minutes
of audio, so tracks are cut into speech spans and stitched back onto the
session timeline automatically — including when you have silence trimming
switched off, because otherwise every track would be refused unsent. A chunk
that is refused anyway is counted, and a track that loses more than a quarter
of itself fails the run rather than handing you a transcript quietly missing
a third of the evening. See OPERATIONS.md → "Choosing a transcription
provider". -
[webapp] Quest Board now checks its own AI providers once a month, so a
dead endpoint is found before a session needs it (#359). Every AI call in
the product happens because a GM asked for something, which means a Whisper
server that stopped answering in April is discovered by a failed
transcription in July — with the recording already made. A new monthly
background task transcribes one second of silence through whichever ASR you
have configured and asks your LLM to reply with "OK", then records
{ran_at, asr_ok, llm_ok, asr_provider, llm_provider, error}under the
provider_canarykey inapp_settings, logging a warning when either side
fails. It costs a second of audio and a handful of tokens a month; it grades
nothing, retries nothing, and treats "not configured" as a blank rather than
a fault. There is no admin panel for it yet — read it with thepsql
one-liner in OPERATIONS.md → Alerting. Alongside it, every AI provider
adapter now has to pass a shared conformance suite in CI, which is what keeps
"swap your AI provider without a rewrite" true as the code moves, and keeps
the self-hosted fallback a tested path rather than a hopeful one. -
[webapp] Quest Board now records what each session costs to process, in
dollars or in GPU minutes depending on what you run (#357). One additive
migration (b6c7d8e9fa0b) adds a newsession_usagetable; nothing
existing is touched and nothing is backfilled, because there is nothing to
backfill from — none of this was ever recorded. A new panel in Admin → Bot
Settings shows the last 30 days by campaign, by month, and run by run: how
many hours of per-speaker audio went in, how much of it the silence pre-pass
removed before transcription, how long each phase took, how many tokens the
model read and wrote, and what it is estimated to have cost. Every processing
run gets a row, including the ones that failed — transcription is the
expensive half and it finishes long before most failures do, so a run that
transcribed three hours and then fell over has already been paid for, and a
retry is a second row rather than a replacement for the first. Dollar
figures are estimates from published list rates on a stated date, marked as
such on every number: no provider tells us what it actually charged, and a
model our rate table has never heard of gets no figure rather than a
plausible-looking guess. If you self-host your transcription or your LLM there
is no bill to estimate, so those runs show the compute time they occupied
instead — which is the number to size a GPU against, and the honest answer
where "$0.00" would imply the run was free. Nothing to configure; it starts
recording on the next session you process. -
[webapp] Admin → Bot Settings can now measure your hardware, and the docs
say what to expect from three named profiles (#353, #354). No migration.
A new panel, "Hardware profile", has a Run preflight button: it makes one
health round trip to your transcription endpoint and one short generation
against your LLM, times them, and reports what it found — a detected profile,
tokens per second, transcription speed relative to real time, the gaps in your
configuration, and roughly how long a 3.5-hour, five-player session will
take on your hardware. Until now the only way to learn that was to record
one and wait. The result is stored with the date it was taken, so the panel
says "as measured on …" rather than pretending to be live, and the
transcription speed is read from sessions you have actually processed rather
than from a synthetic clip — a deployment that has processed nothing sees
"not yet measured" instead of an invented number.POSTandGET /api/admin/ai/preflightare the same thing as JSON. docs/OPERATIONS.md →
"Hardware profiles" now carries a table of three profiles — CPU-only, modest
GPU, workstation-class GPU — with the model, the hardware, the expected
processing time and the measured accuracy scores for each, including the
command that produced every number, plus
docs/examples/docker-compose.cpu-llm.yml: a complete, runnable CPU-only LLM
service to drop into the stack if you have no GPU.
Changed
- [webapp] Running without a GPU is now a supported configuration rather than
a slower version of one (#353). No migration. Every timeout in the AI
pipeline used to be a fixed number chosen against a GPU — five minutes for a
structured call, ten for a transcription track, thirty seconds for the "Test
connection" button. On CPU-only hardware those are not slow budgets, they are
impossible ones, so a self-hoster without a GPU saw failures rather than
patience, with a recording already made and nothing naming the cause. A new
QB_AI_PROFILEsetting (auto,gpuorcpu) scales all of them together,
andQB_LLM_TIMEOUT_SECONDS,QB_LLM_PROSE_TIMEOUT_SECONDSand
QB_ASR_TIMEOUT_SECONDSset any of the three directly if you have measured
your own endpoint — a value you write down is used exactly as written. The
Celery time limits are derived from those timeouts rather than configured
beside them, so the limit can never be shorter than the provider calls it is
supposed to contain. Nothing changes for an existing deployment: the
default isauto, which is the GPU timings unless the new preflight has
measured your box as CPU-class, and on the GPU profile the task limits are the
same 6 hours they have always been. Two smaller consequences worth knowing:
the monthly provider health check and the admin "Test connection" button now
scale too, because a thirty-second cap on a CPU endpoint could only ever
report a working provider as broken.
Fixed
- [webapp] A paid transcription service was being reported as free compute
(#502). One additive migration (e9fa0b1c2d3e) adds three nullable
columns tosession_usage; nothing existing is touched and nothing is
backfilled. Admin → AI Usage recorded the provider you configured rather
than the one that ran — and "Detect from the URL" is the default, which
stores nothing at all. So if you had pasted an OpenAI or Groq endpoint and
left that select alone, every one of those runs was filed as self-hosted:
the page showed GPU seconds and no dollars for sessions you were being
invoiced for, which is the exact opposite of what it is for. Runs now record
the transcription service, model and endpoint they actually used, so a
managed one is counted as managed from here on. Older rows are left as they
are — the endpoint a session used six weeks ago is not recoverable now, and
guessing it would be inventing history rather than repairing it. Alongside
that: managed transcription can be priced at all for the first time (OpenAI
and Groq list rates, per model — the Groq figure is a floor, because their
ten-second minimum per request is not visible from what is recorded), the
rate table has learned the Claude 5 models the documentation recommends
instead of showing no cost for them, andmake conformance-livecan now be
pointed at a managed transcription service rather than only at the bundled
one.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-