forked from enderofwings/NexusOS
174 lines
12 KiB
Markdown
174 lines
12 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## What is NexusOS
|
|
|
|
NexusOS is a local AI assistant platform. It runs a Python/FastAPI backend (Synapse) that interfaces with a locally bundled Ollama instance, a dedicated memory microservice, and a React/Vite frontend. All AI inference runs through Ollama on `localhost`; no external AI provider is configured or called.
|
|
|
|
## Running the Project
|
|
|
|
NexusOS runs **single-process**: the Synapse backend on port 8000 serves the
|
|
built web UI (`interface/web/dist`) itself, so there is no separate Vite server
|
|
at runtime. Ollama is started manually (sidebar **Start AI** / `nexus-cli.sh
|
|
start --ai`), not on backend startup.
|
|
|
|
**Windows (recommended):**
|
|
```powershell
|
|
powershell -ExecutionPolicy Bypass -File .\install-windows.ps1 # one-time native install
|
|
ncp web # memory :8001 + backend :8000 + UI
|
|
```
|
|
|
|
**Linux full stack (dev):**
|
|
```bash
|
|
./launch_nexus.sh
|
|
```
|
|
This activates the `Promethean` venv and starts the memory service on port 8001 and the Synapse backend on port 8000 (which also serves the built UI). It additionally starts a **Vite dev server** for frontend hot-reload — a Linux-dev convenience, unlike the single-process Windows/production path where the backend serves `dist/` alone. It does **not** start Ollama.
|
|
|
|
**macOS (community-supported):**
|
|
```bash
|
|
./install-macos.sh # one-time: Homebrew packages + venv + web build, via bin/sync.py
|
|
./launch_nexus.sh # same script as Linux - it's plain bash, no Linux-only calls
|
|
```
|
|
No bundled Ollama binary (Linux x86-64 only) and no XFCE desktop branding — both
|
|
already no-op on macOS via `bin/sync.py`'s `linux_stage()`. Ollama is instead the
|
|
Homebrew-installed native binary, picked up automatically because
|
|
`OllamaManager` falls back to `ollama` on PATH when the bundled binary is
|
|
absent; that gets full Metal GPU acceleration with no extra config.
|
|
|
|
**Individual services via CLI:**
|
|
```bash
|
|
# From nexus-core/ with Promethean venv active:
|
|
source Promethean/bin/activate
|
|
|
|
# Backend (serves the built UI at :8000 too)
|
|
uvicorn synapse.main:sio_app --host 0.0.0.0 --port 8000 --reload
|
|
|
|
# Memory service
|
|
uvicorn synapse.memory.service:app --host 0.0.0.0 --port 8001 --reload
|
|
|
|
# Frontend DEV server (hot-reload) — only when editing the UI; production is the
|
|
# built dist/ served by the backend. Run `npm run build` to refresh dist/.
|
|
cd interface/web && npm run dev
|
|
```
|
|
|
|
**Management CLI** (`nexus` / `ncp`) — start/stop services with PID tracking, plus
|
|
terminal access to the same features as the web UI (REST API on `:8000`):
|
|
```bash
|
|
./management/nexus-cli.sh start # starts backend + frontend
|
|
./management/nexus-cli.sh stop
|
|
./management/nexus-cli.sh start --backend|-b / --frontend|-f / --memory|-m
|
|
|
|
# Interactive TUI (Hermes/OpenClaw-style; needs pip install 'nexusos-ai[tui]'):
|
|
nexus # bare command opens the Textual chat TUI
|
|
nexus tui # same, explicit
|
|
|
|
# Feature one-shots (dispatch to nexusos_cli/nexus_api.py — httpx):
|
|
nexus chat send "<message>" # stream a reply (POST /chat/stream)
|
|
nexus memory list|add <text>|rm <id>
|
|
nexus playbook list|show <id> # first playbook (*) is the active system prompt
|
|
nexus history [query] # recent conversations
|
|
nexus monitor # ASCII status dashboard (no prompt)
|
|
```
|
|
The interactive TUI lives in `nexusos_cli/tui_app.py` (Textual, optional extra).
|
|
One-shot subcommands and `nexus monitor` remain for scripts. The CLI package is
|
|
`nexusos_cli/` (what the wheel ships); `management/` keeps desktop-only pieces —
|
|
shell wrappers, Tk control panel, XFCE panel wiring.
|
|
`management/controlpanel.py` (tkinter GUI, wired into the XFCE panel via
|
|
`bin/panel/nexus-popup.py`) stays.
|
|
|
|
**Checks (the release gate):**
|
|
```bash
|
|
./bin/check.sh # pytest + eslint + frontend tests + .ps1/.sh parse + wheel build
|
|
```
|
|
There is no hosted CI — the remote is self-hosted Gitea with no act_runner — so
|
|
this script *is* the gate. Run it before tagging a release.
|
|
|
|
**Frontend lint only:**
|
|
```bash
|
|
cd interface/web && npm run lint
|
|
```
|
|
|
|
**Frontend build:**
|
|
```bash
|
|
cd interface/web && npm run build
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Python venv
|
|
All Python code runs inside `Promethean/` (a local venv). Always activate it before running backend commands: `source Promethean/bin/activate`. Dependencies are layered: `requirements-base.txt` holds the GPU-agnostic core, and a thin overlay pins the right PyTorch build for the target — `requirements-amd.txt` (ROCm), `requirements-nvidia.txt` (CUDA, generated by `bin/gen-nvidia-reqs.py`), or `requirements-windows.txt` (CPU-only). `bin/sync.py` (`requirements()`) selects NVIDIA, AMD, CPU/Windows, or — via an explicit `sys.platform == "darwin"` check, since `os.name` alone can't tell macOS apart from Linux — `requirements-base.txt` with no overlay at all for macOS, from the host.
|
|
|
|
### Synapse Backend (`synapse/`)
|
|
FastAPI app at `synapse/main.py`. Key responsibilities:
|
|
- `/chat/stream` — chat with Ollama; streaming uses SSE (the only chat endpoint — the non-stream `/chat` was removed). After each exchange the stream endpoint calls the Memory Service to auto-extract persistent facts.
|
|
- `/playbooks` — CRUD for playbooks stored as YAML files in `data/playbooks/` via `synapse/playbooks/store.py`.
|
|
- `/memory` — CRUD for persistent facts (proxies the same SQLite store as the memory service).
|
|
- `/models` — lists, pulls, and deletes Ollama models by proxying Ollama's HTTP API.
|
|
- `/settings` and `/ollama` — persist runtime settings and control Ollama lifecycle.
|
|
- `/conversations` — persists, retrieves, edits, deletes, and exports full chat history from SQLite.
|
|
- `/icons` — lists local application icons and applies NexusOS branding.
|
|
|
|
**System prompt assembly** (in `main.py` `chat_stream_endpoint`): the final system prompt is built by layering the active playbook instructions → reference playbook context → persistent memory facts → relevant past conversation snippets retrieved by `store.search_conversations`.
|
|
|
|
### Memory Service (`synapse/memory/`)
|
|
A separate FastAPI app on port 8001. `service.py` exposes `/memories/extract` which calls `extractor.py` — an Ollama prompt that decides whether to persist a new fact from a conversation exchange. The main Synapse backend calls this asynchronously after each streaming response. Both services share the same SQLite database (`synapse/memory/memory.db`).
|
|
|
|
### Playbook System (`synapse/playbooks/` + `synapse/playbook_manager.py`)
|
|
Playbooks are ordered records (title, goal, instructions, tags), each persisted as a `{id}.yaml` file in `data/playbooks/` by `PlaybookFileStore` (the dir is `PLAYBOOK_DIR` in `nexus_config.py`). The **first** playbook by order is the active system prompt; all subsequent playbooks are injected as reference context. `PlaybookManager` is the thin class the backend uses to retrieve them and assemble the system prompt.
|
|
|
|
### Ollama (`ollama/bin/ollama`)
|
|
A bundled Ollama binary lives at `ollama/bin/ollama`. `OllamaManager` in `synapse/ollama_manager.py` manages its lifecycle (start/stop/health-check) and selects the best available model. GPU detection uses Vulkan (`vulkaninfo`) to prefer discrete AMD/NVIDIA GPUs. The Ollama HTTP API is at `http://127.0.0.1:11434` (overridable via `OLLAMA_HOST` env var).
|
|
|
|
### Frontend (`interface/web/`)
|
|
React 19 + Vite. No routing library — `App.jsx` manages page state in a single `currentPage` useState. All API calls hit `http://localhost:8000` (configured in `src/config.js`). Built to `dist/` (gitignored) via `npm run build` and served by the backend at `:8000` — the mount is in `synapse/main.py` (`_DIST` at `/`, guarded by `is_dir()`), so `dist/` must be built for the UI to appear. Pages: Chatbot, Playbook editor, Conversation History, Models, Memory, Settings, Logs.
|
|
|
|
### Code Tracks (`synapse/tools.py` + `synapse/code_run.py`)
|
|
|
|
Two separate tools, split by *where the code runs*:
|
|
|
|
- **`render_preview`** — validates markup and returns a fence the chat renders in
|
|
an opaque-origin `sandbox="allow-scripts"` iframe. Nothing executes
|
|
server-side. Languages: `PREVIEW_LANGS` in `synapse/tools.py`, mirrored by
|
|
`interface/web/src/preview/languages.js`.
|
|
- **`run_snippet`** — compiles and runs a single file on the host via
|
|
`synapse/code_run.py`, and returns a ```nexus-run fence carrying the source and
|
|
its captured output. Languages: `RUN_LANGS` in `synapse/code_run.py`, mirrored
|
|
by `interface/web/src/preview/run-langs.js`.
|
|
|
|
Each pair of registries is asserted equal by `tests/test_tools.py` — nothing
|
|
couples them at runtime, so drift fails the check gate instead of silently
|
|
degrading in the chat.
|
|
|
|
`run_snippet` is an **action tool**: `action_tool_policy` gates it (`off` by
|
|
default, `ask` = per-call Approve/Deny in chat). Read the `code_run.py` module
|
|
docstring before touching it — it runs code as the current user and is explicit
|
|
about which of its five layers are load-bearing and which are only a tripwire.
|
|
|
|
### Persistent Storage
|
|
Most data lands in `synapse/memory/memory.db` (SQLite, WAL mode). Tables: memory facts, conversations, messages, app settings. `synapse/memory/store.py` (`PersistentMemoryStore`) owns the schema and all queries. Playbooks are the exception — they live as YAML files in `data/playbooks/` (see Playbook System). `nexus_config.py` defines all paths; it also ensures all required directories exist on import.
|
|
|
|
### Curry (`synapse/curry_core.py` + `synapse/curry_store.py`)
|
|
`curry_core.py` is vendored from [Athena-Pro/Curry](https://github.com/Athena-Pro/Curry), with two deliberate deviations from upstream documented in the file's own docstring (a sandbox-escape fix and a `check_same_thread=False` connection fix) — an immutable, versioned fact store (constants, functions, model registrations, inference provenance) backed by its own SQLite file (`CURRY_DB` in `nexus_config.py`, separate from `memory.db`). `curry_store.py` opens it into a module-level singleton (`curry_db`) at import time — the same pattern as `memory.store.store` / `playbooks.store.playbook_store` — so it's preloaded and callable from anywhere in the backend without extra setup. It ships inside the wheel (`bin/check.sh`'s packaging gate asserts this) and has no external dependencies of its own. Ten `curry_*` tools in `tools.py` expose it to chat (`curry_declare_constant`, `curry_call_function`, etc.); the five that write or execute are ACTION tools in `ALWAYS_ASK_ACTION_TOOLS`, same approval floor as `edit_source`. Re-sync `curry_core.py` from upstream by hand, not by script.
|
|
|
|
### Direct tool invocation (`synapse/slash_commands.py`)
|
|
A chat message that's nothing but `/tool_name(arg=val, ...)` (Python-call-shaped, arguments parsed via `ast.literal_eval` only — no names, no calls, no attribute access) dispatches straight through `tools.dispatch()`, skipping model selection, context assembly, and the ask-policy approval round-trip. A human typing it is the approval. Wired into `chat_stream_endpoint` as an early short-circuit; the TUI's `_handle_slash` falls through to the backend for anything shaped like a tool call that isn't one of its own local meta-commands (`/help`, `/model`, `/new`).
|
|
|
|
### Logs & Runtime State
|
|
- `runtime/backend.log`, `runtime/frontend.log`, `runtime/memory.log` — service stdout
|
|
- `runtime/logs/ollama.log`, `runtime/logs/chat.log`
|
|
- `runtime/pids/backend.pid`, `runtime/pids/frontend.pid` — used by the management CLI
|
|
|
|
## Key Config
|
|
|
|
| Concern | Location |
|
|
|---|---|
|
|
| Ollama host | `OLLAMA_HOST` env var (default `http://127.0.0.1:11434`) |
|
|
| All filesystem paths | `synapse/nexus_config.py` `Settings` class |
|
|
| Frontend API base URL | `interface/web/src/config.js` |
|
|
| Default chat/memory models | `synapse/nexus_config.py` `DEFAULT_CHAT_MODEL` / `DEFAULT_MEMORY_MODEL` |
|
|
| Python dependencies (base) | `requirements-base.txt` |
|
|
| Python dependencies (AMD/ROCm) | `requirements-amd.txt` |
|
|
| Python dependencies (NVIDIA/CUDA) | `requirements-nvidia.txt` (generated by `bin/gen-nvidia-reqs.py`) |
|
|
| Python dependencies (Windows/CPU) | `requirements-windows.txt` |
|