AGENTS.md 3.3 KB

AGENTS.md

Always-on speech-to-text daemon plus a separate voice-assistant layer. Python 3.11+ (dev env is 3.13, but requires-python = ">=3.11" and ruff targets py311 — don't use 3.12+ syntax), uv, src/ layout.

Commands

uv sync                                                  # install into .venv
uv run pytest -v                                         # full suite (hardware- and network-free)
uv run pytest tests/test_segmenter.py::test_name -v      # single test
uv run ruff check .                                      # only static check (no mypy/pyright)
uv build                                                 # sdist + wheel (hatchling)

There is no CI, pre-commit, Makefile, or task runner. Run ruff + pytest manually before finishing.

Process architecture (don't break this)

Three processes wired by UNIX sockets under $XDG_RUNTIME_DIR (fallback /tmp):

  • stt-daemon (cli.py) — capture → pre-roll/VAD → segment → transcribe → stdout + JSONL. Publish-only on stt-daemon.sock: it never accepts inbound messages; lifecycle is signals only.
  • stt-assistant (assistant/runner.py) — subscribes to that bus, owns all state (modes, command buffer, chat history, LLM, tools, audit), serves stt-assistant.sock.
  • stt-tui / stt-ctl — clients of the assistant control socket only.

Stateful logic belongs in the assistant. Transcription runs on a single worker thread by design (faster-whisper isn't thread-safe) — do not parallelize it.

stt-daemon with no subcommand runs the daemon (legacy --check / --list-sources still work).

Config

  • Loaded from config.toml in the current working directory only (no env var, no XDG lookup); --config overrides. A missing file means dataclass defaults.
  • config.example.toml is the shipped template (values only); the README config section is the annotated reference. Keep all three in sync when adding a key.
  • Mapping is manual — no pydantic/dynaconf. Adding a key means editing the dataclass and load_config() in config.py.
  • Env vars: STT_DAEMON_STATE (provisioning state; default audio/audio.json in the cwd), XDG_RUNTIME_DIR (sockets), HF_HOME (Whisper cache).
  • Never treat the live config.toml as a source of defaults; it is gitignored user state.

Testing

  • tests/ has 220 tests, all hardware- and network-free. Preserve that: inject fakes instead of touching real pactl, audio devices, or an LLM.
  • Key seams: Daemon(...) accepts capture_factory, vad_factory, transcriber_factory, sink_factory; PulseAudio takes an injectable runner; AssistantService takes time_fn, executor, audit.
  • No conftest.py — per-file fakes are the convention. Tests are the behavioral spec; read them before changing behavior.

Conventions & gotchas

  • Never commit gitignored runtime output: config.toml, transcripts/, audio/, audit/, .omo/.
  • New assistant tools: register in assistant/tools.py. Model-supplied arguments are JSON-parsed and schema-validated before running; set side_effect and requires_confirmation for anything non-read-only.
  • Whisper models must be multilingual — do not use .en variants (defaults auto-detect es/en).
  • .omo/plans/ holds local design plans (gitignored), including the assistant-layer spec.