This commit is contained in:
valenti
2026-10-17 15:49:20 +01:00
parent 430997bd2e
commit bfed42c3b3
124 changed files with 5646 additions and 249 deletions
+172 -15
View File
@@ -20,6 +20,25 @@ TTS_REFERENCE_AUDIO=/root/pipecat-voice/voice_profiles/solis.wav
# otherwise the raw string is used. Leave empty to auto-load <audio>.txt.
TTS_REFERENCE_TEXT=/root/pipecat-voice/voice_profiles/solis.txt
# --- Drama 3 de-emphasis (TTS_ENGINE=fish) ---
# Drama 3 can sound overly theatrical/recitative. Two knobs trim that register
# without changing the voice reference. Leave a sampling variable EMPTY to omit
# the field and fall back to the server default.
# Sampling temperature sent to SGLang. Lower values reduce over-articulation;
# the recitative "radio drama" register lives in the distribution tails, so a
# lower value makes the delivery flatter. Tunable range: 0.3–0.6.
TTS_TEMPERATURE=0.45
# Nucleus sampling top-p: trims the tail of the acoustic token distribution
# (exaggerated intonations). 0.7–0.8 is the useful range.
TTS_TOP_P=0.75
# Optional top-k. Leave empty to omit the field (server default).
TTS_TOP_K=
# Neutral style tag prepended to every synthesis request that does not already
# start with a "[...]" tag. LLM replies carry no tag (the system prompt forbids
# stage directions), so they get this register; clip banks with their own tag
# are left untouched. Empty disables the prefix.
TTS_STYLE_TAG="[conversational, casual]"
# --- Kokoro (TTS_ENGINE=kokoro) ---
KOKORO_URL=http://localhost:8880
KOKORO_VOICE=af_heart
@@ -71,15 +90,50 @@ FILLER_ENABLED=true
FILLER_DELAY_MS=2500
FILLER_GAIN_IDLE=1.0
FILLER_GAIN_DUCK=0.15
# --- Start filler (turn-start acknowledgment when the reply is late) ---
# A second latency mask, on its own mixer channel: if the first audio chunk of
# the reply has not reached the mixer within START_FILLER_GO_DELAY ms, one of the
# phrases below (picked at random) is played so the agent acknowledges the turn
# while the LLM/TTS are still working. If audio arrives in time the cue is
# dropped. It shares the filler gain, so it ducks out of the way once the reply
# starts.
START_FILLER_ENABLED=true
# How long (ms) to wait for the first audio chunk of the reply before playing the
# start filler (default 1200). Measured from chunk ARRIVAL (not from playback
# start, which would also include the TTS_PREBUFFER_MS jitter buffer). Same idea
# as FILLER_DELAY_MS, but shorter.
START_FILLER_GO_DELAY=500
# Phrases spoken by the start filler, ONE PER LINE (a single line with "|"
# separators also works). One clip is synthesized per phrase and a RANDOM one is
# played when the reply is late, so the cue varies turn to turn. The bank follows
# this list: editing it triggers a full re-synthesis at the next restart (no
# manual deletion needed), phrases dropped here are deleted on disk, and a
# failed synthesis keeps the previous clip. An unchanged list is skipped, so
# startup stays fast. Drama-3 style tags ([calm], [sigh], ...) are passed
# through to the TTS.
START_FILLER_TEXTS="[calm] Un momento...
[thoughtful] A ver, veamos...
[hesitant] Entonces... echemos un vistazo...
[sigh] Vale, ya voy..."
# Directory holding the start-filler clips (kept separate from FILLERS_DIR so
# they are not added to the round-robin filler bank).
START_FILLER_DIR=assets/start_filler
# TTS jitter buffer (ms): how much synthesized audio the mixer buffers before it
# starts playing the reply. Lower values start speaking sooner; higher values
# absorb uneven TTS chunk arrival at the cost of added latency. Drama-3's vocoder
# emits audio in windows of `stream_followup_stride` codec frames (2048 samples =
# 46.4 ms each); with the recommended server setting (stride 16, ramp
# "[10,12,16]") the first window is 10 frames = 0.464 s, so 450 ms starts
# playback on that very first window. Mid-reply underruns resume as soon as any
# audio is queued again (no full refill).
TTS_PREBUFFER_MS=450
# emits audio in windows of codec frames (2048 samples = 46.4 ms each); with the
# recommended server setting (stride 4, followup 8, overlap 16, crossfade 512)
# the first window is 4 frames = 185.8 ms and the steady windows are 8 frames =
# 371.6 ms. The first arrives at the server TTFB (~0.38 s) and the second ~350 ms
# later, so ANY value in (186, 557] ms primes on the same second window (TTFA
# ~0.73 s); 550 ms takes the whole ~557 ms cushion for free. That cushion absorbs
# the drain when several TTS requests share one GPU and produce slower than real
# time (measured ~1.5x concurrent vs ~0.94x alone); without it every chunk seam
# starves the queue and pauses playback (a micro-gap at each sentence start).
# Below ~186 ms playback starts on the first window (TTFA ~= server TTFB) but
# leaves almost no cushion. Mid-reply underruns resume as soon as any audio is
# queued again (no refill).
TTS_PREBUFFER_MS=550
# Number of TTS requests kept in flight at once. Reply chunks are synthesized
# ahead of playback (and replayed in order) so each request's start-up latency
# hides behind the previous chunk's audio instead of leaving an audible gap.
@@ -89,18 +143,98 @@ TTS_PIPELINE_DEPTH=3
# --- Backchannel (short acknowledgments while the user speaks) ---
# Master switch. When disabled, no during-utterance acknowledgment is played.
BACKCHANNEL_ENABLED=true
# Directory of preloaded "*.raw" PCM clips (16-bit LE mono, 44.1 kHz), e.g.
# mmhmm / ajá / claro. Played round-robin.
# Directory of the generated "*.raw" PCM clips (16-bit LE mono, 44.1 kHz). One
# clip is synthesized per phrase of BACKCHANNEL_TEXTS at startup.
BACKCHANNEL_DIR=assets/backchannel
# Phrases spoken by the backchannel, ONE PER LINE (a single line with "|"
# separators also works). One clip is synthesized per phrase and a RANDOM one is
# played, so the cue varies from turn to turn. The bank follows this list:
# editing it triggers a full re-synthesis at the next restart (no manual
# deletion needed), phrases dropped here are deleted on disk, and a failed
# synthesis keeps the previous clip. An unchanged list is skipped, so startup
# stays fast. Drama-3 style tags ([calm], ...) are passed through to the TTS.
#
# A line may start with an optional "category::" prefix (e.g. "agree::..."); the
# scheduler uses it to progress through the turn (an acknowledgment first,
# agreement later) and to avoid chaining two cues of the same kind. Lines
# without a prefix belong to the default "ack" category.
BACKCHANNEL_TEXTS="ack::[calm] Mm-hmm...
ack::[attentive] Ajá...
agree::[agreeing] Claro...
ack::[listening] Ya veo..."
# Mix gain applied to the backchannel channel (0..1). Kept low so the cue stays
# in the background.
BACKCHANNEL_GAIN=0.35
# Continuous speech required (ms) before the first backchannel of a turn.
BACKCHANNEL_GAIN=0.55
# Confirmed speech required (ms) before the first cue may be considered.
BACKCHANNEL_MIN_SPEECH_MS=1500
# Minimum spacing (ms) between two backchannels. Clamped to >= MIN_SPEECH_MS.
# Base spacing (ms) between two cues of the same turn; randomized by ±JITTER/2.
BACKCHANNEL_INTERVAL_MS=3000
# Maximum backchannels emitted per user turn.
BACKCHANNEL_MAX_PER_TURN=2
# Timing jitter (ms): delays the first cue by 0..JITTER after MIN_SPEECH_MS and
# spreads the spacing, so cues don't land on a fixed cadence.
BACKCHANNEL_JITTER_MS=1200
# Micro-pause window (ms): the cue lands only while the user is in a short
# pause. The reaction DEPTH is drawn at random inside [MIN, MAX] per pause, so
# cues don't all land at exactly MIN. Below MIN the user is likely still
# mid-word; above MAX (capped below VAD_MIN_SILENCE_MS) the utterance is about
# to close.
BACKCHANNEL_PAUSE_MIN_MS=96
BACKCHANNEL_PAUSE_MAX_MS=260
# Probability (0..1) of cueing when a qualifying micro-pause occurs.
BACKCHANNEL_PROB=0.65
# Random per-cue volume scaling (± fraction) so cues don't sit at a fixed level.
BACKCHANNEL_VOLUME_JITTER=0.2
# Maximum cues emitted per user turn.
BACKCHANNEL_MAX_PER_TURN=3
# --- Multilingual clip banks (backchannel + filler + start filler) ---
# Every clip bank lives in a per-language subdirectory `<dir>/<lang>` named after
# the ISO 639-1 code (<dir>/<lang>/, e.g. assets/backchannel/es/), including the
# default language LANG_DEFAULT. When the user is detected speaking a different
# language, that language's banks are generated on demand into their own
# subdirectory and the session switches to them. Each language has its own
# directory, so regenerating one never removes the clips of another.
LANG_DEFAULT=es
# Detect the user's language with a dedicated LLM call (a short auxiliary
# request that returns only the ISO code) and switch the banks when it differs
# from the active one. Requires the LLM (LLM_ENABLED=true).
LANG_DETECT_ENABLED=true
# Minimum transcript length (chars) before a sample is sent to the LLM.
LANG_MIN_CHARS=12
# The first user turn is always checked; afterwards the language is re-checked
# once every N turns, so a mid-conversation switch is picked up without paying
# for a detection call on every turn.
LANG_REDETECT_EVERY=5
# Built-in phrase tables cover es/en/it/fr/de/pt. For any other language the
# phrase list is generated by the LLM; set false to keep only the tables.
LANG_LLM_FALLBACK_ENABLED=true
# --- Per-session audit log ---
# Writes one structured JSONL file per WebSocket session: a configuration
# snapshot at the start, every inbound control message and outbound event, the
# cue emissions, the language switches, the barge-ins / interrupts and an
# event-count summary at the end. Useful for post-mortem analysis and for
# tuning latency knobs (FILLER_DELAY_MS, START_FILLER_GO_DELAY, ...). One
# <id>.jsonl file per session, written under SESSION_AUDIT_DIR.
SESSION_AUDIT_ENABLED=true
SESSION_AUDIT_DIR=logs/sessions
# --- External audio source (URL / radio / YouTube) ---
# Injects an audio stream — a direct media URL, an Icecast/Shoutcast radio or a
# YouTube link — into the mixer's own "external" channel, mixed under the
# agent's voice. Demonstrated in the /mivocero client (URL box + Play/Stop +
# volume slider). Decoding is delegated to external tools:
# * ffmpeg — decodes direct URLs, radio and HLS streams (required);
# * yt-dlp — resolves YouTube URLs to a decodable stream (YouTube only).
# A barge-in (interrupt) stops the agent but leaves this background source
# playing; the external channel does not count as the agent speaking.
# Gain applied to the external channel (kept below 1 so it sits under the voice).
EXTERNAL_AUDIO_GAIN=0.6
# Override the decoder binaries if they are not on PATH.
MEDIA_FFMPEG_BIN=ffmpeg
MEDIA_YTDLP_BIN=yt-dlp
# Extra yt-dlp arguments (space-separated), e.g. to pass cookies or a JS
# runtime: "--cookies /path/cookies.txt". Leave empty normally.
MEDIA_YTDLP_ARGS=
# --- LLM reasoning (OpenAI-compatible chat-completions endpoint) ---
# When enabled, the user transcript is sent to the model and the reply is
@@ -143,12 +277,35 @@ LLM_DIRECT_ANSWERS=true
# word-boundary flush (chunks then end on sentence punctuation only).
LLM_STREAM_FIRST_CHUNK_CHARS=20
# Same threshold for every chunk after the first. Larger chunks give better
# prosody; smaller ones keep the audio flowing while the model is generating.
LLM_STREAM_CHUNK_CHARS=120
# prosody and fewer TTS requests: each chunk is a separate request with its own
# start-up and its own audio seam, so a larger value cuts the number of seams
# (where the TTS queue can starve) per reply.
LLM_STREAM_CHUNK_CHARS=240
# Optional system prompt override. Leave unset for the built-in voice-assistant
# prompt (see `DEFAULT_LLM_SYSTEM_PROMPT` in src/config.rs).
#LLM_SYSTEM_PROMPT=
# --- Tool calling (configurable HTTP tools) ---
# Lets the model call external HTTP services (weather, home automation, a
# webhook, …) before answering. The deployment's vLLM server runs without a
# tool parser, so it returns the call as text in the Qwen native
# `<tool_call>` format: the engine parses it client-side, executes the HTTP
# request and hands the result back to the model (see src/tools.rs). The
# tool-call block is never spoken nor shown in the chat box.
# Enabled automatically when LLM_TOOLS_FILE points to a valid tool file.
LLM_TOOLS_ENABLED=false
# JSON file with the tool definitions (name, description, JSON schema, url,
# method, headers). See tools.example.json.
LLM_TOOLS_FILE=
# Maximum tool-call rounds per turn (call → execute → re-prompt). Bounds
# runaway loops; the first round is the model's initial reply.
LLM_TOOL_MAX_ITERATIONS=4
# Timeout of a single tool HTTP request, in seconds.
LLM_TOOL_TIMEOUT_SECS=15
# Tool responses are truncated to this many characters before being handed
# back to the model (keeps the context small).
LLM_TOOL_RESULT_MAX_CHARS=2000
# --- HTTPS (optional) ---
# Set BOTH to serve HTTPS/WSS instead of plain HTTP. A secure context is
# required by browsers to grant microphone access (getUserMedia) when the page