4.0 KiB
lorabot
Bridges a MeshCore companion radio to any OpenAI-compatible LLM endpoint
(e.g. llama-server, vLLM, Ollama). Listens for direct messages on the device, runs each
conversation through the LLM with full per-sender history stored in SQLite, and replies back
over the mesh — split into UTF-8-safe chunks within the MeshCore packet payload limit.
Features
- LLM-backed DMs with persistent per-sender chat history; replies are split across multiple packets when needed.
- Reliable chunked delivery via meshcore's
send_msg_with_retry: each chunk waits for the recipient ACK (correlated byexpected_ackcode) before the next chunk goes out, with a configurable retry budget. - Contact persistence: contacts are mirrored from the device into SQLite at connect and
kept in sync via
NEW_CONTACTevents, so resolution is local and survives a flaky or cleared device contact list. - Inbound deduplication: consecutive identical DMs from the same sender within a short window are dropped (handles radio retransmits and impatient peers).
- Slash commands:
/help,/clear(reset LLM context),/thinking(toggle thinking-mode prompts). - LLM tool calling (when the model supports it): a built-in weather tool (Open-Meteo, no key); web search and URL fetch via Tavily when an API key is set.
- Web UI (read-only by default): live status, conversations, contact list, manual advert
button. Listens on
127.0.0.1:8080by default.
Quick start
python -m venv .venv && source .venv/bin/activate
pip install -e .
cp config.example.toml config.toml
# edit serial_port and [llm] in config.toml
python -m lorabot
Config file path defaults to ./config.toml and can be overridden with LORABOT_CONFIG.
Any field can be overridden via env vars, e.g. LORABOT_LLM__API_KEY=sk-....
The web UI is at http://localhost:8080 while the bot is running.
Layout
- src/lorabot/bot.py — connect, wire collaborators, subscribe to
CONTACT_MSG_RECVandNEW_CONTACT. - src/lorabot/transport.py —
MeshTransport: contact resolution (DB-backed) and reliable chunked sending with per-contact serialization. - src/lorabot/handler.py — DM pipeline: dedup → command dispatch or LLM call → send → persist.
- src/lorabot/commands.py — slash-command parser and registry.
- src/lorabot/llm.py —
AsyncOpenAIwrapper with tool-call loop. - src/lorabot/tools/ — pluggable LLM tools (weather, web search, fetch URL).
- src/lorabot/db.py — SQLite schema and repo functions for conversations, messages, contacts.
- src/lorabot/messages.py — UTF-8-safe byte-length splitting.
- src/lorabot/web.py — aiohttp web UI (status, conversations, contacts, SSE).
- src/lorabot/config.py — TOML + env-var settings (pydantic-settings).
Docker
Build and push a multi-arch image (linux/amd64 + linux/arm64):
docker login registry.example.com # once
export LORABOT_IMAGE=registry.example.com/team/lorabot
./scripts/build-and-push.sh # tags: latest + <git sha>
EXTRA_TAGS="v0.1.0" ./scripts/build-and-push.sh # add explicit version
PUSH=0 PLATFORMS=linux/amd64 ./scripts/build-and-push.sh # local load only
Run via compose (set LORABOT_IMAGE, LORABOT_LLM_BASE_URL, LORABOT_LLM_MODEL,
optionally LORABOT_DEVICE):
export LORABOT_IMAGE=registry.example.com/team/lorabot:latest
export LORABOT_LLM_BASE_URL=http://llama:8080/v1
export LORABOT_LLM_MODEL=llama-3.1-8b-instruct
export LORABOT_DEVICE=/dev/ttyUSB0
docker compose up -d
The container expects config.toml mounted at /etc/lorabot/config.toml and
persists SQLite to a named volume at /data. Any field can still be overridden
via LORABOT_<SECTION>__<KEY> env vars.