Skip to content

feat: run instant-mode live transcription on the media server - #3548

Merged
richiemcilroy merged 11 commits into
mainfrom
feat/live-transcribe-media-server
Oct 29, 2026
Merged

richiemcilroy merged 11 commits into
mainfrom
feat/live-transcribe-media-server

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Instant-mode live transcription can now run on the media server instead of a long-lived workflow. The media server already sits next to the transcription service, so it does the waiting, downloading and transcribing. The web app stays the only writer of the transcript and the claim, and decides when to promote. The new path is off unless LIVE_TRANSCRIBE_RUNNER=media-server is set, and it falls back to the existing workflow whenever the media server can't take a recording.

sequenceDiagram
    participant D as Desktop
    participant W as Web app
    participant M as Media server
    participant S as Storage
    participant A as Transcription API
    D->>W: create instant recording
    W->>W: claim + new session id
    W->>M: POST /live-transcribe/sessions (state, signed URLs)
    loop every second
        M->>S: GET manifest (If-None-Match)
    end
    M->>S: GET init + segments (signed)
    M->>A: upload, transcribe
    M->>W: report chunk (segment range + words)
    W->>S: merge into transcription.live.json
    W-->>M: authoritative cursor + fresh URLs
    M->>W: finish (done)
    W->>S: promote to canonical transcript
Loading

How it works

  • Media server (apps/media-server/src/lib/live-transcribe.ts): one in-memory session per recording.
    • It polls the manifest with If-None-Match, plans the next chunk with the same rules as planNextLiveChunk, and sends init + context + new segments to the AssemblyAI-compatible API it was handed.
    • It keeps the init and recent segments cached, so context audio is downloaded once.
    • It has no storage credentials and no database access. It only holds signed URLs, which every report refreshes.
  • Web app (apps/web/app/api/webhooks/media-server/live-transcribe/route.ts): every report goes through the same storage and claim helpers as the workflow (apps/web/lib/live-transcribe-runner.ts).
    • The media server only says which segments it sent. describeLiveChunk rebuilds the offsets from the manifest itself, so a disagreement can't misplace words. It reproduces planNextLiveChunk exactly.
    • Promotion, the full-pass fallback and AI generation are unchanged and shared with the workflow.
  • Workflow (apps/web/workflows/live-transcribe.ts): same behaviour. Its step bodies now call the shared helpers.

Safety

  • Only one runner per recording. The claim stores a session id ($.liveTranscript.session). Only reports from that session are heard, and starting, handing over or falling back moves the id atomically (rotateLiveSession).
  • The web app owns the cursor. A chunk must start right after the artifact's lastAudioSegmentIndex. Anything else gets the real cursor back and nothing is written. Duplicate or late reports are harmless.
  • Every failure ends up in a path that exists today:
    • Media server unreachable, busy or draining: the workflow runs the recording and resumes from the artifact.
    • Media server deploys: on SIGTERM, sessions are released at once and the web app restarts each one on another server, or on the workflow.
    • Media server dies outright: the claim stamp goes stale after 3 minutes and the normal full pass runs, as it does when a workflow dies.
    • A chunk keeps failing: it is retried without context, then recorded as a gap, which blocks promotion and sends the recording to the full pass.
  • A timed-out start is never resent. It may already have been accepted, so the claim moves to a fresh id and the workflow takes over.
  • Limits match the workflow: 1 h of audio, 420 chunks, 8 minutes without new audio. On top of that, each session lasts at most 3 h and each server holds at most 200 sessions.

Cost

Workflow Media server
Waiting for audio a held function polling every 3 s one storage GET per second, on the media server
Per chunk about 4 workflow events, plus a step replay one short request to the web app
Function memory step pinned at 3 GB for the whole recording none between reports
Stop to final transcript (same recording, local stack) 0.99 s 0.50 s

Rollout

  1. Deploy the media server image.
  2. Set LIVE_TRANSCRIBE_RUNNER=media-server on the web app. Removing it returns every new recording to the workflow.

RetriggerView in GreptileConfidence Score: 5/5

The latest changes appear safe to merge; no new blocking issues were found.

Summary

The PR adds optional live transcription on the media server while keeping transcript writes and promotion in the web app.

  • The media server transcribes live recording segments and reports each chunk.
  • The web app can hand eligible recordings to a media-server session.
  • The workflow and media server use shared helpers to update and promote transcripts.

Reviews (4) · Last reviewed commit: "fix(media-server): cancel in-flight chun..." · Reviewed by Greptile

Comment thread apps/web/lib/live-transcribe-runner.ts Outdated
Comment thread apps/web/app/api/webhooks/media-server/live-transcribe/route.ts Outdated
Comment thread apps/media-server/src/lib/live-transcribe.ts Outdated

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found 3 potential issues (3 medium).

View 2 other findings in Capy.

Open in Capy

Comment thread apps/web/app/api/webhooks/media-server/live-transcribe/route.ts Outdated
Comment thread apps/web/lib/live-transcribe.ts Outdated
Comment thread apps/media-server/src/index.ts
@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
cap-status Skipped Skipped Oct 29, 2026 6:06pm UTC

@vercel
vercel Bot temporarily deployed to Preview – cap-status October 29, 2026 17:52 Inactive
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@vercel
vercel Bot temporarily deployed to Preview – cap-status October 29, 2026 17:59 Inactive
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread apps/media-server/src/lib/live-transcribe.ts
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@vercel
vercel Bot temporarily deployed to Preview – cap-status October 29, 2026 18:06 Inactive

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

Open in Capy

@richiemcilroy
richiemcilroy merged commit 451da7f into main Oct 29, 2026
44 checks passed

This branch was successfully deployed

1 active (outdated) and 1 inactive deployments
Preview – cap-status — 98be0c31 Deployed Oct 29, 2026 by vercel[bot]
Preview – cap-web — ee07e53c Deployed Oct 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant