Thanks for your interest in contributing to Tambourine! This guide will help you get started.
If you find Tambourine useful, consider giving the repo a star — it helps others discover the project and keeps contributors motivated. You can also join the Discord server to connect with the community.
- Rust (use
rustup) - cargo-edit (required for
cargo upgrade;pnpm run updatedepends on this) - Node.js 22+ and pnpm
- Python 3.13+ via uv
- Linux only:
libwebkit2gtk-4.1-dev,build-essential,libxdo-dev,libssl-dev, and other Tauri dependencies
cd server
cp .env.example .env # Add your API keys
uv sync # Install dependencies
uv run python main.py # Start the server (default: localhost:8765)cd app
pnpm install # Install dependencies
pnpm dev # Start Tauri app in dev mode# Install pre-commit (one-time)
uv tool install pre-commit
# Enable git hooks for this repository
pre-commit installAfter installing hooks, pre-commit will automatically run linting, formatting, and type checking on commit. You can also run checks manually:
lychee -v . # Lint markdown files for broken links and spelling errorspnpm lint # Biome linting with auto-fix
pnpm typecheck # TypeScript type checking
pnpm knip # Detect unused code
pnpm check # Run all checks (lint, typecheck, knip, test, cargo)uv run ruff check --fix # Linting with auto-fix
uv run ruff format # Code formatting
uv run ty check # Type checkingcargo clippy --all-targets --all-features --locked # Linting
cargo fmt # FormattingOr use the pnpm wrapper from the app directory:
pnpm cargo:clippy
pnpm cargo:fmt
pnpm cargo # Run all Rust checkshadolint server/Dockerfile turn-server/Dockerfile # Dockerfile lintingSuppressed rules live in .hadolint.yaml with a comment explaining why each is ignored. Add to that file rather than disabling inline.
shellcheck turn-server/*.sh # Shell script linting# TypeScript
cd app && pnpm test
# Python
cd server && uv run pytest
# Rust
cd app && pnpm cargo:test
# or: cd app/src-tauri && cargo test --lockedUse descriptive commit messages with a type prefix:
Feat:New featuresFix:Bug fixesChore:Maintenance, dependency updatesDocs:Documentation changesRefactor:Code refactoring without behavior changes
Example: Feat: add support for Azure Speech provider
- Prefer explicit types over raw dicts—make invalid states unrepresentable where practical
- Prefer typed variants over string literals when the set of valid values is known.
- Use exhaustive pattern matching (
matchin Python and Rust,ts-patternin TypeScript) so the type checker can verify all cases are handled - Structure types to enable exhaustive matching when handling variants
- Prefer shared internal functions over factory patterns when extracting common logic from hooks or functions—keep each export explicitly defined for better IDE navigation and readability
Use this as a quick feel for when types are not well utilized.
- Finite value set -> union/enum instead of
string - Mutually exclusive states -> state union/enum instead of many booleans
- Function inputs that represent domain concepts -> use those domain types directly
Under-modeled:
start_session(provider_id: string, is_recording: boolean, is_paused: boolean)
Better modeled:
ProviderId = "deepgram" | "assemblyai" | "whisper"
SessionState = "idle" | "recording" | "paused" | "error"
start_session(provider_id: ProviderId, session_state: SessionState)
Client and server should evolve independently:
- Unknown values: Parse to an explicit
Unknown*variant (neverNone), log at warn level, preserve raw data, gracefully ignore instead of raising exception
- Verbose naming: Variable and function naming should read like documentation
- Strategic comments: Only for non-obvious logic or architectural decisions; avoid restating what code shows
- Prioritize business behavior and user-visible outcomes over implementation details.
- Test our own domain logic (state transitions, message parsing, defaults, fallbacks), not third-party library internals.
- Prefer real typed inputs/outputs and avoid mocking behavior by default.
- Use mocks only when truly necessary, primarily at unstable boundaries (network, filesystem, time, OS integrations) for determinism.
- Keep tests resilient to refactors: assert on externally meaningful behavior, not private call sequences.
Scope: this section applies only to Rust code in app/src-tauri.
- Model expected or handled outcomes as typed variants (enums), not through the error channel.
- Reserve the error channel for unexpected or unrecoverable failures.
- For internal unexpected-failure paths, use
anyhow::Result. - Add
.context(...)or.with_context(...)at fallible boundaries to preserve failure context. - For Tauri command and external interface boundaries, choose the boundary error shape intentionally on a case-by-case basis.
- Keep boundary behavior stable unless a change is intentional and justified in the PR.
- Fork the repository and create a feature branch from
main - Make your changes and ensure all checks pass (
pnpm checkin app, CI will run server checks)- Pre-commit hooks run automatically and will auto-fix most formatting issues and check for linting / type issues
- If a hook fails, you can review the output, stage the auto-fixed files, and commit again
- Write clear commit messages following the conventions above
- Submit a pull request to
mainwith a description of your changes
Be respectful and constructive in all interactions. We're building this together and value contributions of all kinds—code, documentation, bug reports, and feature suggestions.
STT and LLM providers are defined in server/services/provider_registry.py:
- Add enum value to
STTProviderIdorLLMProviderIdinserver/protocol/providers.py - Import the pipecat service class in
server/services/provider_registry.py - Add a provider config entry to
STT_PROVIDERSorLLM_PROVIDERS - Add the environment variable to
.env.example
See existing providers for credential mapper patterns.
The examples/ directory contains pre-built prompt configurations for different domains. Community contributions are welcome.
- Customize your prompts in Settings > LLM Formatting Prompt
- Go to Settings > Data Management and click Export
- Copy the 3
.mdfiles to a new directory inexamples/(use lowercase with hyphens, e.g.,medical-transcription) - Test by importing and performing dictation
Open an issue, start a GitHub Discussion, or join the Discord server.