- route.ts: replace @anthropic-ai/sdk with groq-sdk, rewrite chat loop
- voice-tool-schemas.ts: convert from Anthropic format to OpenAI/Groq function tools
- voice-history.ts: extend HistoryMessage type to include tool role, simplify cache stubs
No prompt caching (Groq does not support it), tool calling preserved.
Безопасность:
- Rate-limit на /api/voice/chat (20/мин per cookie/IP, env VOICE_RATE_LIMIT).
Защищает от случайных циклов и утечки PIN.
- Усечение user prompt'а до 4000 символов в /api/voice/chat.
- Tool-loop защита от циклов: если LLM дважды просит тот же tool с теми же
args — прерываем (раньше мог уйти в бесконечный цикл при tool error'ах).
Чистка кода:
- lib/debug.ts — vlog/vwarn/verror гейтят браузерные логи за
NEXT_PUBLIC_VOICE_DEBUG=1 (или localStorage 'voice-debug=1').
Серверные console.log оставлены — полезны в Docker logs.
- lib/audio-wav.ts — вынесена дублированная floatToWav из VoiceController.
- Удалены orphan компоненты FocusCard.tsx и CountdownCard.tsx
(не подключены, отвергнуты по UX-фидбеку).
Resilience:
- WakeWordDetector: drop-on-busy в onChunk — на медленных устройствах
(Android, бюджетный CPU) backlog inference больше не копится.
- voice-history fallback на /tmp/voice-history если /data не примонтирован
(локальная разработка / нестандартная конфигурация).
Шаг 1 миграции голосового стека из home-voice-assistant в сам tablet:
- /api/voice/chat — Claude Haiku 4.5 с tool-loop (max 4 раунда), prompt
caching на system + старой истории, история в /data/voice-history/.
Эмитит command/response/error в voice-bus → орб моргает как раньше.
- /api/voice/stt — Groq whisper-large-v3-turbo, multipart или raw audio.
- lib/voice-text.ts — порт clean_for_speech (без pymorphy3, время в
именительном падеже) и strip_fillers + RESET_PATTERNS.
- lib/voice-executors.ts — tool executors через loopback fetch на
существующие /api/voice/tools/* и /api/voice/timer.
- Поддержка ANTHROPIC_PROXY/GROQ_PROXY (fallback на HTTPS_PROXY).
После деплоя нужны GROQ_API_KEY и ANTHROPIC_API_KEY в tablet.env.
Шаги 2 (push-to-talk в браузере) и 3 (wake-word) — отдельно.