Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Artemis

A fully local, sovereign AI assistant platform. No cloud dependency anywhere in the reasoning path — inference, routing, memory, and voice all run on consumer hardware you own, gated behind a private network you control.

Solo-developed, pre-alpha, actively in daily use as its own test harness.

Thesis

LLMs are good at one thing: fuzzy-to-strict semantic processing for open-ended reasoning. Everything else — scheduling, routing, auth, state, history, priority — should run on the most reliable deterministic mechanism available for that job, not be handed to a model to improvise.

This project is an attempt to prove that thesis out in a real system, not just argue it: the LLM is one component in a larger neurosymbolic architecture, not the central core it's usually treated as. Concretely, that means a daemon watching for events never asks a model whether something is alert-worthy — that's a diff, a threshold, a pattern match, in ordinary code. The model's only job, when it's invoked at all, is describing something already decided to matter.

Architecture

frontends (Godot / Kotlin+Compose / Tkinter)
        |
        v  (WebSocket sidecar: register, heartbeat, focus, format)
    mailroom (Rust / axum / tokio)
        |
        |  priority scheduling, session registry, deferred dispatch
        v  (HTTP, shared-secret auth)
      bow (Python / FastAPI)
        |
        |  grammar-constrained routing -> generation
        v
  llama.cpp servers (local inference)
        ^
        |
    daemons (independent processes, POST into mailroom /submit)

mailroom is the front door. It owns priority scheduling (Critical preempts everything else, with requeue-and-resume rather than drop), session identity (client-declared stable designations, not server-assigned connection IDs, so a reconnecting client resumes its lane instead of looking like a stranger), and the delivery model for ObserverAlert traffic — daemon-originated events get resolved against whichever frontend is actually in the foreground before anything is generated, not buffered as finished text waiting for a home. If nothing is in the foreground, the unrealized request waits and gets re-resolved the moment something becomes active again. Full wire protocol — what a frontend actually needs to implement to talk to mailroom — is documented in Core/Mailroom/mailroom_frontend_contract.md.

bow is the compute core. It does grammar-constrained two-pass routing — a constrained decision pass at low temperature to pick a route, then an unconstrained generation pass to actually speak — rather than asking one unconstrained call to both decide and respond. It sits behind mailroom and trusts exactly one caller.

Daemons are independent processes with no shared infrastructure requirement beyond knowing mailroom's /submit URL. The scheduler, buffering, focus-tracking, and per-destination formatting already exist in mailroom, so a new observer is genuinely cheap to add — the shipped example (a live sports-score watcher) is ~200 lines with no auth, session, or delivery logic of its own.

What's live

  • mailroom: priority-tiered scheduling with Critical preemption, designation-keyed sessions, deferred dispatch for alerts, unified streaming delivery for chat and alerts alike, heartbeat + explicit focus tracking, two-tier (refresh/access) JWT auth for frontend connections, shared-secret auth for its own calls into bow.
  • bow: grammar-constrained routing, KERNEL_HALT kill switch checked before every request, secret rotation without restart, timing-safe auth comparison.
  • Four frontends, live-tested, not just designed: Godot desktop, Godot mobile, Kotlin/Compose Android, CustomTkinter desktop. Each keeps one persistent WebSocket to mailroom and receives all streamed tokens — chat and daemon alerts alike — over that same socket.
  • Voice pipeline (Android): custom-trained wake word model → Whisper STT (on-device) → mailroom → streamed response → local TTS → audio out. Runs independent of screen state via a foreground Service, so it works with the phone locked in a pocket.
  • Smart glasses integration: Meta Ray-Ban (DAT SDK) wired through the same pipeline as a display/audio surface, confirmed on real hardware.
  • One daemon shipped: a sports-score watcher polling a public API, diffing state on disk (restart-safe, dedups against last-alerted state), posting real transitions into mailroom.

What's stubbed / not yet wired

Being upfront about this rather than letting the architecture diagram oversell it:

  • Broadcast delivery. RoutingPolicy::Broadcast exists as an enum variant; every policy currently resolves as "active session or nothing." Real broadcast (grouping foreground sessions by declared output format, one generation per group) isn't built.
  • No lane introspection. No endpoint to see what's currently registered or buffered in mailroom — fine for one operator watching logs, worth having once more daemons exist.
  • No provenance signal on daemon traffic. mailroom can't yet tell "this came from a verified daemon" from "this is a fresh user query," which means no automatic skip of redundant work for already-verified facts. Scoped as non-urgent given this never leaves a private network, but it's the most-wanted piece of unbuilt plumbing right now.
  • No adaptive alert priority. Every alert from a given daemon is treated identically regardless of history — no tracking yet of whether a daemon's alerts actually get engaged with.

