Files
esp32-server/config.yaml
kunthawat 261b0f3e91 feat: wire Opus decode + VAD turn detection to firmware-compatible reply
Device streams continuous 20ms Opus frames with no turn markers. The loop
now decodes Opus, feeds the VAD, fires one turn on end-of-speech, and
replies with the firmware JSON contract (tts.state / stt.text) + Opus audio.

- app/audio/opus.py: RealOpus/Opus16kEncoder/PassthroughOpus; fix add_dll_directory
  to use absolute paths (WinError 87)
- app/xiaozhi/websocket.py: DeviceLoop.run() = decode -> VAD -> run_turn -> reply
- app/config.py: AudioConfig (opus kind + VAD thresholds)
- config.yaml: audio.opus=real, VAD 5/8/3000 frames
- tests/test_app.py: utterance = loud burst + silence; asserts stt + audio + tts stop
- requirements.txt: opuslib>=3

21/21 tests; live handshake verified via zhi.moreminimore.com on venv python.
2026-10-03 20:35:38 +07:00

68 lines
2.2 KiB
YAML

# Hermes-Xiaozhi Bridge configuration.
# All backends default to "mock" so the bridge runs/tests on any machine
# (no GPU, no device, no native libs). Point at real endpoints on the
# voice-server by changing engine + *_endpoint values.
#
# Protocol values marked FIRMWARE are PoC assumptions — verify against the
# real Xiaozhi firmware before locking (CON-004).
host: 0.0.0.0 # LAN reachable (device on 192.168.1.x)
port: 8766
ws_path: /ws/xiaozhi
protocol:
hello_version: 1 # FIRMWARE
opus_sample_rate: 16000 # FIRMWARE
opus_channels: 1 # FIRMWARE
frame_ms: 20 # FIRMWARE
audio:
opus: real # real = decode device Opus | passthrough = raw PCM (tests)
vad_min_speech_frames: 5 # 100 ms of speech before an utterance counts
vad_min_silence_frames: 8 # 160 ms of silence ends the utterance
vad_max_utterance_frames: 3000 # 60 s safety flush
stt:
engine: mock # mock | typhoon | faster-whisper
language: th
sample_rate: 16000
# typhoon_endpoint: http://127.0.0.1:8888
# whisper_model: large-v3
tts:
engine: mock # mock | jaitts
voice: default
device: cuda:0
sample_rate: 24000
# jaitts_endpoint: http://127.0.0.1:8889
hermes:
transport: mock # mock | openai_http
reasoning: none # REQ-004 — voice profile is reasoning-none
tools: [session_search] # REQ-004/REQ-008 — read/recall only
# base_url: http://127.0.0.1:11434/v1
max_context_tokens: 16000
max_history_turns: 12
security:
enabled: true
devices:
# FIRMWARE: Device-Id header = MAC address (xiaozhi v2.5.0), not "xiaozhi-main"
- device_id: "b8:f8:62:e8:62:c0"
token_hash: "00a83e022237248c6841e61daeb58354645f8d2e0f7fa29fb855a0d098c879df"
# OTA onboarding: the device POSTs to CONFIG_OTA_URL (baked into firmware at
# build time) and reads websocket.url/token here. MUST match the firmware
# build's OTA_URL = https://zhi.moreminimore.com/ota
ota:
server_ws_url: "wss://zhi.moreminimore.com/ws/xiaozhi"
server_token: "3d5816c063b19699cdbb9172d54a95f0"
protocol_version: 1
timezone_minutes: 420 # UTC+07:00
firmware_version: "2.5.0-custom"
gpu:
monitor_enabled: true
stt_vram_budget_gb: 2.0
tts_vram_budget_gb: 4.0