๐Ÿ— KeyzHub
19Keys ยท community archive
4873 bytes raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# jarvis-voice

**Requirements:** macOS, Python 3, `portaudio` (via Homebrew), an ElevenLabs API key + voice ID, and a Telegram bot token/chat ID โ€” full walkthrough in [SETUP.md](SETUP.md).

A wake-word voice loop for talking to your own AI agent out loud, Jarvis-style. Say
"Hey Neo," it answers "Peace god," and from there you talk back and forth until you
say "Peace god" again to end the session.

This is the whole thing in one file: `neo-voice-assistant.py`. No framework, no
server โ€” just a `while True:` loop wired to three pieces:

```
EARS   โ†’   BRAIN            โ†’   MOUTH
mic/STT    your agent            TTS
(local)    (via Telegram)        (ElevenLabs, falls back to macOS `say`)
```

## Why this matters

Most "voice assistant" tutorials give you a wake word and a canned response. This
one routes every question to a real agent โ€” whatever you've already got running
that listens on Telegram (a Claude Code agent, an n8n flow, a cron job with a bot
token, anything) โ€” and speaks back whatever that agent says. The mic and the
speaker are the only new surface area; your actual brain stays wherever it already
lives.

## How it works

1. **Ears** โ€” `speech_recognition` listens on your Mac's mic, transcribes locally
   in short chunks, and watches for a wake phrase ("hey neo," "hey nio," a few
   near-miss variants for STT noise).
2. **Brain** โ€” once activated, everything you say gets POSTed to a Telegram bot as
   `[VOICE] <your text>`, then the script polls `getUpdates` for up to 40 seconds
   waiting for a bot reply in that same chat. Whatever agent is behind that bot
   token is the actual brain โ€” this script doesn't know or care what it is.
3. **Mouth** โ€” the reply is cleaned of markdown, capped to 800 characters, and
   streamed through ElevenLabs' TTS API in your cloned (or chosen) voice. If the
   ElevenLabs call fails for any reason, it falls back to macOS's built-in `say`
   so the loop never goes silent.

Say "peace god," "goodbye," "shut down," or "go to sleep" to end the conversation
and drop back to wake-word listening. Say the wake word again mid-conversation and
it just re-acknowledges you instead of restarting.

## Quickstart

```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

cp env.example .env
# fill in ELEVENLABS_API_KEY, ELEVENLABS_VOICE_ID, NEO_TELEGRAM_BOT_TOKEN, NEO_TELEGRAM_CHAT_ID

python3 neo-voice-assistant.py
```

The script refuses to start if any of the four env vars are missing โ€” it fails
loud instead of silently falling back to something broken.

You'll need:
- A Mac with a working microphone (it auto-picks a "MacBook"/"Built-in" input if
  one exists, otherwise the system default).
- `afplay` and `say`, both stock on macOS โ€” no extra install.
- An ElevenLabs account and API key, plus a voice ID (clone your own voice in
  their dashboard, or use any voice from their library).
- A Telegram bot (via [@BotFather](https://t.me/BotFather)) and the chat ID it
  talks in, wired to whatever agent you want answering.

## Make it your own

- **Swap the brain.** The only contract is "POST a message, poll the same chat for
  a bot reply." Point `NEO_TELEGRAM_BOT_TOKEN` / `NEO_TELEGRAM_CHAT_ID` at any bot
  backed by any agent โ€” the voice loop doesn't need to change.
- **Change the wake word and exit phrases.** Edit the `WAKE_WORDS` and
  `EXIT_WORDS` lists at the top of the file.
- **Swap the personality.** Change what `speak("Peace god...")` says on activation
  and what it says on sign-off โ€” those are the only two hardcoded lines.
- **Go fully local for the mouth.** This version calls ElevenLabs (cloud) with a
  `say` fallback. If you want the whole loop to run offline, replace the body of
  `speak()` with a local TTS call instead of the `httpx.post(...)` block โ€”
  [Chatterbox](https://github.com/resemble-ai/chatterbox) (MIT-licensed voice
  cloning, runs well on Apple Silicon) via
  [mlx-audio](https://github.com/Blaizzy/mlx-audio) is a solid drop-in: clone a
  voice once, generate audio locally with no API key and no per-character cost,
  and keep ElevenLabs (or `say`) as the fallback tier if the local model errors
  out. That gives you the same three-tier fallback chain โ€” local model, cloud
  API, OS built-in โ€” with nothing but the OS `say` command required to always
  work.
- **Change how it listens.** `listen()` uses Google's free Web Speech API through
  `speech_recognition` โ€” swap in a local Whisper model if you want STT offline
  too.

## Files

- `neo-voice-assistant.py` โ€” the full loop: ears, brain routing, mouth.
- `requirements.txt` โ€” `SpeechRecognition`, `httpx`, `PyAudio` (PyAudio needs
  `portaudio` on macOS: `brew install portaudio` before `pip install`).
- `env.example` โ€” the four env vars this script needs, with placeholders (copy to
  `.env`, which is gitignored).