From 93f67942c743bca300e9171b2518d52dd46d5547 Mon Sep 17 00:00:00 2001 From: lucas Date: Sat, 30 May 2026 03:31:27 +0900 Subject: [PATCH] Add AssemblyAI Sapat transcription guide Signed-off-by: lucas --- authors/lucas_chinook.md | 5 + ...0_definition_async_speech_transcription.md | 20 ++ ...yai_transcription_with_sapat_in_daytona.md | 246 ++++++++++++++++++ ...ription_with_sapat_in_daytona_workflow.svg | 32 +++ 4 files changed, 303 insertions(+) create mode 100644 authors/lucas_chinook.md create mode 100644 definitions/20260530_definition_async_speech_transcription.md create mode 100644 guides/20260530_run_assemblyai_transcription_with_sapat_in_daytona.md create mode 100644 guides/assets/20260530_run_assemblyai_transcription_with_sapat_in_daytona_workflow.svg diff --git a/authors/lucas_chinook.md b/authors/lucas_chinook.md new file mode 100644 index 00000000..3a3d95fa --- /dev/null +++ b/authors/lucas_chinook.md @@ -0,0 +1,5 @@ +Author: Lucas Chinook Title: AI Engineer Description: Lucas Chinook builds +developer automation, AI-assisted coding workflows, and reproducible cloud +development guides. Lucas focuses on practical systems that engineers can +verify locally before they rely on them in production. Author GitHub: +[GitHub](https://github.com/chinook1001) diff --git a/definitions/20260530_definition_async_speech_transcription.md b/definitions/20260530_definition_async_speech_transcription.md new file mode 100644 index 00000000..8f2ed4e1 --- /dev/null +++ b/definitions/20260530_definition_async_speech_transcription.md @@ -0,0 +1,20 @@ +--- +title: 'Async Speech Transcription' +description: 'A speech-to-text workflow that submits audio as a job, polls for completion, and then fetches the transcript.' +date: 2026-05-30 +author: 'Lucas Chinook' +--- + +# Async Speech Transcription + +## Definition + +Async speech transcription is a speech-to-text pattern where an application uploads audio, creates a transcription job, waits for the service to process that job, and fetches the transcript after completion. + +## Context and Usage + +Async transcription is useful when audio may be large, processing may take longer than a single HTTP request, or the speech provider offers extra analysis features such as speaker labels, formatting, summaries, or entity extraction. + +For command-line tools, the async pattern usually has four parts: upload the media file, submit a job that references the uploaded audio, poll the job status, and write the finished transcript to local output. + +That shape keeps the local workflow predictable while still letting the provider run longer processing in the background. diff --git a/guides/20260530_run_assemblyai_transcription_with_sapat_in_daytona.md b/guides/20260530_run_assemblyai_transcription_with_sapat_in_daytona.md new file mode 100644 index 00000000..139a902e --- /dev/null +++ b/guides/20260530_run_assemblyai_transcription_with_sapat_in_daytona.md @@ -0,0 +1,246 @@ +--- +title: "Run AssemblyAI Transcription With Sapat in Daytona" +description: "Build and verify an AssemblyAI-backed Sapat transcription workflow inside a reproducible Daytona workspace." +date: 2026-05-30 +author: "Lucas Chinook" +tags: ["daytona", "sapat", "assemblyai", "speech-to-text"] +--- + +# Run AssemblyAI Transcription With Sapat in Daytona + +# Introduction + +Video recordings are easy to collect and hard to operationalize. +A product demo, a customer interview, or a long engineering review can hold useful details, but those details stay trapped until someone turns the audio into searchable text. +Sapat helps with that by converting media files, sending the audio to a speech-to-text provider, and writing a transcript next to the original file. + +This guide shows how to run Sapat with AssemblyAI in a Daytona workspace. +The workflow uses an [async speech transcription](../definitions/20260530_definition_async_speech_transcription.md) provider: Sapat uploads the converted audio, submits a transcript job, polls until AssemblyAI finishes processing, and writes the transcript locally. +Daytona gives the process a clean development environment, so the same commands work for local review, provider testing, and future CI checks. + +The companion Sapat implementation is in [nibzard/sapat#60](https://github.com/nibzard/sapat/pull/60). It adds the `assemblyai` provider to Sapat's current provider registry after the new plugin architecture refactor. + +![Sapat AssemblyAI workflow](./assets/20260530_run_assemblyai_transcription_with_sapat_in_daytona_workflow.svg) + +## TL;DR + +- Use Daytona to create a reproducible Python workspace for Sapat. +- Install the AssemblyAI-enabled Sapat branch or use upstream Sapat after the provider PR lands. +- Store `ASSEMBLYAI_API_KEY` in `.env`, never in code or transcripts. +- Run Sapat with `--provider assemblyai` and a model fallback such as `universal-3-pro,universal-2`. +- Validate with mocked tests before using real audio, then inspect the generated transcript file. + +## Prerequisites + +You need: + +- A GitHub account and access to a Daytona workspace. +- Python 3.9 or newer in the workspace. +- `ffmpeg`, because Sapat converts source media before transcription. +- An AssemblyAI API key stored outside Git. +- A small MP4 or audio sample that you are allowed to upload to a third-party service. + +For reference, AssemblyAI documents file upload through its [upload endpoint](https://www.assemblyai.com/docs/api-reference/files/upload) and transcript job creation through its [transcript submit endpoint](https://www.assemblyai.com/docs/api-reference/transcripts/submit). +The Sapat provider follows that upload, submit, poll, fetch sequence. + +## Step 1: Create a Daytona Workspace + +Start with an empty repository or a small project repository that contains sample media. For a new scratch project, create these files: + +```bash +mkdir sapat-assemblyai-daytona +cd sapat-assemblyai-daytona +mkdir -p .devcontainer samples transcripts +``` + +Add a `.devcontainer/devcontainer.json` file: + +```json +{ + "name": "sapat-assemblyai-workspace", + "image": "mcr.microsoft.com/devcontainers/python:3.11-bookworm", + "features": { + "ghcr.io/devcontainers/features/ffmpeg:1": {} + }, + "postCreateCommand": "python -m pip install --upgrade pip", + "customizations": { + "vscode": { + "extensions": ["ms-python.python"] + } + } +} +``` + +Push the repository, open it in Daytona, and wait for the workspace to finish building. The important part is not the exact base image. The important part is that every contributor gets Python, `ffmpeg`, and the same project layout instead of debugging transcription from a half-configured laptop. + +## Step 2: Install the AssemblyAI-Enabled Sapat Branch + +Until the provider PR is merged, install the companion branch directly: + +```bash +python -m venv .venv +. .venv/bin/activate +pip install "git+https://github.com/chinook1001/sapat@codex/add-assemblyai-provider" +``` + +After the provider lands upstream, switch back to the normal repository: + +```bash +pip install "git+https://github.com/nibzard/sapat" +``` + +Confirm that the CLI is available: + +```bash +sapat --help +``` + +You should see a `--provider` option. Sapat discovers providers dynamically, so the `assemblyai` provider only appears when the required environment variable is present. + +## Step 3: Configure AssemblyAI Without Leaking Secrets + +Create a local `.env` file: + +```bash +cat > .env <<'EOF' +ASSEMBLYAI_API_KEY=replace-with-your-key +ASSEMBLYAI_SPEAKER_LABELS=false +ASSEMBLYAI_PUNCTUATE=true +ASSEMBLYAI_FORMAT_TEXT=true +EOF +``` + +Then add `.env` to `.gitignore`: + +```bash +printf ".env\ntranscripts/\n" >> .gitignore +``` + +The provider also supports `ASSEMBLYAI_BASE_URL` for mocked or proxied validation, plus `ASSEMBLYAI_POLL_INTERVAL_SECONDS` and `ASSEMBLYAI_TIMEOUT_SECONDS` if you need to tune long jobs. For normal usage, the API key is the only required value. + +## Step 4: Run a Single Transcription + +Place a short video or audio file in `samples/`. Keep the first test boring: ten to thirty seconds is enough to prove the provider path. + +```bash +sapat samples/product-demo.mp4 \ + --provider assemblyai \ + --model "universal-3-pro,universal-2" \ + --language en \ + --quality M \ + --transcription-prompt "Product names: Daytona, Sapat, AssemblyAI" +``` + +The model value is comma-separated on purpose. AssemblyAI's current transcript API accepts a `speech_models` array, and the provider turns the CLI string into that list. In this example, Sapat requests `universal-3-pro` first and keeps `universal-2` as a fallback. + +When the command finishes, look next to the input file for the generated transcript. If you are processing sensitive material, move transcripts into a private storage path instead of committing them to the repository. + +## What Happens Behind the Command + +It helps to know what is happening under the hood before you scale this workflow to real project recordings. +Sapat first converts the input media to the provider's preferred audio format. +For the AssemblyAI provider, that is MP3, which keeps the first implementation compatible with the existing Sapat conversion pipeline. + +After conversion, the provider opens the audio file as binary data and sends it to AssemblyAI's upload endpoint. +AssemblyAI returns an `upload_url`. +Sapat does not treat that URL as the transcript; it uses the URL as an input to a second request that creates a transcript job. +That second request carries the selected `speech_models`, language configuration, optional prompt, and optional formatting flags. + +The provider then polls the transcript endpoint until the job is completed, failed, or timed out. +That polling loop is the reason this provider lives on Sapat's async provider base instead of the simpler OpenAI-compatible provider helper. +When AssemblyAI reports `completed`, Sapat fetches the finished transcript payload and maps it into Sapat's shared `TranscriptionResult` object. + +This design is useful in review because each boundary is testable without real audio or a real key. +The mocked tests can prove that Sapat sends the correct upload request, submits the correct JSON payload, handles queued and completed statuses, exposes transcript text, and raises an error for failed jobs. +Real API keys are only needed for manual smoke tests. + +## Step 5: Run a Directory Batch + +Sapat also accepts a directory of MP4 files: + +```bash +sapat samples \ + --provider assemblyai \ + --model "universal-3-pro,universal-2" \ + --language en \ + --quality L +``` + +For batch jobs, start with low or medium quality unless your audio is hard to understand. +Higher quality can improve recognition in noisy files, but it also increases upload size and provider processing time. +Keep source media, generated MP3s, and transcripts out of Git unless they are intentionally public fixtures. + +For team usage, add a simple naming rule before the first batch. +For example, keep source files in `samples/raw/`, write final transcripts to `transcripts/reviewed/`, and move any failed or unclear outputs to `transcripts/needs-human-review/`. +That small convention prevents reviewers from mixing raw generated transcripts with edited notes. + +Also decide what should happen to source media after the transcript is approved. +Some teams keep source recordings in private object storage and only commit cleaned summaries. +Others keep neither recordings nor transcripts in Git, and use the repository only for the repeatable transcription workflow. +The safer default is to keep private media outside the repository unless the file is intentionally public. + +## Step 6: Validate Before Real Usage + +The provider implementation was designed so that tests do not need a real AssemblyAI API key. It uses mocked HTTP calls to verify the upload, transcript submission, polling, completion, registry discovery, and error paths. + +Inside a checkout of the companion Sapat branch, run: + +```bash +python -m venv /tmp/sapat-test-venv +/tmp/sapat-test-venv/bin/python -m pip install pytest pytest-mock black click requests python-dotenv openai halo +PYTHONPATH="$PWD" /tmp/sapat-test-venv/bin/python -m pytest tests -q +PYTHONPATH="$PWD" /tmp/sapat-test-venv/bin/python -m black --check \ + sapat/providers/assemblyai.py \ + sapat/providers/async_poll.py \ + tests/providers/test_group_b.py \ + tests/test_registry.py +``` + +The current provider PR validation passed with `183 passed` and one local LibreSSL warning from `urllib3`. That warning came from the macOS Python SSL build and was not a project test failure. + +## Review Checklist + +Before opening a content or provider PR, check the workflow like a maintainer would. +This is especially important for transcription providers because a broken provider may still look fine if the guide is polished. + +- The provider should be hidden when `ASSEMBLYAI_API_KEY` is missing. +- The provider should appear through Sapat's dynamic registry when the key is present. +- Tests should mock HTTP traffic instead of uploading real media. +- The request payload should include `audio_url` and `speech_models`. +- The polling path should cover at least queued, completed, and failed statuses. +- The guide should never include a real API key, private media file, generated transcript, or payout detail. + +For content review, verify that the reader can copy the commands into a Daytona workspace and understand which steps are temporary branch review steps. +Once the Sapat provider merges, update the install command to use upstream Sapat rather than the companion branch. +That keeps the article evergreen instead of pinning readers to a contributor branch forever. + +## Troubleshooting + +**Problem:** `Provider 'assemblyai' is not available.` + +**Solution:** Confirm that `.env` is being loaded and that `ASSEMBLYAI_API_KEY` is set in the Daytona terminal where you run Sapat. Sapat hides providers whose required environment variables are missing. + +**Problem:** The command waits longer than expected. + +**Solution:** AssemblyAI processing is asynchronous. Use a shorter test file first, then raise `ASSEMBLYAI_TIMEOUT_SECONDS` for long recordings. If your file is very large, split it before upload or run smaller batches. + +**Problem:** The transcript is technically correct but misses product names. + +**Solution:** Use `--transcription-prompt` with domain terms, acronyms, and speaker names. Keep the prompt short and factual. Do not paste private meeting notes into a prompt unless the recording itself is already approved for upload. + +**Problem:** The transcript should include speaker labels. + +**Solution:** Set `ASSEMBLYAI_SPEAKER_LABELS=true` in `.env`. Speaker labels are useful for interviews, support calls, and engineering reviews where ownership matters. + +## Conclusion + +You now have a Daytona workflow that installs Sapat, enables the AssemblyAI provider, keeps credentials out of Git, and turns media files into local transcripts. The same setup works for a one-off product demo or for a repeatable batch job across a folder of recordings. + +The strongest habit is to validate the provider with mocked tests before sending real audio. Once that path is green, Daytona gives every reviewer the same environment, Sapat handles conversion and output, and AssemblyAI handles the long-running transcription job. + +## References + +- [AssemblyAI upload endpoint](https://www.assemblyai.com/docs/api-reference/files/upload) +- [AssemblyAI transcript submission endpoint](https://www.assemblyai.com/docs/api-reference/transcripts/submit) +- [Sapat AssemblyAI provider PR](https://github.com/nibzard/sapat/pull/60) +- [Daytona installation documentation](https://www.daytona.io/docs/installation/installation/) diff --git a/guides/assets/20260530_run_assemblyai_transcription_with_sapat_in_daytona_workflow.svg b/guides/assets/20260530_run_assemblyai_transcription_with_sapat_in_daytona_workflow.svg new file mode 100644 index 00000000..d15ec8fc --- /dev/null +++ b/guides/assets/20260530_run_assemblyai_transcription_with_sapat_in_daytona_workflow.svg @@ -0,0 +1,32 @@ + + Sapat AssemblyAI transcription workflow in Daytona + A Daytona workspace runs Sapat, converts video audio, uploads audio to AssemblyAI, polls for the transcript, and writes a local text file. + + + Daytona workspace + Secrets stay in .env, code and validation stay reproducible. + + + Input media + MP4 or directory + + + Sapat + Convert + split + + + AssemblyAI + Upload + poll job + + + Transcript + Local .txt output + + + Review path: mocked provider tests, registry checks, CLI help, and markdown validation before opening the PR. + + + + + +