90 lines
4.0 KiB
Markdown
90 lines
4.0 KiB
Markdown
# lorabot
|
|
|
|
Bridges a [MeshCore](https://meshcore.io) 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 by `expected_ack` code) 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_CONTACT` events, 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](https://tavily.com) 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:8080` by default.
|
|
|
|
## Quick start
|
|
|
|
```sh
|
|
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](src/lorabot/bot.py) — connect, wire collaborators, subscribe to
|
|
`CONTACT_MSG_RECV` and `NEW_CONTACT`.
|
|
- [src/lorabot/transport.py](src/lorabot/transport.py) — `MeshTransport`: contact resolution
|
|
(DB-backed) and reliable chunked sending with per-contact serialization.
|
|
- [src/lorabot/handler.py](src/lorabot/handler.py) — DM pipeline: dedup → command dispatch
|
|
or LLM call → send → persist.
|
|
- [src/lorabot/commands.py](src/lorabot/commands.py) — slash-command parser and registry.
|
|
- [src/lorabot/llm.py](src/lorabot/llm.py) — `AsyncOpenAI` wrapper with tool-call loop.
|
|
- [src/lorabot/tools/](src/lorabot/tools/) — pluggable LLM tools (weather, web search,
|
|
fetch URL).
|
|
- [src/lorabot/db.py](src/lorabot/db.py) — SQLite schema and repo functions for
|
|
conversations, messages, contacts.
|
|
- [src/lorabot/messages.py](src/lorabot/messages.py) — UTF-8-safe byte-length splitting.
|
|
- [src/lorabot/web.py](src/lorabot/web.py) — aiohttp web UI (status, conversations,
|
|
contacts, SSE).
|
|
- [src/lorabot/config.py](src/lorabot/config.py) — TOML + env-var settings
|
|
(pydantic-settings).
|
|
|
|
## Docker
|
|
|
|
Build and push a multi-arch image (`linux/amd64` + `linux/arm64`):
|
|
|
|
```sh
|
|
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`):
|
|
|
|
```sh
|
|
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.
|