Skip to content

Latest commit

ย 

History

87 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

locat โ€” a local, fully-offline Pipecat voice bot

WIP but isn't everything?

A voice bot that runs Key-Free & 100% offline. Speech-to-text, the language model, and text-to-speech are all local services, and the "transportโ€ " is your machine's own audio hardware โ€” the microphone and speakers.

Built on the latest Pipecat release (โ‰ฅ 1.7). This repo is meant to double as a clear, reproducible example of how to wire up a fully-local Pipecat bot.

The v1 personality is a private financial thinking partner: something you can talk through money decisions with, out loud, knowing nothing you say leaves the computer.

โ€ Audio is hard and there are a few ways to handle it in this scenario. See "how do you solve a problem like echo cancellation?"


Requirements

  • Apple Silicon Mac recommended โ€” Whisper-MLX (the default STT) uses Apple's MLX framework and only runs there. Intel Macs and Linux work too: they default to CPU STT (faster_whisper) automatically. Windows: not yet (WSL works).
  • ~15 GB free disk for the models
  • Python 3.12 is pinned as 3.14 is too new for the ML wheels.
  • A plain uv sync needs no compiler โ€” everything in the base install comes from a prebuilt wheel. Several deps (onnxruntime, numba/llvmlite, cryptography) have already dropped Intel-mac wheels, so pyproject.toml pins those back to their last Intel-mac release under [tool.uv]. If uv sync ever starts building something from source, run python3 scripts/check_wheels.py to see which platform lost a wheel. The one exception is PyAudio, which has no macOS/Linux wheels โ€” so it is an opt-in extra (--extra local-audio) needed only by the headphones front-end.
  • uv โ€” Python package manager.
  • Ollama โ€” serves the local LLM.
  • PortAudio OR any web browser โ€” PyAudio's native dependency / audio handling. Browser front-ends need neither PortAudio nor a compiler.

Quickstart Setup

Note

The first pull will take a few minutes to download the models.

Browser-based (for echo cancellation):

git clone git@github.com:vipyne/locat.git && cd locat
uv sync
bash scripts/run_ollama.sh
uv run python scripts/prefetch_models.py

Ctrl+C; then turn off wi-fi if you want to show off and then:

./start.sh

Open http://localhost:7860, choose "Media over QUIC", click Connect & have a conversation. (Prefer WebRTC? ./start.sh -t webrtc โ†’ http://localhost:7860/client.)

Or...

PyAudio & headphones (for echo cancellation):

Important

Use headphones ๐ŸŽง

git clone git@github.com:vipyne/locat.git && cd locat
brew install portaudio            # Debian: sudo apt install portaudio19-dev
uv sync --extra local-audio       # the extra adds PyAudio (needs PortAudio)
bash scripts/run_ollama.sh
uv run python scripts/prefetch_models.py

Ctrl+C; then turn off wi-fi if you want to show off and then:

./start.sh -t headphones

Have a conversation.

Setup

1. Clone, install system deps, and sync the environment

git clone git@github.com:vipyne/locat.git && cd locat
uv sync                       # creates .venv and installs everything (Python 3.12)

That is all you need for the browser front-ends. For the headphones front-end (bot.py), PyAudio has to compile against PortAudio, so install it and opt into the extra:

brew install portaudio                # Debian: sudo apt install portaudio19-dev
uv sync --extra local-audio

Note

uv sync uninstalls any extra you don't pass, so keep listing the ones you want: uv sync --extra local-audio --extra piper. (./doctor.sh -i preserves whatever is already installed.)

Optionally copy the config template (everything is optional โ€” the bot runs with an empty or absent .env):

cp env.example .env

2. Fetch the models (the one-time online step)

Four model-backed components need weights. Two download from Hugging Face (anonymously โ€” none are gated); the LLM is pulled by Ollama. Silero VAD and Smart Turn v3 ship inside the Pipecat package, so they download nothing.

All checkpoints are steered into one directory โ€” LOCAT_MODEL_DIR, default ./models/ (gitignored) โ€” so everything the bot needs lives next to the code. Every engine follows it, so you can move the whole lot anywhere:

mv ./models /Volumes/T7/locat-models
echo 'LOCAT_MODEL_DIR=/Volumes/T7/locat-models' >> .env

Absolute paths and ~ both work; relative paths resolve against the repo root, not your shell's cwd. config.py and scripts/model_dir.sh implement the same rules, so Python and the shell scripts always agree.

a) Pull the LLM into the repo's Ollama store:

