Skip to content

Repository files navigation

image

What is "TL;DR"?

Not every agent response is speech friendly. Responses are often too long, and they frequently include details that are useful on screen but awkward out loud: URLs, file links, markdown, code fences, long lists, and other visual-only context.

TL;DR rewrites that text into a short spoken version, sends it to a configured TTS backend, and streams the audio back as soon as bytes are available.

How It Works

  1. Send agent text to TL;DR.
  2. The summarizer creates a short version of what should be said.
  3. The rewritten text is sent to TTS.
  4. WAV audio streams back to the client and can start playing immediately.

The daemon exposes an HTTP API and an tldr speak helper. Hook scripts are included for common agent harnesses so completions can be spoken automatically.

Requirements

  • Python 3.11+
  • uv
  • ffplay for the streaming playback
  • A summarizer backend with an OpenAI-compatible chat completions API
  • A TTS backend with an OpenAI/MLX-Audio-compatible /audio/speech API, or local MLX audio support on Apple Silicon
  • Docker, only if you want to run the server in a container

Install

Install the CLI from GitHub:

uv tool install git+https://github.com/sagebynature/tldr

From a local checkout:

uv tool install .

For local Apple Silicon MLX audio profiles, install optional extras:

uv tool install 'tldr[mlx,kokoro] @ git+https://github.com/sagebynature/tldr'

Only the tldr command is installed.

Configure

Generate user config:

tldr init-config --profile remote

The generated config is written to:

~/.config/tldr/config.toml

Use --force to replace an existing generated config:

tldr init-config --profile remote --force

For Apple Silicon local MLX defaults:

tldr init-config --profile apple-local

Config lookup order:

  1. --config /path/to/config.toml
  2. ./config.toml
  3. ~/.config/tldr/config.toml
  4. Built-in defaults

The checked-in config.toml uses http://127.0.0.1:9000/v1 for the OpenAI-compatible summarizer backend and remote TTS profile examples. Update base_url, api_key, model, voice, and profile names for your backend.

Useful commands:

  • tldr config-check --config config.toml
  • tldr init-config --profile remote|apple-local [--force]
  • tldr serve --config config.toml
  • tldr health --config config.toml
  • tldr stop --config config.toml
  • tldr install --harness codex|claude|omp|pi|hermes
  • tldr speak [--config config.toml] [--server 127.0.0.1] [--port 9200] [--session_id ID] [--summarize true|false] TEXT...

Run Server

Start the HTTP daemon:

tldr serve --config config.toml

From a development checkout:

uv run python -m tldr serve --config config.toml

The default server listens on:

http://127.0.0.1:9200

OpenAPI docs are available while the server is running:

  • http://127.0.0.1:9200/docs
  • http://127.0.0.1:9200/redoc
  • http://127.0.0.1:9200/openapi.json

Check or stop the daemon:

tldr health --config config.toml
tldr stop --config config.toml

health and stop locate the daemon through state under state_dir. With auto_start = true, commands can start the daemon if no live state is found. Auto-started daemon stdout and stderr go to <state_dir>/daemon.log.

Speak With curl

POST /v1/speak returns streamed audio/wav. This example streams the response directly into ffplay:

curl -sS \
  -H 'Content-Type: application/json' \
  -d '{"text":"The implementation is complete. I updated the README, verified CLI options, and left server configuration unchanged.","summarize":true}' \
  http://127.0.0.1:9200/v1/speak \
  | ffplay -nodisp -autoexit -loglevel error -i pipe:0

Save audio instead:

curl -sS \
  -H 'Content-Type: application/json' \
  -d '{"text":"Speak this after summarizing it.","summarize":true}' \
  http://127.0.0.1:9200/v1/speak \
  --output reply.wav

Request fields:

  • text required, non-empty string
  • metadata optional object
  • summarize optional boolean, default true
  • tts_profile optional named TTS profile
  • summarizer_profile optional named summarizer profile

Headers set caller and session identity:

  • X-TTS-Caller
  • X-TTS-Session-Id

Set "summarize": false to send text directly to TTS.

POST /v1/summarize returns summary-only JSON without generating audio:

{"summary":"Short spoken version.","changed":true}

It accepts text, optional summarizer_profile, and summary override fields word_threshold, max_words, temperature, and max_tokens.

Speak With The CLI

The CLI helper posts to the server with curl and pipes streamed WAV audio to ffplay:

tldr speak --session_id demo "Codex finished the task and the tests passed."

Use a specific server:

tldr speak --server 127.0.0.1 --port 9200 "Read this response out loud."

Skip summarization:

tldr speak --summarize false "Speak this exact text."

--session_id interrupts any previous playback for the same session before starting the new one.

Install A Harness Hook

Install a hook for your agent harness:

tldr install --harness codex

Supported harnesses:

  • codex
  • claude
  • omp
  • pi
  • hermes

Examples:

tldr install --harness claude
tldr install --harness hermes

The installer copies the matching hook into the harness config area and updates harness settings where needed. Hermes installs ~/.hermes/agent-hooks/tldr/hermes_tts.py, touches ~/.hermes/tts.enabled, and updates ~/.hermes/config.yaml. Hooks call the local daemon, so start tldr serve before expecting spoken completions.

Run With Docker

Build and run the daemon with Compose:

docker compose up --build

Compose publishes the service on host port 9200, mounts config, and exposes host model servers to the container:

${TLDR_CONFIG:-./config.docker.toml}:/config/config.toml:ro
9200:9200
host.docker.internal:9000/v1

Use a different config file:

TLDR_CONFIG=./config.toml docker compose up --build

The container runs the TL;DR server only. Summarizer and TTS model servers must be reachable from inside the container.

Development

make run       # uv run python -m tldr serve --config config.toml
make test      # typecheck, then unittest
make typecheck # uv run ty check src tests
make lint      # ruff check src tests --fix
make format    # ruff format src tests
make check     # lint, format, typecheck, test

Use another config while developing:

make run CONFIG=/path/to/config.toml

make lint and make format write fixes.

Logging

The default logging config lives at src/tldr/logging.conf. Use a custom logging config from TOML:

[logging]
config_file = "/path/to/logging.conf"

Custom logging paths expand ~. Auto-started daemon stdout and stderr are written to <state_dir>/daemon.log.

About

Harness-neutral local speech brief daemon for agent responses

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages