Patter can persist every call to a directory tree on disk so you can replay transcripts, audit tool calls, and track latency/cost trends without running a hosted dashboard. Logging is opt-in and off by default — nothing is written unless you ask for it. The same on-disk layout also backs the local dashboard’s call history: when persistence is enabled,Documentation Index
Fetch the complete documentation index at: https://patter-06b046ce-feat-observability-otel-attrs-0-6-1.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
phone.serve() rebuilds the in-memory dashboard from disk on startup so call history survives process restarts without an external database.
Enable
You can turn persistence on either in code (recommended for application defaults) or via an environment variable (recommended for ops overrides). Both reach the same on-disk layout — see the Configuration reference for the full precedence rules.In code
persist value | Behaviour |
|---|---|
omitted / None (default) | Falls back to PATTER_LOG_DIR; off when env is also unset (backward-compatible). |
False | Force-off, even if PATTER_LOG_DIR is set. |
True | Platform default location (see below). |
"<path>" | Use the supplied path (~ expanded). |
Via env var
SetPATTER_LOG_DIR before starting the server:
auto (and for persist=True):
- macOS:
~/Library/Application Support/patter - Linux:
$XDG_DATA_HOME/patter(falls back to~/.local/share/patter) - Windows:
%LOCALAPPDATA%\patter
persist is unset and the env var is unset, the logger is a no-op — no directories are created, no files are written. When persist is set explicitly, the env var is ignored.
Layout
metadata.json is written atomically (tmp file + rename) so a reader never sees a half-written file. JSONL files are append-only.
Metadata schema
Phone redaction
Caller / callee numbers inmetadata.json are masked by default (last 4 digits). Change via:
transcript.jsonl is not redacted — it can contain customer PII spoken during the call. Gate access to the log root and/or wire up your own redaction pipeline before exporting.
Retention
Old day directories are cleaned up automatically. The sweep runs on ~2% of calls (sampled; no daemon) so a long-running server doesn’t accumulate indefinitely.Dashboard hydration
When persistence is enabled,phone.serve() calls MetricsStore.hydrate(log_root) once at startup so the local dashboard repopulates its 500-call ring buffer from the on-disk envelopes before the first new call lands. There’s nothing to wire up — it just happens.
If you instantiate the store yourself (custom dashboard host, separate process), call hydrate() directly:
hydrate() is idempotent: call_ids already in the store are skipped, and unparseable records are logged at debug level rather than aborting. The on-disk JSON/JSONL files are the source of truth — the in-memory store is a cache on top.
Reading a call
Safety guarantees
- File-write errors never raise into the call path — a full disk or a permissions hiccup logs a warning and the call continues uninterrupted.
- Writes happen on a background thread (
asyncio.to_thread) so they never block the audio pipeline. - When persistence is disabled (
persist=False, orpersistunset andPATTER_LOG_DIRunset),CallLogger.enabledisFalseand every method returns immediately.
Interop
Patter session reports include per-turn STT/LLM/TTS spans, tool invocations, latency metrics, and barge-in events.transcript.jsonl rows slot into OpenTelemetry gen_ai.* turn spans. The TypeScript SDK writes the same schema, so a multi-runtime deployment produces a single coherent directory tree.
