diff --git a/README.md b/README.md index 3c065bf..f0ca91e 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ The curated channel chatter your bot observes is used only as short-term *contex llm: backend: "google" # google | openai | anthropic api_key: "your-api-key" - model: "gemini-2.5-flash" + model: "gemini-3.1-flash-lite" ``` Any OpenAI-compatible endpoint works for local models β€” point `base_url` at Ollama (`http://localhost:11434/v1`), LiteLLM (`http://localhost:4000/v1`), or Open WebUI. diff --git a/work/Dockerfile b/work/Dockerfile index b359201..f62c587 100644 --- a/work/Dockerfile +++ b/work/Dockerfile @@ -71,7 +71,6 @@ COPY --chown=meshai:meshai meshai/ ./meshai/ # Overwrite with freshly built frontend assets from stage 1 COPY --from=frontend --chown=meshai:meshai /build/meshai/dashboard/static/ ./meshai/dashboard/static/ COPY --chown=meshai:meshai pyproject.toml . -COPY --chown=meshai:meshai README.md . # Reference only: docker-entrypoint.sh writes its own minimal default to # /data/config.yaml on first boot and does NOT read this file. It's shipped # for local/pip installs and anyone who wants the legacy single-file schema diff --git a/work/README.md b/work/README.md deleted file mode 100644 index 5c40839..0000000 --- a/work/README.md +++ /dev/null @@ -1,312 +0,0 @@ -# MeshAI - -**An LLM-powered assistant for LoRa mesh networks β€” on Meshtastic *and* MeshCore, at the same time.** - -MeshAI connects to your mesh, watches network health and the world around it in real time, answers questions over the air, and broadcasts the alerts that matter β€” weather, wildfire, road, seismic, RF, and mesh-health β€” with a full web dashboard to drive it all. - -> ### πŸ€– Built with AI ("vibecoded") -> In the interest of transparency: **MeshAI was vibecoded** β€” designed, built, debugged, and documented in close collaboration with LLM coding assistants. The architecture, most of the implementation, and this README were produced that way. It's a real, running project on a live mesh, but expect the pragmatic style, opinionated shortcuts, and occasional rough edges that come with the territory. Issues and PRs are welcome. - -![MeshAI dashboard](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/dashboard.png) - ---- - -## Highlights - -- **Dual-transport** β€” runs on **Meshtastic** and **MeshCore** simultaneously. Each mesh is first-class: independent connection, routing, and behavior, one shared brain. -- **Conversational bot** β€” DM it "how's the mesh?" or ask about weather, fires, roads, or a specific node, and get a data-driven answer over LoRa. The reply goes back on whichever mesh you asked from. -- **Per-mesh awareness** β€” it watches chat on each mesh separately (rolling short-term memory), so "what's happening on the mesh?" answers about *your* mesh. Private DMs stay private; curated knowledge stays separate. -- **Broadcast intelligence** β€” weather alerts, wildfire updates, road/traffic, seismic, RF/band conditions, and mesh-health notifications, formatted to fit LoRa and routed per-mesh, per-family. -- **Mesh health** β€” a 5-pillar health score with per-region breakdowns, infrastructure monitoring, coverage-gap analysis, and battery/solar tracking (Meshtastic). -- **Web dashboard** β€” a clean React UI to configure every transport, route every message type, watch a live activity feed, browse contacts, and tune the bot β€” no config-file spelunking required. -- **Knowledge base (RAG)** β€” optional hybrid retrieval over a large curated vector store for survival/comms/technical Q&A. -- **Multi-backend LLM** β€” Google Gemini, OpenAI, Anthropic, or any OpenAI-compatible local model (Ollama, LiteLLM, etc.). - ---- - -## The dashboard - -Everything is driven from the web UI, organized into **General**, **Meshtastic**, and **MeshCore** sections β€” each mesh mirrors the other so there's nothing to relearn when you add the second transport. - -**Live activity log** β€” every broadcast, on both meshes, with per-mesh badges and Sent/Skip status: - -![Activity Log](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/activity.png) - -**Per-family routing** β€” decide exactly where each message type goes: broadcast vs. DM, which channel, which recipients β€” independently for each mesh: - -![Routing](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/mt-routing.png) - -**MeshCore contacts & companion** β€” the live roster from your MeshCore companion node, with names, types, last-heard, position, and optional telemetry polling: - -![MeshCore Contacts](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/mc-contacts.png) - -**Data feeds** β€” turn environmental sources on/off and tune thresholds in one place: - -![Data Feeds](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/datafeeds.png) - -**Nodes & health** β€” per-node infrastructure detail: battery, utilization, coverage, neighbors, hardware: - -![Nodes & Health](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/nodes.png) - ---- - -## Quick start - -```bash -git clone https://github.com/zvx-echo6/meshai.git -cd meshai -pip install -e . -cp config.example.yaml config.yaml # then edit config.yaml (or use the dashboard) -meshai -``` - -Or with Docker: - -```bash -mkdir -p meshai/data && cd meshai -curl -O https://raw.githubusercontent.com/zvx-echo6/meshai/main/docker-compose.yml -curl -o data/config.yaml https://raw.githubusercontent.com/zvx-echo6/meshai/main/config.example.yaml -# edit data/config.yaml, then: -docker compose up -d -``` - -The dashboard comes up on `http://localhost:8080`. - ---- - -## Transports - -MeshAI speaks two mesh protocols. **Meshtastic is always the base transport.** **MeshCore turns on automatically the moment you set a MeshCore host** β€” there's no separate on/off toggle to forget. - -### Meshtastic - -Connect over TCP (recommended) or serial: - -```yaml -connection: - type: "tcp" # or "serial" - tcp_host: "192.168.1.100" - tcp_port: 4403 - # serial_port: "/dev/ttyUSB0" -``` - -### MeshCore - -MeshAI attaches to a MeshCore **companion** (the pyMC / MeshCore companion frame server) over TCP and acts as a node on the MeshCore mesh: - -```yaml -connection: - meshcore_host: "192.168.1.253" # blank = MeshCore off - meshcore_port: 5050 -``` - -Once connected, MeshCore gets its own **Connection**, **Routing**, **Scheduled Broadcasts**, **Contacts & Companion**, and **Danger Zones** pages in the dashboard β€” the same capabilities as Meshtastic, using MeshCore's own idioms (channels by name, contacts by pubkey). Messages are sized to fit whichever mesh they go out on. - ---- - -## The conversational bot - -DM MeshAI on either mesh and it answers with the LLM, using live mesh data, environmental feeds, and (optionally) a knowledge base. A few things it's careful about: - -- **Answers on the mesh you asked from.** A MeshCore DM gets a MeshCore reply; a Meshtastic DM gets a Meshtastic reply. Each mesh's "answer DMs" switch is independent. -- **Per-mesh chat memory.** It keeps a short rolling window of recent channel chatter *per mesh* (configurable retention, default 14 days) so "what's happening on the mesh?" reflects the mesh you're on. Ask about the other mesh by name to cross over. -- **Three separate lanes.** Shared channel context, your private DM history, and the curated knowledge base never bleed into each other. -- **LoRa-fit replies.** Responses are chunked to a per-mesh character budget with sentence-aware splitting and continuation prompts. - -### Commands - -Alongside natural-language questions, a set of `!` commands are available (all toggleable, so they can defer to another service like MeshMonitor): - -| Category | Commands | -|----------|----------| -| Mesh | `!health` Β· `!mesh` Β· `!status` Β· `!region [name]` Β· `!neighbors [node]` | -| Weather / RF | `!wx-alerts` Β· `!solar` Β· `!hf` Β· `!satpass` | -| Fire | `!fire` Β· `!hotspots` Β· `!ignitions` | -| Hazards | `!avalanche` Β· `!roads` / `!traffic` Β· `!rivers` / `!gauges` | -| Utility | `!help` Β· `!clear` | - ---- - -## Mesh intelligence (Meshtastic) - -MeshAI continuously aggregates mesh data and computes a **5-pillar health score**: - -| Pillar | Weight | Measures | -|--------|--------|----------| -| Infrastructure | 30% | Router/repeater uptime | -| Utilization | 25% | Channel busyness / RF congestion | -| Coverage | 20% | How many monitoring sources see each node | -| Behavior | 15% | Traffic patterns (noisy/misconfigured nodes) | -| Power | 10% | Battery health of infrastructure nodes | - -Infrastructure nodes are tracked individually (battery, offline alerts, coverage, neighbors, hardware); client nodes coming and going is normal and ignored. Regions are fully configurable β€” local names, aliases, cities, and radius β€” with no hardcoded geography. - -Data comes from one or more **Meshview** instances and a **MeshMonitor** instance, polled on a staggered schedule with built-in rate-limiting: - -```yaml -mesh_sources: - - name: "meshview" - type: meshview - url: "http://192.168.1.100:8080" - enabled: true - - name: "meshmonitor" - type: meshmonitor - url: "http://192.168.1.100:3333" - api_token: "your-bearer-token" - enabled: true -``` - ---- - -## Environmental & hazard feeds - -MeshAI pulls real-time situational data and turns it into LoRa broadcasts and query answers. Sources include **NWS weather alerts**, **NIFC wildfire perimeters**, **NASA FIRMS satellite fire detections**, **USGS earthquakes**, **USGS stream gauges**, **road/traffic (511 / TomTom)**, **NOAA space weather**, and **avalanche/RF-propagation** feeds. - -Everything is switched on/off and tuned from the dashboard's **Data Feeds** page β€” enable a source, set thresholds and geography, and route its output per-mesh on the **Routing** page. Broadcast wording is tightened to fit a single LoRa packet without dropping the important details (e.g. affected towns on a weather alert). - -### Native adapters vs. Central - -Each hazard feed can get its data one of two ways, chosen per-feed with a `feed_source` switch: - -- **`native`** β€” MeshAI fetches the source's public API **directly** (api.weather.gov, NIFC, USGS, NOAA SWPC, NASA FIRMS, TomTom, 511, avalanche centers). Self-contained β€” no extra infrastructure. This is the default and the original data path. -- **`central`** β€” MeshAI subscribes to **Central**, a companion service that pre-aggregates the same hazard data and republishes it as a **NATS JetStream** firehose, so many bots/nodes can share one set of upstream API calls and geo/severity filtering instead of each hammering the source APIs. - -```yaml -environmental: - central: - enabled: true - url: "nats://central.echo6.mesh:4222" # NATS server (tailnet-gated, no auth) - durable: "meshai-consumer" # durable consumer name prefix - region: "us.id" # server-side subject filtering - connect_timeout: 10 - nws: { feed_source: central } # this feed comes from Central … - fires: { feed_source: native } # … this one is fetched directly - # …one feed_source per hazard adapter -``` - -Native and Central are **mutually exclusive per feed** β€” flip any adapter between them independently. Two special cases: **`satpass`** is Central-only (there's no native predictor), and **`ducting`** (VHF tropo) is native-only (no Central equivalent). MeshAI keeps running whether or not Central is up: a **runtime** drop auto-reconnects (durable consumers resume where they left off), and a **startup** outage is logged and retried in the background rather than blocking boot β€” the LLM bot, both transports, mesh-health, and any `native` feeds all come up regardless; only the Central-sourced hazard feeds wait for Central to return. Feeds set to `native` don't depend on Central at all. - ---- - -## Knowledge base (RAG) - -Optional hybrid retrieval for survival, comms, medical, and technical Q&A. - -- **Primary** β€” queries a **Qdrant** hybrid store (dense `bge-m3` + sparse, Reciprocal Rank Fusion) over a large curated vector set, via a networked TEI embedding service. Nothing is copied locally. -- **Fallback** β€” a local **SQLite** knowledge base (FTS5 keyword + `bge-small-en-v1.5` vectors) if the vector service is unreachable. - -```yaml -knowledge: - enabled: true - backend: auto # qdrant | sqlite | auto - qdrant_host: "192.168.1.150" - qdrant_port: 6333 - qdrant_collection: "recon_knowledge_hybrid" - tei_host: "192.168.1.150" - tei_port: 8090 - top_k: 5 -``` - -The curated channel chatter your bot observes is used only as short-term *context* β€” it is never written into the knowledge base. - ---- - -## LLM configuration - -```yaml -llm: - backend: "google" # google | openai | anthropic - api_key: "your-api-key" - model: "gemini-3.1-flash-lite" -``` - -Any OpenAI-compatible endpoint works for local models β€” point `base_url` at Ollama (`http://localhost:11434/v1`), LiteLLM (`http://localhost:4000/v1`), or Open WebUI. - ---- - -## Architecture - -``` - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - Meshtastic ────────▢│ │◀──────── MeshCore - (TCP / serial) β”‚ CompositeTransport β”‚ (companion / pyMC TCP) - β”‚ per-mesh routing + sizing β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β–Ό β–Ό β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Router β”‚ β”‚ Notification β”‚ β”‚ Mesh Data β”‚ - β”‚ LLM / cmd β”‚ β”‚ Pipeline β”‚ β”‚ Store + β”‚ - β”‚ DM gating β”‚ β”‚ weather Β· fire β”‚ β”‚ Health β”‚ - β”‚ per-mesh β”‚ β”‚ road Β· seismic β”‚ β”‚ Engine β”‚ - β”‚ context β”‚ β”‚ RF Β· mesh-healthβ”‚ β”‚ 5-pillar β”‚ - β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ β”‚ - β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ - β”‚ LLM β”‚ β”‚ Knowledge β”‚ β”‚ β”‚ Env / Central β”‚β—€β”€β”€β”€β”€β”€β”˜ - β”‚ backend β”‚ β”‚ Qdrant/FTS5 β”‚ β”‚ β”‚ feed adapters β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–Ό β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Responderβ”‚ ACK-paced, LoRa-fit, - β”‚ + Chunkerβ”‚ routed per mesh - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - Web Dashboard (React) ── configure everything -``` - ---- - -## Running as a service - -```ini -# /etc/systemd/system/meshai.service -[Unit] -Description=MeshAI -After=network.target - -[Service] -Type=simple -User=your-user -WorkingDirectory=/path/to/meshai -ExecStart=/usr/bin/python3 -m meshai -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target -``` - -```bash -sudo systemctl daemon-reload -sudo systemctl enable --now meshai -``` - -Every deployment is designed to survive a reboot; the dashboard's connection settings drive both transports. - ---- - -## Playing nice with other services - -- **advBBS** β€” MeshAI coexists on the same Meshtastic node; BBS protocol traffic (sync, RAP, mail) is auto-filtered (`bot.filter_bbs_protocols: true`). -- **MeshMonitor** β€” MeshAI reads MeshMonitor's auto-responder patterns to avoid duplicate replies, and uses its API as a mesh-intelligence data source. -- **MeshCore companion** β€” MeshAI attaches as its own companion identity so it can share the radio without evicting other companion clients. - ---- - -## Acknowledgments - -- [Meshtastic](https://meshtastic.org/) β€” the mesh platform it started on -- [MeshCore](https://meshcore.io/) & [pyMC](https://github.com/rightup/pyMC_core) β€” the second transport -- [MeshMonitor](https://github.com/Yeraze/meshmonitor) by Yeraze β€” monitoring integration & data source -- [advBBS](https://github.com/zvx-echo6/advbbs) β€” coexistence design -- [Qdrant](https://github.com/qdrant/qdrant) Β· [sqlite-vec](https://github.com/asg017/sqlite-vec) Β· [fastembed](https://github.com/qdrant/fastembed) β€” retrieval stack -- The LLM coding assistants that vibecoded most of this - -## License - -MIT - -## Author - -K7ZVX β€” matt@echo6.co diff --git a/work/README.md b/work/README.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/work/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/work/pyproject.toml b/work/pyproject.toml index 16c56a1..d729d6d 100644 --- a/work/pyproject.toml +++ b/work/pyproject.toml @@ -6,7 +6,6 @@ build-backend = "setuptools.build_meta" name = "meshai" version = "0.1.0" description = "LLM-powered Meshtastic mesh network assistant" -readme = "README.md" license = {text = "MIT"} requires-python = ">=3.10" authors = [