Commit 30789bd
committed
feat: add optional local transcript cleanup after speech recognition
Adds an opt-in, self-hosted text model that corrects grammar, punctuation,
capitalization, and paragraph breaks on a final transcript. It runs on the
gateway machine after recognition, sees only recognised text (never audio),
and needs no Voca account and no internet access once its model is installed.
Off by default, and off until an operator installs a model and enables it.
The governing rule is that cleanup can improve a transcript and never lose
one. The deterministic writing-style result is computed first and
independently; every way cleanup can fail — no model, unsupported language,
input over the ceiling, runtime busy, timed out, malformed answer, or an edit
the checks refuse — returns that exact string, with a bounded reason. A
recognition that succeeded stays a success: cleanup failure is metadata on a
200, never an error a client has to handle. Cancellation propagates rather
than being swallowed as a tidy fallback.
app/cleanup/ holds the whole feature:
- service.py is the single finalization every entry point shares, so sessions,
one-shot requests, and streaming cannot drift into different decisions.
- validation.py refuses a candidate that changes a protected span, a digit run,
a negation, a weekday or month, the writing system, or more of the text than
an edit budget allows, and rejects wrappers, refusals, leaked reasoning, and
truncated generations.
- prompts.py serialises the transcript as JSON data inside a user message, so a
dictated "ignore the previous instructions" is content to preserve.
- transport.py owns a raw loopback socket: no URL to redirect, no proxy
inherited, a bounded body, and a close that actually stops the backend.
- worker.py/host.py launch and own a llama-server via argv on an unpublished
ephemeral port with a credential of its own; manager.py resolves environment
overrides against saved UI choices, admits one inference at a time, and
unloads an idle worker without ever touching the speech engine.
- catalog.py is a separate namespace with its own pin file. A cleanup artifact
with no pinned revision and SHA-256 cannot be installed at all, and never
appears as a selectable speech engine.
Wiring:
- Sessions take cleanup: off|conservative|inherit, snapshotted at creation, and
return original_transcript plus a redacted cleanup block. finish/retry answer
from storage without a second model call.
- /v1/audio/transcriptions takes a multipart cleanup field defaulting to off,
keeps the {"text": ...} body unchanged, and reports status in optional
X-Voca-Cleanup-* headers.
- /v1/stream runs exactly one cleanup pass, after the streaming lock and the
engine lease are released. Partials are never rewritten.
- New GET /v1/capabilities reports supported modes and languages, keeping
offered languages separate from evaluated ones.
- Raw style is never corrected, whatever a request asks for.
Also closes the read-then-update race around concurrent finish: the state test
now lives inside the statement, so exactly one caller starts a transcription
and a session deleted or re-uploaded mid-flight is neither resurrected nor
overwritten by the stale job.
Storage migrates additively and idempotently; existing rows and transcripts are
untouched and read back as cleanup off with no manufactured original.
The shipped candidates are the upstream-published Qwen3 0.6B and 1.7B Q8_0
GGUFs. Qwen publishes no smaller quantization in its own repositories, and a
community Q4_K_M is a separate artifact whose bytes the upstream card does not
vouch for, so first-party provenance won over file size. evaluated_languages is
empty for both: no benchmark has been run, so nothing claims a tested language.1 parent f34c887 commit 30789bd
51 files changed
Lines changed: 6545 additions & 98 deletions
File tree
- app
- cleanup
- fragments
- routes
- templates
- settings
- test_panel
- webui
- docs
- scripts
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
198 | 198 | | |
199 | 199 | | |
200 | 200 | | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
201 | 242 | | |
202 | 243 | | |
203 | 244 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
| 79 | + | |
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
| |||
490 | 491 | | |
491 | 492 | | |
492 | 493 | | |
| 494 | + | |
493 | 495 | | |
494 | 496 | | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
495 | 503 | | |
496 | 504 | | |
497 | 505 | | |
498 | 506 | | |
499 | 507 | | |
500 | 508 | | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
501 | 559 | | |
502 | 560 | | |
503 | 561 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
| |||
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
18 | 22 | | |
19 | 23 | | |
20 | 24 | | |
| |||
318 | 322 | | |
319 | 323 | | |
320 | 324 | | |
321 | | - | |
| 325 | + | |
322 | 326 | | |
323 | 327 | | |
324 | 328 | | |
| |||
355 | 359 | | |
356 | 360 | | |
357 | 361 | | |
358 | | - | |
| 362 | + | |
359 | 363 | | |
360 | 364 | | |
361 | 365 | | |
362 | | - | |
| 366 | + | |
363 | 367 | | |
364 | | - | |
| 368 | + | |
365 | 369 | | |
366 | | - | |
367 | | - | |
| 370 | + | |
| 371 | + | |
368 | 372 | | |
369 | 373 | | |
370 | 374 | | |
| |||
422 | 426 | | |
423 | 427 | | |
424 | 428 | | |
425 | | - | |
| 429 | + | |
426 | 430 | | |
427 | 431 | | |
428 | 432 | | |
| |||
453 | 457 | | |
454 | 458 | | |
455 | 459 | | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
456 | 550 | | |
457 | 551 | | |
458 | 552 | | |
| |||
471 | 565 | | |
472 | 566 | | |
473 | 567 | | |
| 568 | + | |
474 | 569 | | |
475 | 570 | | |
476 | 571 | | |
| |||
487 | 582 | | |
488 | 583 | | |
489 | 584 | | |
| 585 | + | |
490 | 586 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
0 commit comments