Commit c5fce57
authored
## What
Adds `POST /v1/stt/transcribe` — an authenticated backend proxy in front
of the
parakeet GPU service's `/v2/transcribe`. This is step 1 of the parakeet
cleanup
plan laid out in #8854 ("Add new authed backend proxy route for mobile
app").
## Why
When a user selects the "Omi Parakeet" STT provider, the mobile app
currently
posts audio buffers directly to `parakeet.omiapi.com/v1/transcribe` with
no
authentication of any kind (`app/lib/models/stt_provider.dart:533`
builds the
request with no Authorization header, and the parakeet service has no
auth
guard of its own). That leaves a GPU transcription service effectively
open to
the public at that path.
This route puts the standard Omi stack in front of it:
- **Auth**: `get_current_user_uid` (Firebase ID token), same dependency
as every other authed endpoint.
- **Rate limiting**: new `stt:transcribe` policy (60/h per UID, mirrors
`voice:transcribe`).
- **Resource isolation**: dedicated `get_stt_proxy_client()` /
`get_stt_proxy_semaphore()`
(4 connections, keep-alive off) so bulk uploads can never starve the
listen
pipeline's internal STT pool (VAD, speaker embedding, speech profile).
Requests
fail fast with 503 if no upstream slot frees up within 30s, and the body
is only
buffered into RAM after a slot is held (max 4 resident bodies; waiters
stay in
the multipart disk spool).
- **Abuse guards**: 200MB body cap (Content-Length + `file.size`
pre-checks, plus a
bounded read as defense-in-depth), and upstream filename sanitization —
parakeet
builds its temp path from the client filename, so path separators,
dot-prefixed
and overlong names are never forwarded.
- **Error mapping**: parakeet's client-actionable 413 (audio too long)
and 503
(model loading / overloaded) are forwarded with their detail; any other
upstream
failure maps to a generic 502 with nothing leaked.
- Route gets a 350s path timeout (`HTTP_STT_TRANSCRIBE_TIMEOUT`) to
cover the 300s
upstream budget; the 120s POST default would kill long transcriptions.
The response mirrors parakeet `/v2/transcribe` verbatim
(`{"text", "segments", "detected_language"}`), so migrating the app is a
URL + auth-header swap.
## Follow-ups (out of scope, per the #8854 sequencing)
- Migrate the app's `omiParakeet` provider to this route — needs a
dynamic auth
header (Firebase tokens expire hourly; `SchemaBasedSttProvider` takes
static
headers at socket creation), so it's a separate app-side change.
- Deprecate / remove the public `/v1/transcribe` exposure after the soak
period.
## Testing
- 28 unit tests (`backend/tests/unit/test_stt_router.py`, registered in
`test.sh`):
auth required, config guard, empty/oversized payloads (including a
chunked body
with no Content-Length and a direct-call test pinning the bounded read
when
`file.size` is unavailable), filename sanitization matrix, slot
exhaustion → 503,
response passthrough, `diarize` forwarding, upstream 413/503 forwarded
(dict,
non-dict JSON and HTML bodies), upstream 500 → 502 with no body leak,
network
error → 502.
- End-to-end: ran the real router under uvicorn against a fake parakeet
upstream
and exercised it with `curl` + a real WAV — 401 without auth, 200 with
exact
JSON passthrough, `diarize=false` forwarded, multipart filename
`../../etc/evil.wav` arriving upstream as `evil.wav`.
- Full `backend/test.sh` run; `scan_async_blockers.py` and
`scan_import_time_side_effects.py` clean.
Refs #8854
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/BasedHardware/omi/pull/9011?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
8 files changed
Lines changed: 551 additions & 3 deletions
File tree
- backend
- routers
- tests/unit
- utils
- docs/doc/developer/backend
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
144 | | - | |
| 144 | + | |
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
| |||
249 | 249 | | |
250 | 250 | | |
251 | 251 | | |
252 | | - | |
| 252 | + | |
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| 83 | + | |
83 | 84 | | |
84 | 85 | | |
85 | 86 | | |
| |||
210 | 211 | | |
211 | 212 | | |
212 | 213 | | |
| 214 | + | |
213 | 215 | | |
214 | 216 | | |
215 | 217 | | |
| |||
239 | 241 | | |
240 | 242 | | |
241 | 243 | | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
242 | 248 | | |
243 | 249 | | |
244 | 250 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
80 | 103 | | |
81 | 104 | | |
82 | 105 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
0 commit comments