bash scripts/run_ollama.sh

This relocates Ollama's model store to ./models/ollama, starts ollama serve, pulls the model (qwen2.5:14b by default, ~9 GB), and keeps the server running in the foreground for the bot. Override the model with LOCAT_LLM_MODEL=qwen2.5:7b bash scripts/run_ollama.sh. Leave this running (or re-run it) whenever you use the bot โ€” it's the local LLM server.

b) Prefetch the Whisper + Kokoro weights:

uv run python scripts/prefetch_models.py

Downloads Whisper-MLX (large-v3-turbo, ~1.5 GB) into $LOCAT_MODEL_DIR/huggingface and Kokoro's ONNX model + voices (~350 MB) into $LOCAT_MODEL_DIR/kokoro, and load-checks the bundled Silero VAD + Smart Turn v3 (no download). Run this once, while online; after it finishes the bot can run with Wi-Fi off.

Approximate total download: ~11 GB (9 GB LLM + 1.5 GB Whisper + 0.35 GB Kokoro).

3. Run

Important

Use headphones ๐ŸŽง

With the Ollama server from step 2a running:

uv run bot.py

The bot speaks a short greeting, then listens. Talk to it; it replies through your speakers. Talk over it and it yields (barge-in). Press Ctrl-C to stop.

One-command launch: ./start.sh brings up the repo-local Ollama server (if it isn't already running), prints the exact STT/LLM/TTS models in play, and serves the MoQ browser bot โ€” so you can skip the manual run_ollama.sh in step 2a. Pick a different transport with -t: ./start.sh -t webrtc (browser, SmallWebRTC) or ./start.sh -t headphones (local audio hardware) โ€” see echo cancellation. Not sure what your machine can handle? ./doctor.sh prints recommended STT/LLM/TTS cascades sized to your hardware (add -v for the full hardware profile and per-slot model catalogs ranked by fit, or -i to interactively pick a combo the script sanity-checks against your hardware).

4. Run offline

Once the models are fetched:

  1. Make sure the local Ollama server is running (bash scripts/run_ollama.sh).
  2. Turn off Wi-Fi / enable Airplane Mode. (It won't use the internet if you don't turn off the internet. This is just showing off.)
  3. uv run bot.py and hold a conversation.

With LOCAT_LOG_LEVEL=DEBUG (the default) you can watch the logs and confirm no service reaches out to the network after the warm-up.


How do you solve a problem like echo cancellation

Use headphones

Because reasons, it's much closer to impossible than just impractical to get native macOS AEC (Acoustic Echo Cancellation) to work with pyaudio. Use headphones and the bot won't keep interrupting itself.

Use the web browser's getUserMedia

Another fantastic workaround is to use a browser. Not the internet, just the web browser. Do this and ๐ŸŽ‰, you have echo cancellation.

Two browser transports ship here โ€” same offline brain, different transport. start.sh brings up Ollama and serves a local page (still fully offline โ€” the browser talks to the bot over loopback, no internet):

./start.sh              # MoQ/QUIC โ†’ open http://localhost:7860, pick "Media over QUIC"
./start.sh -t webrtc    # WebRTC   โ†’ open http://localhost:7860/client

No secrets, no keys

There are no API keys anywhere in this project, and there's nowhere to put one:

  • Ollama pulls the LLM from its own public registry and serves it locally.
  • Whisper-MLX, Kokoro, Silero VAD, Smart Turn v3 download anonymously from Hugging Face (none are gated) โ€” or, for Silero/Smart Turn, ship bundled with Pipecat.

.env is config only โ€” model names, a voice, device indices, cache paths. It is gitignored, but nothing secret ever belongs in it. The single network event in the bot's entire lifecycle is the one-time, anonymous model download in step 2.


What's inside

Component Service Notes
Speech-to-text WhisperSTTServiceMLX (default) Apple-Silicon-optimized Whisper via MLX. Alternatives via LOCAT_STT_ENGINE: faster_whisper (CPU), moonshine (tiny CPU ONNX)
Language model Qwen2.5-14B-Instruct via Ollama Local, OpenAI-compatible endpoint; env-configurable
Text-to-speech KokoroTTSService (default) Natural local neural voice (kokoro-onnx). Alternative via LOCAT_TTS_ENGINE: piper
Turn-taking Silero VAD + Local Smart Turn v3 Barge-in / interruptions, fully local (bundled with Pipecat)
Transport LocalAudioTransport PyAudio mic + speaker I/O (requires headphones)
Alternative transports SmallWebRTC / MoQ run in a browser โ†’ free echo cancellation via getUserMedia

Configuration

Every knob is an environment variable (read from .env if present). All are optional โ€” the shown value is the default. See env.example for the copy-paste template.

LOCAT_ means it's ours. Anything read by this repo carries the prefix, so you can tell at a glance what's safe to change and what belongs to someone else. Exactly four variables are external โ€” read by third-party software, keeping their upstream names because renaming them would break the tool that reads them:

External variable Read by
HF_HOME, HF_HUB_DISABLE_PROGRESS_BARS huggingface_hub
OLLAMA_MODELS, OLLAMA_HOST the ollama binary

If you already export one of those globally, it affects locat too. Note that LOCAT_OLLAMA_BASE_URL is ours despite the name โ€” it's the URL the bot dials, not something ollama reads.

Variable Default What it does
LOCAT_LLM_MODEL qwen2.5:14b Ollama model tag. Same string run_ollama.sh pulls and the bot serves. Smaller/faster: qwen2.5:7b.
LOCAT_OLLAMA_BASE_URL http://localhost:11434/v1 OpenAI-compatible Ollama endpoint (note the trailing /v1).
LOCAT_STT_ENGINE whisper_mlx* STT engine services.py builds: whisper_mlx, faster_whisper, or moonshine (uv sync --extra moonshine). *Default is faster_whisper on non-Apple-Silicon machines.
LOCAT_WHISPER_MODEL LARGE_V3_TURBO MLXModel member: TINY, MEDIUM, LARGE_V3, LARGE_V3_TURBO. Must match what you prefetched.
LOCAT_FASTER_WHISPER_MODEL DISTIL_MEDIUM_EN faster-whisper model (when LOCAT_STT_ENGINE=faster_whisper); downloads on first use.
LOCAT_MOONSHINE_MODEL SMALL_STREAMING Moonshine model (when LOCAT_STT_ENGINE=moonshine); downloads on first use.
LOCAT_TTS_ENGINE kokoro TTS engine services.py builds: kokoro or piper (uv sync --extra piper; piper-tts is GPL-3.0).
LOCAT_KOKORO_VOICE af_heart Kokoro voice id (e.g. af_bella, am_michael, bf_emma).
LOCAT_PIPER_VOICE en_US-lessac-medium Piper voice id (when LOCAT_TTS_ENGINE=piper); downloads (~60 MB) on first use into ./models/piper.
LOCAT_INPUT_DEVICE_INDEX (system default) PyAudio mic index.
LOCAT_OUTPUT_DEVICE_INDEX (system default) PyAudio speaker index.
LOCAT_GREETING "Hi. I'm your private, offline financial thinking partnerโ€ฆ" Opening line spoken on startup.
LOCAT_GREETING_DELAY_SECS 1.0 Delay before the greeting (lets the audio-out stream spin up).
LOCAT_LOG_LEVEL DEBUG Loguru level for stderr. DEBUG surfaces each service's activity โ€” handy for the offline check.
LOCAT_WEB_PORT 7860 Port bot_web.py / bot_moq.py serve on (used by ./start.sh).
LOCAT_VAD_CONFIDENCE 0.7 Silero speech-probability threshold (0โ€“1) before audio counts as speech.
LOCAT_VAD_MIN_VOLUME 0.0 Absolute-loudness gate. 0.0 disables it, which keeps turn detection level-independent across mics โ€” raise toward 0.3โ€“0.6 only if a noisy room false-triggers.
LOCAT_VAD_START_SECS 0.2 Sustained speech before "user started speaking".
LOCAT_VAD_STOP_SECS 0.2 Sustained silence before "user stopped speaking".
LOCAT_MODEL_DIR ./models The one directory every model downloads into โ€” HF cache, Kokoro, Piper and Ollama all hang off it. Absolute, ~, or relative-to-repo. Move it to relocate everything at once.
HF_HOME [external] $LOCAT_MODEL_DIR/huggingface Hugging Face cache root (Whisper-MLX, faster-whisper, Moonshine). Set only to split HF out of the shared dir. Advanced.
HF_HUB_DISABLE_PROGRESS_BARS [external] 1 Silences HuggingFace download progress bars, which otherwise clutter the bot's logs. Advanced.
LOCAT_KOKORO_MODEL_PATH $LOCAT_MODEL_DIR/kokoro/kokoro-v1.0.onnx Kokoro ONNX model path. Advanced.
LOCAT_KOKORO_VOICES_PATH $LOCAT_MODEL_DIR/kokoro/voices-v1.0.bin Kokoro voices bundle path. Advanced.
LOCAT_PIPER_DOWNLOAD_DIR $LOCAT_MODEL_DIR/piper Where Piper voices download. Advanced.
OLLAMA_MODELS [external] $LOCAT_MODEL_DIR/ollama Ollama store location (used by run_ollama.sh). Use an absolute path if you set it โ€” ollama resolves relative paths against its own cwd. Advanced.
OLLAMA_HOST [external] 127.0.0.1:11434 Host the Ollama server binds to (used by run_ollama.sh). Advanced.

Changing LOCAT_LLM_MODEL swaps which local model answers; changing LOCAT_KOKORO_VOICE changes the voice you hear.


Repository layout

All three bots share one offline brain (the same STT โ†’ VAD โ†’ LLM โ†’ TTS pipeline); they differ only in the transport. The STT/LLM/TTS services themselves are built in services.py, dispatched on LOCAT_STT_ENGINE / LOCAT_TTS_ENGINE โ€” so swapping engines (via .env or ./doctor.sh -i) never touches a bot file you may have customized.

locat/
โ”œโ”€โ”€ bot.py                    # CLI / headphones โ€” LocalAudioTransport
โ”œโ”€โ”€ bot_web.py                # browser / speakers โ€” SmallWebRTC (free echo cancellation)
โ”œโ”€โ”€ bot_moq.py                # browser / speakers โ€” MoQ over QUIC (lower latency)
โ”œโ”€โ”€ services.py               # STT/LLM/TTS builders, engine-dispatched (LOCAT_STT_ENGINE / LOCAT_TTS_ENGINE)
โ”œโ”€โ”€ config.py                 # env-driven settings, zero-config defaults
โ”œโ”€โ”€ spoken_text_filter.py     # TTS filter: "$3,000" โ†’ "three thousand dollars"
โ”œโ”€โ”€ prompts/
โ”‚   โ””โ”€โ”€ financial_advisor.py  # the v1 system prompt
โ”‚
โ”œโ”€โ”€ start.sh                  # one command: bring up Ollama + run the bot (-t moq|webrtc|headphones)
โ”œโ”€โ”€ doctor.sh                 # what can this machine handle? (-v full report, -i model picker)
โ”œโ”€โ”€ stop.sh                   # stop the background Ollama server
โ”‚
โ”œโ”€โ”€ scripts/
โ”‚   โ”œโ”€โ”€ model_dir.sh          # resolves LOCAT_MODEL_DIR for the shell scripts
โ”‚   โ”‚                         #   (config.py does the same for Python)
โ”‚   โ”œโ”€โ”€ run_ollama.sh         # relocate Ollama store + serve + pull the LLM
โ”‚   โ”œโ”€โ”€ prefetch_models.py    # one-time online warm-up (Whisper + Kokoro)
โ”‚   โ”œโ”€โ”€ check_wheels.py       # tripwire: every dep must have a wheel per platform
โ”‚   โ”œโ”€โ”€ check_audio.py        # diagnostic: raw mic input level meter
โ”‚   โ””โ”€โ”€ check_vad.py          # diagnostic: Silero VAD confidence/volume vs thresholds
โ”‚
โ”œโ”€โ”€ ralph/                    # the "ralph loop" that built this repo
โ”‚   โ”œโ”€โ”€ ralph.sh              #   autonomous agent runner
โ”‚   โ”œโ”€โ”€ PROMPT.md             #   per-iteration instructions for the loop
โ”‚   โ”œโ”€โ”€ RALPH.md              #   operator runbook for the loop
โ”‚   โ””โ”€โ”€ PLAN.md               #   the approved build plan the loop followed
โ”‚
โ”œโ”€โ”€ MODELS_TO_ADD.md          # engines considered but not (yet) wired โ€” and why
โ”œโ”€โ”€ env.example              # documented config knobs (copy to .env)
โ”œโ”€โ”€ .python-version           # 3.12
โ”œโ”€โ”€ pyproject.toml            # uv project + pinned deps
โ”œโ”€โ”€ uv.lock                   # locked dependency versions
โ”‚
โ””โ”€โ”€ models/                   # ALL checkpoints live here โ€” $LOCAT_MODEL_DIR,
    โ”‚                         # relocatable (gitignored; created by setup)
    โ”œโ”€โ”€ huggingface/          # Whisper-MLX + faster-whisper + Moonshine (HF cache)
    โ”œโ”€โ”€ kokoro/               # Kokoro onnx + voices
    โ”œโ”€โ”€ piper/                # Piper voices (if LOCAT_TTS_ENGINE=piper)
    โ””โ”€โ”€ ollama/               # Ollama LLM store

Roadmap

v1 is conversation only; the repo is structured so later capabilities layer in cleanly, each its own build cycle:

  1. create a fun custom frontend for the browser versions.
  2. Document RAG over your own financial files (local embeddings + vector store).
  3. Function-calling tools (compound interest, amortization, savings-goal calculators).
  4. Persistent memory across sessions (local JSON/SQLite).

Not financial advice

The bot is a private thinking partner, not a licensed financial advisor. It has no access to your real accounts and won't invent your numbers. For big, irreversible, or high-stakes decisions, confirm with a qualified professional. Ha, claude wrote this^ when I said I wanted to create a fully offline bot that I could talk to about my personal finances. But yes, always consult a human after consulting a bot.

Emojis

claude did not add enough/any emojis so: ๐ŸŽ‰๐ŸŽŠ๐Ÿฅณ๐ŸŽˆ๐ŸŽ๐ŸŽ€๐ŸŒŸโœจ๐Ÿ’ซโญ๐ŸŒˆ๐Ÿ”ฅ๐Ÿ’ฅโšกโ˜€๏ธ๐ŸŒ™๐ŸŒ›๐ŸŒœ๐ŸŒž๐Ÿช๐ŸŒ๐ŸŒŽ๐ŸŒ๐ŸŒŠ๐Ÿ”๏ธโ›ฐ๏ธ๐ŸŒ‹๐Ÿ—ป๐Ÿ•๏ธ๐Ÿ–๏ธ๐Ÿœ๏ธ๐Ÿ๏ธ๐ŸŒ…๐ŸŒ„๐ŸŒ‡๐ŸŒ†๐Ÿ™๏ธ๐ŸŒƒ๐ŸŒŒ๐ŸŽ†๐ŸŽ‡๐ŸŒ ๐ŸŒ‰๐Ÿ€๐ŸŒฟ๐Ÿƒ๐ŸŒพ๐ŸŒต๐ŸŒด๐ŸŒณ๐ŸŒฒ๐ŸŽ„๐ŸŒฐ๐Ÿ„๐ŸŒป๐ŸŒบ๐ŸŒธ๐ŸŒผ๐ŸŒท๐ŸŒน๐Ÿฅ€๐Ÿ’๐Ÿต๏ธ๐ŸŒŠ๐Ÿ ๐ŸŸ๐Ÿฌ๐Ÿณ๐Ÿ‹๐Ÿฆˆ๐Ÿ™๐Ÿฆ‘๐Ÿฆ๐Ÿฆž๐Ÿฆ€๐Ÿš๐ŸŒ๐Ÿฆ‹๐Ÿ›๐Ÿ๐Ÿž๐Ÿฆ—๐Ÿ•ท๏ธ๐Ÿฆ‚๐Ÿข๐Ÿ๐ŸฆŽ๐Ÿฆ–๐Ÿฆ•๐Ÿ™๐Ÿฆญ๐Ÿฆฆ๐Ÿฆฅ๐Ÿพ๐Ÿ•๐Ÿˆ๐Ÿ‡๐Ÿฟ๏ธ ๐Ÿฆซ๐Ÿฆƒ๐Ÿ”๐Ÿ“๐Ÿฃ๐Ÿค๐Ÿฅ๐Ÿฆ†๐Ÿฆข๐Ÿฆ…๐Ÿฆ‰๐Ÿฆš๐Ÿฆœ๐Ÿ•Š๏ธ๐Ÿง๐Ÿฆ๐Ÿฆฉ๐Ÿฆจ๐Ÿ˜๐Ÿฆ๐Ÿฆ›๐Ÿช๐Ÿซ๐Ÿฆ’๐Ÿฆ“๐Ÿ‚๐Ÿƒ๐Ÿ„๐ŸŽ๐Ÿ–๐Ÿ๐Ÿ‘๐Ÿฆ™๐Ÿ๐ŸฆŒ๐Ÿ•โ€๐Ÿฆบ๐Ÿˆโ€โฌ›๐Ÿฆฎ๐Ÿฉ๐Ÿพ๐ŸŽ๐Ÿ๐Ÿ๐ŸŠ๐Ÿ‹๐ŸŒ๐Ÿซ๐Ÿˆ๐Ÿ’๐Ÿ‘๐Ÿฅญ๐Ÿ๐Ÿฅฅ๐Ÿฅ๐Ÿ…๐Ÿ†๐Ÿฅ‘๐Ÿฅฆ๐Ÿฅฌ๐Ÿฅ’๐ŸŒถ๏ธ ๐Ÿซ‘๐ŸŒฝ๐Ÿฅ•๐Ÿซ’๐Ÿง„๐Ÿง…๐Ÿฅ”๐Ÿ ๐Ÿฅ๐Ÿฅฏ๐Ÿž๐Ÿฅ–๐Ÿฅจ๐Ÿง€๐Ÿฅš๐Ÿณ๐Ÿงˆ๐Ÿฅž๐Ÿง‡๐Ÿฅ“๐Ÿฅฉ๐Ÿ—๐Ÿ–๐ŸŒญ๐Ÿ”๐ŸŸ๐Ÿ•๐Ÿซ“๐Ÿฅช๐Ÿฅ™๐Ÿง†๐ŸŒฎ๐ŸŒฏ๐Ÿซ”๐Ÿฅ—๐Ÿฅ˜๐Ÿซ•๐Ÿฅซ๐Ÿ๐Ÿœ๐Ÿฒ๐Ÿ›๐Ÿฃ๐Ÿฑ๐ŸฅŸ๐Ÿฆช๐Ÿค๐Ÿ™๐Ÿš๐Ÿ˜๐Ÿฅ๐Ÿฅ ๐Ÿฅฎ๐Ÿข๐Ÿก๐Ÿง๐Ÿจ๐Ÿฆ๐Ÿฅง๐Ÿง๐Ÿฐ๐ŸŽ‚๐Ÿฎ๐Ÿญ๐Ÿฌ๐Ÿซ๐Ÿฟ๐Ÿฉ๐Ÿช๐ŸŒฐ๐Ÿฅœ๐Ÿฏ๐Ÿฅ›๐Ÿผโ˜•๐Ÿซ–๐Ÿต๐Ÿงƒ๐Ÿฅค๐Ÿง‹๐Ÿถ๐Ÿบ๐Ÿป๐Ÿฅ‚๐Ÿท๐Ÿฅƒ๐Ÿธ๐Ÿน๐Ÿง‰๐Ÿพ๐ŸงŠ๐Ÿฅ„๐Ÿด๐Ÿฝ๏ธ๐Ÿฅฃ๐Ÿฅก๐Ÿฅข๐Ÿง‚โšฝ๐Ÿ€๐Ÿˆโšพ๐ŸฅŽ๐ŸŽพ๐Ÿ๐Ÿ‰๐Ÿฅ๐ŸŽฑ๐Ÿช€๐Ÿ“๐Ÿธ๐Ÿ’๐Ÿ‘๐Ÿฅ๐Ÿ๐Ÿชƒ๐Ÿฅ…โ›ณ๐Ÿช๐Ÿน๐ŸŽฃ๐Ÿคฟ๐ŸฅŠ๐Ÿฅ‹๐ŸŽฝ๐Ÿ›น๐Ÿ›ผ๐Ÿ›ทโ›ธ๏ธ ๐ŸฅŒ๐ŸŽฟโ›ท๏ธ ๐Ÿ‚๐Ÿช‚๐Ÿ‹๏ธ ๐Ÿคผ๐Ÿคธโ›น๏ธ ๐Ÿคบ๐Ÿคพ๐ŸŒ๏ธ ๐Ÿ‡๐Ÿง˜๐Ÿ„๐ŸŠ๐Ÿคฝ๐Ÿšฃ๐Ÿง—๐Ÿšต๐Ÿšด๐Ÿ†๐Ÿฅ‡๐Ÿฅˆ๐Ÿฅ‰๐Ÿ…๐ŸŽ–๏ธ ๐Ÿต๏ธ ๐ŸŽ—๏ธ ๐ŸŽซ๐ŸŽŸ๏ธ ๐ŸŽช๐Ÿคน๐ŸŽญ๐Ÿฉฐ๐ŸŽจ๐ŸŽฌ๐ŸŽค๐ŸŽง๐ŸŽผ๐ŸŽน๐Ÿฅ๐ŸŽท๐ŸŽบ๐ŸŽธ๐Ÿช•๐ŸŽป๐ŸŽฒโ™Ÿ๏ธ๐ŸŽฏ๐ŸŽณ๐ŸŽฎ๐ŸŽฐ๐Ÿงฉ

About

LOcal pipeCAT bot; works completely offline; no API keys

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages