Personal modular AI agent system. FastAPI backend + vanilla JS client. The agent is named Navi — female, loyal, uncensored personal assistant. Runs locally via Ollama.
gemma4:e2b-it-q8_0 (2B active params, Q8 — small but capable)ollama_think: bool = True — model reasoning is enabled and streamed to client.venv/bin/uvicorn navi.main:app --reload --port 8000navi/core/agent.py)Tool-calling loop with llm.complete() for tool turns, llm.stream() for final response. Events yielded: ToolEvent, ThinkingDelta, ThinkingEnd, TextDelta, StreamEnd. Tool schemas built fresh on every run_stream() call from registry + tools/enabled.json.
Two tiers:
Built-in tools (navi/tools/):
web_search, filesystem, http_request, code_exec, terminal, ssh_exec, image_viewwrite_tool — Navi's primary self-extension mechanism (writes + reloads in one call)reload_tools — hot-reload all user tools without server restartlist_tools — returns actual live tool list from registry (Navi calls this when asked what she can do)tool_manual — returns manuals/<tool_name>.md if exists, else auto-generates from schemaUser tools (tools/*.py):
write_tool, or manuallyname, description, parameters, async def execute(params) -> strtools/enabled.json — list of user tool names to auto-include in all profilestools/_template.py — canonical format reference (starts with _, not auto-loaded)Currently created by Navi: get_current_datetime.py, user_notes.py (working, correct format).
navi/profiles/)secretary, server_admin, smart_home. Each has enabled_tools, system_prompt, model, temperature, max_iterations. All profiles have the same built-in tool set: [..., reload_tools, write_tool, list_tools, tool_manual]. User tools from enabled.json are merged in by Agent._tool_list().
navi/config.py → .env)NAVI_PERSONA env var — prepended to every profile's system prompt separated by ---. Contains: personality, self-extension instructions, write_tool usage rules, tool_manual usage.
navi/core/registry.py)ToolRegistry tracks _builtin_names to distinguish builtins from user tools on reload. reload_user_tools() drops all non-builtins and reloads from disk. Built-in tools with registry injection: ReloadToolsTool, WriteToolTool, ListToolsTool, ToolManualTool.
navi/core/sqlite_session_store.py)Persistent SQLite sessions. model_dump(mode='json') required for datetime serialization. Session ID in URL hash for bookmarking.
navi/api/websocket.py)client → server: {type: "message", content: "...", images: [...]}
server → client: stream_start
thinking_delta {delta} ← reasoning chunks (collapsible in UI)
thinking_end
tool_call {tool, args, result, success}
stream_delta {delta}
stream_end {content}
error {message}
client/)ES modules: app.js (state/routing), chat.js (DOM helpers), ws.js (WebSocket), api.js (REST), sidebar.js. Thinking blocks: open during reasoning, auto-collapse on thinking_end, re-openable (like tool cards). Tool cards: accordion, collapsed by default, click to expand. Images: paste/attach, base64 via FileReader, rendered in bubbles. No localStorage — session from URL hash or most recent server session.
navi/tools/loader.py)Tries module-level format first (preferred for user tools), falls back to class-based. Errors isolated per file — one broken file doesn't affect others. Detailed error messages: lists exactly which required definitions are missing.
.env)NAVI_PERSONA="..." # global personality + tool writing rules OLLAMA_HOST=... OLLAMA_DEFAULT_MODEL=gemma4:e2b-it-q8_0 OLLAMA_NUM_CTX=8192 OLLAMA_THINK=true
manuals/)Markdown files, one per tool. tool_manual serves them on demand. Currently: manuals/write_tool.md (full format reference + working example). Auto-generation fallback from tool schema if no .md exists.
write_tool validates code before writing (checks for 4 required definitions)write_tool adds tool to tools/enabled.json on success → available in all profilesrun_stream() entry)tool_manual("write_tool") before writing a toollist_tools when asked about her capabilities (not generate from memory)no-store cache middleware on /static/ — safe to hard-refresh during developmentwrite_tool (improving — model still sometimes struggles with format)tool_manual + explicit format feedback in write_tool errors is the current mitigationlist_tools fixes this if she uses it