Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions authors/telemark_digital_publisher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Author: Telemark Digital Publisher Title: Technical Publisher Description:
Telemark Digital Publisher builds and documents practical developer workflows
for automation, AI tooling, and reproducible open-source projects, with a focus
on clear setup paths, testable examples, and careful disclosure of tool-assisted
work. Author Image:
![telemarkdigital-publisher](https://github.com/telemarkdigital-publisher.png)
Author LinkedIn: Author Twitter: Company Name: Telemark Digital Company
Description: Technical publishing and automation studio for practical
developer tooling. Company Logo Dark:
![telemarkdigital-publisher](https://github.com/telemarkdigital-publisher.png)
Company Logo White:
![telemarkdigital-publisher](https://github.com/telemarkdigital-publisher.png)
26 changes: 26 additions & 0 deletions definitions/20260816_definition_local_speech_to_text.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: 'Local Speech-to-Text'
description: 'Speech recognition that runs on local hardware instead of sending audio to a hosted API.'
date: 2026-08-16
author: 'Telemark Digital Publisher'
---

# Local Speech-to-Text

## Definition

Local speech-to-text is an audio transcription workflow where the speech
recognition model runs on a developer's own machine, workstation, or
containerized environment instead of uploading audio to a hosted transcription
API. The model turns spoken language in an audio or video file into text while
the input media stays inside the local environment.

## Context and Usage

Local speech-to-text is useful when developers need repeatable tests, offline
workflows, predictable costs, or stronger control over source audio. In a
[development environment](</definitions/20240819_definition_development environment.md>)
such as a Daytona workspace, teams can install a local transcription engine,
pin its dependencies, and run the same command against meeting recordings, demo
videos, lectures, podcasts, or support-call exports without adding hosted API
credentials to the project.
335 changes: 335 additions & 0 deletions guides/20260816_offline_transcription_with_sapat_and_daytona.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
---
title: 'Offline Transcription with Sapat and Daytona'
description: 'Run a local faster-whisper transcription workflow for video files inside a reproducible Daytona workspace.'
date: 2026-08-16
author: 'Telemark Digital Publisher'
tags: ['python', 'daytona', 'speech-to-text', 'ai']
---

# Offline Transcription with Sapat and Daytona

# Introduction

Speech-to-text looks simple from the outside: give a tool a video file, get a
transcript back. The hidden work is in the environment. A useful transcription
workflow needs `ffmpeg`, Python dependencies, model files, repeatable command
flags, and a place to test changes without filling your laptop with one-off
packages. That is exactly where a
[Daytona workspace](</definitions/20240819_definition_daytona workspace.md>)
helps.

This guide walks through a local, offline-first transcription setup using
[Sapat](https://github.com/nkkko/sapat), a multi-provider speech-to-text CLI.
The workflow uses the proposed Sapat
[faster-whisper provider](https://github.com/nibzard/sapat/pull/76) so audio can
stay inside the workspace instead of being uploaded to a hosted transcription
API. You will create the workspace, install the local provider, transcribe a
video, tune the Whisper settings, and validate the output in a way another
developer can repeat.

![Offline transcription workflow](assets/20260816_offline_transcription_sapat_daytona_architecture.svg)

## TL;DR

- Create a Daytona workspace from Sapat so Python, `ffmpeg`, and project files
live in a reproducible development environment.
- Use Sapat's local `faster_whisper` provider when you want
[local speech-to-text](</definitions/20260816_definition_local_speech_to_text.md>)
without hosted API keys.
- Start with the `small` model on CPU using `int8` compute, then tune model
size, beam size, language, VAD, and prompts for your source material.
- Keep test media small, do not commit private recordings, and document the
exact command that produced each transcript.

## What Sapat Adds to Whisper

Whisper-style transcription engines are powerful, but their raw setup can feel
scattered. You might run one command to extract audio, another to call a model,
another to clean up temporary files, and another to save the transcript where
the rest of your project expects it. Sapat wraps those steps behind one CLI.

The current Sapat project already supports several hosted and local providers,
including Azure OpenAI, Groq, OpenAI-compatible endpoints, WhisperX,
whisper.cpp, Vosk, and Moonshine. The faster-whisper provider adds another local
path. It uses the Python package
[faster-whisper](https://pypi.org/project/faster-whisper/), which exposes a
`WhisperModel` class and runs Whisper inference through CTranslate2. In
practice, that gives developers a provider that can run on CPU with int8
quantization or on GPU with larger models when hardware is available.

The important difference is control. Hosted transcription APIs are convenient,
but they require credentials and may create usage costs. A local provider lets
you keep sensitive audio inside the workspace, pin the dependency stack, and run
the same transcript command during local testing, review, and documentation.

## Prerequisites

You need a few pieces before starting:

- A working Daytona installation.
- Docker available to Daytona.
- Basic familiarity with
[Python](</definitions/20240820_defintion_python.md>) and
[Git](</definitions/20240819_definition_git.md>).
- A short test video or audio file that you are allowed to process.

For the cleanest first run, use a short MP4 or WAV sample under one minute.
Keep real customer calls, internal meetings, medical recordings, and financial
data out of the tutorial. You can validate the workflow with a synthetic clip
or a public-domain sample before moving to private media.

## Step 1: Create a Daytona Workspace

Start from the Sapat repository. If the faster-whisper provider has already
landed in Sapat, use the upstream project:

```bash
daytona create https://github.com/nkkko/sapat --code
```

If you are testing the provider before merge, create the workspace from the
companion branch instead:

```bash
daytona create https://github.com/telemarkdigital-publisher/sapat --code
```

Once the workspace opens, confirm the repository is available:

```bash
pwd
ls
python --version
```

You should see Sapat's `README.md`, `pyproject.toml`, `sapat` package directory,
and `tests` directory. If your workspace starts in another directory, move into
the cloned repository before continuing.

## Step 2: Install Sapat with faster-whisper

Create a virtual environment inside the workspace and install Sapat in editable
mode. The editable install is useful while you test provider changes because the
CLI sees your local source files immediately.

```bash
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[faster-whisper,dev]'
```

On Windows-based workspaces, activate with:

```powershell
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e '.[faster-whisper,dev]'
```

The `faster-whisper` extra installs the local inference engine. The `dev` extra
installs test tooling so you can run the provider tests before trusting the
workflow.

## Step 3: Configure Local Inference

The faster-whisper provider does not need hosted API keys. It does accept
environment variables for model runtime behavior:

| Variable | Suggested first value | Purpose |
| --- | --- | --- |
| `FASTER_WHISPER_DEVICE` | `cpu` | Runs on CPU first for portability. Use `cuda` only when the workspace has GPU access. |
| `FASTER_WHISPER_COMPUTE_TYPE` | `int8` | Keeps CPU memory and compute requirements lower. |
| `FASTER_WHISPER_DOWNLOAD_ROOT` | `.models/faster-whisper` | Stores downloaded models in a project-local cache. |
| `FASTER_WHISPER_VAD_FILTER` | `false` | Turns voice activity detection on only when silence trimming is useful. |
| `FASTER_WHISPER_WORD_TIMESTAMPS` | `false` | Enables word-level timing only when needed. |

Create or update your `.env` file:

```bash
cp .env.example .env
printf '\nFASTER_WHISPER_DEVICE=cpu\n' >> .env
printf 'FASTER_WHISPER_COMPUTE_TYPE=int8\n' >> .env
printf 'FASTER_WHISPER_DOWNLOAD_ROOT=.models/faster-whisper\n' >> .env
```

Model files may download on the first run. That is not a hosted transcription
call, but it is still network activity. In production teams, pre-warm the model
cache in your workspace image or CI environment so runs are faster and more
predictable.

## Step 4: Add a Test Video

Place your test file in a local `samples` directory:

```bash
mkdir -p samples transcripts
cp ~/Downloads/demo-video.mp4 samples/demo-video.mp4
```

If you do not have a test video yet, generate a small synthetic audio file with
your usual media tooling or use a public sample you are licensed to process. Do
not commit the source recording unless it is intentionally public. Add local
media paths to `.gitignore` when needed:

```bash
printf '\nsamples/*.mp4\nsamples/*.wav\ntranscripts/*.txt\n.models/\n' >> .gitignore
```

That keeps the reproducible commands in the repository while leaving private
audio, generated transcript files, and model caches out of version control.

## Step 5: Run the Transcription

Run Sapat with the local provider:

```bash
sapat samples/demo-video.mp4 \
--provider faster_whisper \
--model small \
--language en \
--quality H
```

Sapat handles the media conversion step, calls the selected provider, and writes
a `.txt` transcript next to the input file. The first run may take longer
because faster-whisper downloads the selected model. Later runs reuse the cache.

For a multilingual clip, either pass the expected language code or let Whisper
detect it:

```bash
sapat samples/interview.mp4 \
--provider faster_whisper \
--model medium \
--language es \
--transcription-prompt "Names: Daytona, Sapat, faster-whisper"
```

The prompt is not a chat prompt. It is an initial transcription hint. Use it for
product names, speaker names, acronyms, or technical vocabulary that the model
might otherwise spell incorrectly.

## Step 6: Tune Whisper Settings

Start small and increase complexity only when the transcript needs it:

- Use `--model tiny` or `--model base` for smoke tests.
- Use `--model small` for a better balance between speed and quality on CPU.
- Use `--model medium` or larger when accuracy matters more than runtime.
- Set `FASTER_WHISPER_BEAM_SIZE=1` for faster drafts and `5` for a steadier
final transcript.
- Set `FASTER_WHISPER_VAD_FILTER=true` when long silent sections waste time.
- Set `FASTER_WHISPER_WORD_TIMESTAMPS=true` only if downstream tooling needs
word-level timing.

If you see repeated phrases or hallucinated endings, try a smaller beam size,
turn off conditioning between windows, and include a short initial prompt:

```bash
printf 'FASTER_WHISPER_CONDITION_ON_PREVIOUS_TEXT=false\n' >> .env
sapat samples/demo-video.mp4 \
--provider faster_whisper \
--model small \
--language en \
--transcription-prompt "Short product demo about Sapat in Daytona."
```

The goal is not to chase every possible model flag. The goal is to keep your
workflow reproducible: command, model, environment variables, input file, and
output transcript should all be easy to explain.

## Step 7: Validate the Workflow

Before sharing the setup with teammates, run the local provider test group:

```bash
python -m pytest tests/providers/test_group_d.py -q
```

For the companion provider patch, the focused validation passed with:

```text
38 passed
```

The full Sapat test suite also passed locally:

```text
182 passed
```

After tests pass, inspect the transcript manually. Automated tests prove the
provider calls the right code paths, but humans still need to check names,
domain terms, timestamps, and whether the transcript is suitable for its next
use. A practical review checklist is:

- Does the transcript preserve speaker names and product names?
- Are obvious false starts or repeated phrases acceptable for your use case?
- Did the command use the intended model and language?
- Is the source media excluded from commits when it should remain private?
- Can a teammate reproduce the same run from the README or guide?

## Common Issues and Troubleshooting

**Problem:** The provider is not listed by Sapat.

**Solution:** Confirm the optional package is installed in the active virtual
environment:

```bash
python -m pip show faster-whisper
python -c "from faster_whisper import WhisperModel; print(WhisperModel)"
```

If the import fails, reinstall the extra:

```bash
python -m pip install -e '.[faster-whisper]'
```

**Problem:** The first transcription run is slow.

**Solution:** The model is likely downloading or warming up. Set
`FASTER_WHISPER_DOWNLOAD_ROOT` to a stable workspace path and reuse that path
between runs. For demos, start with `tiny`, `base`, or `small`.

**Problem:** CPU transcription runs out of memory.

**Solution:** Use `FASTER_WHISPER_COMPUTE_TYPE=int8`, choose a smaller model,
and process shorter clips. If your Daytona workspace has GPU support, use
`FASTER_WHISPER_DEVICE=cuda` and a compute type appropriate for that hardware.

**Problem:** Words are mostly right, but product names are wrong.

**Solution:** Pass a short `--transcription-prompt` with names, acronyms, and
specialized terms. Keep it factual and compact.

**Problem:** Private audio appeared in a commit.

**Solution:** Remove the file from the commit, add media paths to `.gitignore`,
and rotate any exposed data if the recording contained sensitive information.
For safer collaboration, keep only commands, fixtures, tests, and synthetic
samples in the repository.

## Conclusion

Sapat gives you one CLI for a multi-provider transcription workflow. Daytona
gives that workflow a reproducible place to run. Adding faster-whisper to the
mix creates a strong local option for developers who want Whisper-quality
transcripts without sending source media to a hosted API.

The practical pattern is simple: create the workspace, install the local
provider, configure CPU-friendly defaults, run a small sample, then document the
exact command and validation results. Once that foundation works, you can scale
the same approach to meeting notes, demo videos, lecture archives, podcast
drafts, or any other workflow where transcription quality and environment
repeatability both matter.

## References

- [Sapat repository](https://github.com/nkkko/sapat)
- [Companion faster-whisper provider PR](https://github.com/nibzard/sapat/pull/76)
- [faster-whisper on PyPI](https://pypi.org/project/faster-whisper/)
- [faster-whisper source on GitHub](https://github.com/SYSTRAN/faster-whisper)
- [Daytona documentation](https://www.daytona.io/docs/)
Loading