Skip to content

Repository files navigation

Voice Formulator — Telegram bot (architecture showcase)

Try the bot on Telegram

CI Python 3.11+ License: CC BY-NC 4.0

Telegram bot that turns a voice message or an audio file into clean, ready-to-send text: speech-to-text, then an LLM "formulator" pass that removes filler and shapes the result according to a chosen mode, output template, and optional translation.

▶ Live bot: t.me/voice_formulator_bot

About this repository. This is a curated, public subset of a production bot, published to show architecture, code structure, and engineering patterns. The billing/payments layer, growth/marketing automation, operator dashboards, and the tuned production prompts have been removed. No secrets or credentials are included — configuration is supplied entirely through environment variables (see .env.example).

Preview

Voice message in, clean ready-to-send text out

Voice message in → clean, ready-to-send text out. The animation source is docs/preview.html (a self-contained HTML/CSS/JS mockup).

Architecture

The app runs two cooperating async tasks from a single process (app/main.py):

  1. an aiogram long-polling dispatcher for the Telegram bot, and
  2. a FastAPI sidecar (app/web) that exposes /health, /ready, and /metrics.

Dependencies are assembled once into an AppServices container and injected into handlers and background services, which keeps wiring explicit and the units testable.

Telegram update
      │
      ▼
app/bot/handlers ── aiogram routers (voice, modes, formats, templates, translations,
      │                              batch, merge, suggestions, help, start, fallback)
      ▼
app/services ───── audio (ffmpeg) → STT (faster-whisper / Groq) → LLM formulator
      │             + ProcessingLimiter (concurrency & per-user queue)
      │             + prompt_builder (system+mode composition, JSON output contract)
      ▼
app/db/repo ────── async SQLAlchemy data layer (users, quota, transcripts,
      │                                          voice requests, events, suggestions)
      ▼
SQLite / PostgreSQL

Layout

Path Responsibility
app/main.py Process entrypoint; builds services, starts polling + web server, graceful shutdown
app/config.py Typed settings via pydantic-settings, loaded from env
app/bot/handlers/ aiogram routers, one module per feature area
app/bot/keyboards.py, texts.py Inline keyboards and user-facing copy
app/services/ Audio decode, STT, LLM routing, concurrency limiter, prompt building, formats/templates/translations
app/db/models.py, repo.py ORM models and the async data-access layer
app/web/ FastAPI app and readiness/metrics checks
tests/ pytest unit tests for the pure logic

Engineering patterns worth a look

  • Provider abstraction with key-pool failover. services/llm.py and services/stt.py rotate over a comma-separated pool of API keys, fall back across models, and place a key on temporary cooldown after 429 responses. The primary LLM model can be pinned or resolved dynamically from a ranking source.
  • Backpressure via a concurrency limiter. services/processing.py bounds concurrent STT jobs, queues waiters, and enforces one active job per user, so a slow transcription can't exhaust resources or let a single user monopolize the worker.
  • Plan-aware quota engine. repo.quota_decision / quota_snapshot grade each request against the user's plan and remaining allowance as a pure function of (user, settings), which makes the rules straightforward to unit-test.
  • Prompt assembly as a contract. services/prompt_builder.py composes a common system prompt with a per-mode or per-template instruction and requires a strict JSON response (status / final_text / reason_code), with explicit prompt-injection hardening (the transcript is treated as data, never instructions). The tuned production prompt text is intentionally omitted from this public version; the assembly structure remains.
  • Result shaping. Output formats (message vs. file) and output templates (todo list, meeting minutes, brief, …) are pluggable transforms over the same pipeline.
  • Observability. /ready aggregates DB/LLM/STT/queue health; /metrics reports queue stats, per-key/per-model call telemetry, and the active model route.

Run locally

Prerequisites: Python 3.11+, ffmpeg, a Telegram bot token, and a Groq (and/or OpenRouter) API key.

python -m venv .venv
. .venv/bin/activate            # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env            # then fill in TELEGRAM_BOT_TOKEN and GROQ_API_KEYS
python scripts/init_db.py
python -m app.main

Health endpoints once running:

  • http://127.0.0.1:8791/health
  • http://127.0.0.1:8791/ready
  • http://127.0.0.1:8791/metrics

Tests

pip install -r requirements-dev.txt
ruff check app tests
python -m pytest

Both commands run in CI on Python 3.11 and 3.12 for every push.

Tech stack

Python 3.11 · aiogram 3 · FastAPI · SQLAlchemy 2 (async) · pydantic-settings · faster-whisper / Groq for STT · Groq / OpenRouter for the LLM · Docker.

License

CC BY-NC 4.0 © 2026 Anton Antonov.

Read it, learn from it, quote it with attribution. Commercial reuse of this code is not granted — the bot it was extracted from is a running commercial product.

About

Telegram bot that turns voice messages into clean, ready-to-send text — STT + LLM formulator. Architecture showcase.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages