- Dockerfile: multi-stage build with CGO_ENABLED=0, embedder model copy, non-root user, healthcheck, and /data volume. - docker-compose.yml: mavend + mavweb services with shared volume, health checks, and restart policy. - .gitignore: ignore models/llm/*.gguf, deploy/telegram.env, tmp artifacts. - deploy/mavend.json: add LLM, phraser, voice sections (embedder, model paths, wake sensitivity). Add telegram token env-var expansion. - deploy/telegram.env.example: template for telegram bot token. - internal/config/config.go: add LLM config struct, voice config struct (embedder, llama, wake sensitivity), telegram token loading. - telegramsink: add chat intent delivery support alongside existing types. - voicesink: skip empty payloads in delivery. - dialogue/session: add chat intent to anaphora resolution, test coverage. - AGENTS.md: update with LLM embedder, LFM model download/configure steps, new UI conventions. - REARCH.md: architecture research document. - cmd/mavend/main.go: wire LLM config, phraser, embedder, telegram config, WebAuthn, IPC event/routine handlers, and reactive notes.
4.3 KiB
Maven — Agent Context
Vikunja
This repo maps to Maven (project ID: 2) in Vikunja.
Feature work, bugs, deployment tasks all go here.
MCP endpoint: http://localhost:9100/mcp (or http://192.168.1.104:9100/mcp from workpc)
Rendering / previewing the web UI locally
To see mavweb pages with real data without touching the production stack:
R=/tmp/mvn-preview; mkdir -p $R
go build -o $R/mavend ./cmd/mavend/ && go build -o $R/mavweb ./cmd/mavweb/
cat > $R/mavend.json <<EOF
{ "db_path": "$R/maven.db", "socket_path": "$R/mavend.sock",
"state_dir": "$R", "tick_interval": "10s" }
EOF
$R/mavend -config $R/mavend.json &
$R/mavweb -addr 127.0.0.1:9299 -core $R/mavend.sock &
- No models/voice/phraser config needed — the phraser stub covers it; mavend
runs fine bare. mavweb serves
/,/dash,/history,/trace,/notifications,/tools. - Socket path must be short — unix sockets cap at ~108 chars; a deep tmp
dir fails with
bind: invalid argument. - Seed data through
ipc.Client(internal package — the seeder must live inside the module, e.g. a throwawaycmd/seedtmp/main.go, deleted after):WriteFact,RecordNudge+ResolveNudge,CreateReminder,ProposeTool. /traceis empty until the first tick fires (wait onetick_interval).- Screenshots:
chromium --headless --disable-gpu --screenshot=out.png --window-size=1280,900 --hide-scrollbars --virtual-time-budget=2000 http://127.0.0.1:9299/dash(use--window-size=430,900for the phone/PWA view). Always pass--virtual-time-budget— without it the screenshot can snap mid-layout and silently drop elements (the PWA lang toggle "disappeared" this way).
Embedder model for intent routing
The router uses a multilingual sentence embedder to classify intents and recall
notes. Without it, the floor HashEmbedder is used — deterministic but weak
(Russian recall rarely clears the confidence gate, many commands fall to
"clarify").
Download the embedder (ONNX, ~90 MB):
make download-embedder
This fetches paraphrase-multilingual-MiniLM-L12-v2 (384-dim, 12-layer,
supports 50+ languages including Russian) to models/embedder/.
Also need ONNX Runtime (libonnxruntime.so):
curl -sL "https://github.com/microsoft/onnxruntime/releases/download/v1.15.1/onnxruntime-linux-x64-1.15.1.tgz" | tar xz
sudo cp onnxruntime-linux-x64-1.15.1/lib/libonnxruntime.so* /usr/local/lib/
Configure in deploy/mavend.json:
"voice": {
"embedder": {
"model_path": "models/embedder/model_quantized.onnx",
"tokenizer_path": "models/embedder/tokenizer.json",
"lib_path": "/usr/local/lib/libonnxruntime.so"
}
}
Without the embedder block, the daemon uses HashEmbedder (works, but weak on
Russian recall — you may see many "clarify" responses).
LFM model for router + phraser
The daemon uses a single resident LFM (sub-1B) for both routing (intent
classification + slot extraction) and phrasing (nudges, reminders, reactive
replies). Without it, the StubPhraser + HashEmbedder classifier are used
— deterministic but stiff (canned confirmations, weak Russian recall).
Download the model (GGUF, ~780 MB):
make download-llm
Or manually:
curl -sL "https://huggingface.co/lfm/LFM2.5-1.2B-Instruct-GGUF/resolve/main/LFM2.5-1.2B-Instruct-Q4_K_M.gguf" \
-o models/llm/LFM2.5-1.2B-Instruct-Q4_K_M.gguf
Configure in deploy/mavend.json — the phraser block points at this
model and the daemon spawns llama-server as a subprocess. The router and
replier use the same llama-server via the shared internal/llm client.
Telegram tokens are read from deploy/telegram.env (gitignored), expanded
via ${VAR} in the JSON config.
Routing is now LFM-first with classifier fallback. The LLM router runs after stage-0 (exact-match grammar) and before the classifier cascade. On any error or parse failure, the classifier handles the utterance — the turn never breaks on the model.
Web UI conventions
- All server-rendered pages share
cmd/mavweb/static/ui.css(served at/ui.css) and thenavtemplate partial (navHTMLincmd/mavweb/main.go, invoked as{{template "nav" "<active-page>"}}). New pages must link both — no per-page inline<style>beyond true one-offs. - Wrap every table in
<div class=scroll>so wide data pans on a phone instead of breaking the layout.