Repository navigation
WP-05: Intake + ASR - #8
Merged
Merged
Conversation
- Transcript parsers (plain/SRT/VTT/Riverside/Descript/Otter) with format detection + U5 chip labels; fixtures per format incl. edge cases - ASR: Groq whisper-large-v3-turbo adapter (verbose_json, segment granularity, lang=en, vocab prompt, concurrency 3), 2s overlap dedupe, sentence-group merge (<=45s/<=500 chars), language gate (U19), fake adapter - Browser compression port from site/app.html: 16 kHz mono Opus ~24 kbps, <=10-min chunks with 2s overlap, plan/mime/bitrade pure functions tested - Intake UI per MK-4: upload tab (drop zone, allow-list, compress/upload progress), paste tab (format chip), details, sample episode, resumable status view via ?id= - Routes: POST /api/uploads/token, POST /api/episodes, POST /api/episodes/:id/transcribe, GET /api/episodes/:id, GET /api/audio/:id at the documented db/queries + blob + entitlements boundaries - Entitlements stub (free-plan limits) until WP-08; quota wall 402 + §6 copy - Tests: 240 unit (parsers, merge, language, Groq, service at mocked boundaries, compression plan) + 7 new e2e (U4/U5/U19 paths)
# Conflicts: # docs/relaunch/DECISIONS.md # web/.env.example # web/db/queries.ts # web/lib/billing/entitlements.ts # web/lib/copy.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WP-05: Intake + ASR
Implements M2 (new episode intake), M3 (Groq transcription) and §8.3 on top of WP-02. Branch:
wp-05-intake-asr, basemain@ 810d8ff. Worktree:okra-wp05.Deliverables
web/lib/asr/—transcribe(chunks: {blobPath, offsetMs}[], opts: {vocab})→Segment[]: Groq adapter (whisper-large-v3-turbo,verbose_json,timestamp_granularities[]=segment,language=en, vocab+guest prompt ≤200 tokens, concurrency 3), 2 s overlap dedupe by timestamp, sentence-group merge (≤45 s / ≤500 chars,seg_XXXXXXids),AsrErrorported fromapi/asr.js, language gate (supportedInputport fromapi/generate-suite.js), plus an in-memory fake adapter.web/lib/transcripts/— plain text, SRT, VTT, RiversideName (00:12:03):, Otter[00:12:03] Name:, DescriptName:; detection returns{format, hasTimestamps, hasSpeakers, chip}; fixtures for every format incl. unsorted timestamps, CRLF, speakerless./app/episodes/new(MK-4) — Upload tab (drop zone, mp3/m4a/wav/aac/ogg/webm/mp4/mov allow-list, ≤2 h; WebAudio compression ported fromsite/app.htmlto mono 16 kHz Opus/WebM 24 kbps; ≤10-min chunks ~1.8 MB with 2 s overlap; compress/upload % progress via@vercel/blob/clientonUploadProgress) and Paste tab (format chip, speakers/timestamps preserved), optional title/number/guests, "Try with a sample episode" (/samples/demo-episode.txt, source=sample, free), resumable status view via?id=(polls GET status every 2 s).POST /api/uploads/token(type allow-list, ≤300 MB, quota check),POST /api/episodes(create; counts quota; paste parsed server-side into segments),POST /api/episodes/:id/transcribe(uploading→queued→transcribing→queued;unsupported_language/no_speechrefund quota; maxDuration 300),GET /api/episodes/:id(status read path, U4),GET /api/audio/:episodeId?t=(short-lived signed URL for the chunk containing t).@/db/queriesseam;@/lib/blobwraps all@vercel/blobcalls;@/lib/billing/entitlementsstub (free-plan limits) until WP-08.Acceptance criteria → evidence
EpisodeIntakeShellcompress/upload progress +?id=status polling; e2eU4: ?id= resumes from server status (transcribing)(network-intercepted); route handler tests:handleEpisodeGet — resumable status read path (U4)(mocked@/db/queries)U5: pasting a Riverside transcript shows the exact format chip; unittranscripts.test.ts(23 tests) asserts chip + parsed speaker/ms segments; service testcreates a paste episode, parsing speakers + timestamps into segments(seg_000000/Dev/12_000)U19: failed unsupported-language episode shows the exact copy;asr-language.test.ts(Latin ratio <0.6, whisper-language gate); service tests: ASRunsupported_language→failed+countsQuota:false(refund); paste-side gate returns the same copy with nothing createdweb/tests/fixtures/transcripts/: plain, srt, srt-unsorted, vtt, riverside, riverside-crlf, descript, otterasr-merge.test.ts: overlap dedupe (drop + keep-when-uncovered), sentence grouping 45 s/500 chars bounds, id/idx renumberingcompression.test.ts:planChunksboundaries + stub absorb, bitrate math (600 s × 24 kbps ≈ 1.8 MB), mime choice table. The AudioWorklet/MediaRecorder graph itself has no Playwright smoke: MediaRecorder + OfflineAudioContext need a real audio device/graph and a >realtime encode — not feasible headless in CI; the pure decision layer is fully unit-tested and the graph is a direct port ofsite/app.html(documented in DECISIONS.md)asr-language.test.tsportssupportedInput()exactly (Latin/letters ≥0.6)Checks
pnpm lint✓ ·pnpm typecheck✓ (next typegen && tsc --noEmit) ·pnpm test✓ 240 unit tests ·pnpm build✓ · e2e ✓ 22/22 (7 new intests/e2e/new-episode.spec.ts, run withE2E_PORT=3105; WP-02 suites unaffected)Live ASR smoke — BLOCKED
.env.relaunchholds sanitized placeholders (values are literally"[SENSITIVE]", nogsk_…key,ASR_PROVIDER ≠ groq), so no live Groq call was possible. The adapter is covered instead by fetch-injected unit tests (request shape, error mappinggroq_<status>,empty_transcript→no_speech, language gate). No synthetic speech fixture exists indocs/samples/(only text), so none was generated.Blockers for the owner
BLOB_READ_WRITE_TOKENis not configured anywhere. Uploads (/api/uploads/tokenanswers 503 "Audio upload isn't set up in this preview."), signed playback (/api/audio/:id503), and the clientupload()flow are implemented against the real@vercel/blobAPI (verified against the installed 2.8.2 types) but cannot run until the token is set. Unit-tested with the boundary mocked; noted inweb/.env.example..env.relaunch) — live ASR smoke pending; owner action above.?t=+chunkOffsetMs) — WP-06 should use the returnedchunkOffsetMsfor seeking.Decisions logged (docs/relaunch/DECISIONS.md, newest first)
Chunk manifest JSON in
audio_blob_path·chunkOffsetsMs[]extension to the §7.5 create body · format-label heuristics (paren→Riverside, bracket→Otter, speaker-only→Descript) · paste-side Latin-ratio gate · entitlements stub seam ·?t=chunk-resolved audio URL · compression port + chunking · overlap dedupe guard · trailing-stub absorb · quota checked at token / counted at create · additivedb/queriesextension.Notes
site/app.html) encodes in real time, so a 60-min episode takes ~60 min to compress client-side. MVP-acceptable (matches legacy); WebCodecs noted as the later escape hatch.db/queries.ts+lib/copy.tsadditions are purely additive.