Repository layout

Artemis/
├── Core/           # The backend proper — nothing else runs without these
│   ├── Mailroom/   # Rust scheduler/router — the front door for all traffic
│   │   └── mailroom_frontend_contract.md  # Wire protocol — start here for a new client
│   └── bow080.py   # Compute core — grammar-constrained routing + generation
├── Services/       # Backends bow actively calls into (search, TTS)
├── Daemons/        # Independent processes that push ObserverAlert traffic
├── Clients/        # Frontends — Android, desktop, Godot-based
└── Legacy/         # Superseded versions, kept for history, not maintained

Core/ groups mailroom and bow together deliberately — they're both infrastructure you can't run the system without, as opposed to Services/ (bow's dependencies), Daemons/ (mailroom's independent event sources), and Clients/ (the things a person actually looks at). If you're tracing a request end to end: it enters through Core/Mailroom, gets dispatched to Core/bow080.py, which may call out to Services/ on the way to a response.

Core/

The two pieces nothing else can run without. Mailroom/ is the Rust scheduler/router covered above, with the wire contract alongside it. bow080.py is the compute core it dispatches to. Neither is optional, and neither is meant to be swapped out casually — this is the one part of the tree that isn't built to be cut.

Services/

Backends bow calls out to mid-turn rather than things that call in: a self-hosted search backend and the TTS daemon. Both are genuinely modular — bow degrades to text-only, search-less operation with a bit of commenting if you don't want to run either, rather than failing outright. Not included by default in the sense that you'll need to point them at your own search index / TTS model — see Models below.

Daemons/

Independent processes that watch something external, decide deterministically whether it's worth surfacing, and POST an ObserverAlert into mailroom's /submit. Zero shared-infrastructure requirement beyond that URL — the shipped example is a live sports-score watcher. Cutting this folder entirely removes zero functionality from chat/voice — daemons are pure addition, not a dependency of anything else in the tree.

Clients/

The frontends: Android (Kotlin/Compose), a Godot-based desktop overlay and mobile port, and a CustomTkinter desktop client. Each is a standalone implementation of the wire contract in Core/Mailroom/ — none of them share code with each other, so picking one to build from and ignoring the rest is a completely normal way to use this repo.

Legacy/

Superseded versions kept for history — an early Ollama-based core and earlier bow revisions. Not maintained, not guaranteed to run against the current mailroom contract, kept only because "how did this used to work" is sometimes worth being able to answer.

Hardware / requirements

Developed and run on a headless Mac Studio (M1 Max, 64GB unified memory), llama.cpp serving local GGUF models, all inter-device traffic over a private mesh network (Tailscale). Nothing here requires that specific hardware — the only real constraint is enough unified memory or VRAM to hold your chosen models, and a network you trust between your devices, since the current auth model assumes a private network as the first line of defense, not the only one.

Models (not included)

No model weights ship in this repo — inference GGUFs and the TTS model are on you to source and point the config at. A few sizing notes from actually running this, not just theory:

  • 8B, Q4 quantization is the verified floor for a single pinned model on an 8GB VRAM setup. This is for pinning one model and staying there — not for coldswapping something larger in and out of that same 8GB.
  • Coldswapping larger models works, including on modest hardware, it's just slower — the swap itself costs real wall-clock time. Fine for an expert lane you hit occasionally; not what you want for anything latency-sensitive.
  • Multi-model pinning (keeping more than one model resident at once, the way this project runs default/coding/analysis/etc. as separate lanes) obviously scales VRAM/unified-memory requirements up from there — the 8B/Q4/8GB number is a single-model floor, not what this project runs day to day.

Status

Pre-alpha, single-operator, live-tested via real manual/adversarial sessions rather than an automated test suite. Treat everything in this repo as a working system under active construction, not a finished product.

This repo is source code, not a packaged install. There's no clean install path yet — no dependency manifest that just works, no setup script, no "clone and run." Getting this running today means reading the code and wiring your own config, model paths, and secrets by hand. A real install path is planned, not abandoned, but isn't here yet.

That said, the system is more modular than its size suggests. Services/ and Daemons/ are both genuinely optional — TTS, search, and any given daemon can be excised with a bit of commenting rather than requiring a surgical rewrite, since none of them are load-bearing for Core/ or for chat itself. If you're trying to understand this codebase, you don't need to understand all of it at once.

About

Lightweight Edge AI Architecture with observers and multiple front ends, including VTuber option.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages