From 4a58b83acc86155caa03f81d94e4310d5d1f84d7 Mon Sep 17 00:00:00 2001 From: chengyongru <61816729+chengyongru@users.noreply.github.com> Date: Wed, 10 Jun 2026 00:36:22 +0800 Subject: [PATCH] docs: make onboarding friendlier for beginners (#4177) * docs: make onboarding friendlier for beginners * docs: build clearer documentation paths Maintainer edit: turn the onboarding follow-up into a layered docs structure for first-time setup, provider selection, troubleshooting, CLI reference, and source-level architecture. This keeps quick start focused while giving advanced users precise reference paths. * docs: render architecture flow with mermaid Maintainer edit: replace the ASCII architecture sketch with a GitHub-rendered Mermaid flowchart so the core runtime path is easier to scan in the PR and README docs. * docs: recommend model presets for model config Maintainer edit: make named modelPresets the primary model configuration path and expand fallback preset examples so string fallbacks are clearly preset names, not raw model IDs. * docs: document api base urls and langfuse setup Maintainer edit: explain when users need apiBase/base URL in quick start and provider docs, and add Langfuse tracing setup with troubleshooting links. * docs: use python module pip consistently Maintainer edit: keep install commands tied to the active Python interpreter by using python -m pip in the Azure optional dependency notes too. * docs: add non-technical getting started path Maintainer edit: add a wizard-first guide for users without terminal or JSON background, including a text TUI menu example and links from the main docs entrypoints. * docs: avoid hard-wrapped prose in user docs Maintainer edit: unwrap ordinary prose across user-facing documentation while preserving markdown structure, code blocks, tables, lists, and prompt/template files. * docs: keep desktop list continuations nested Maintainer edit: preserve list nesting after unwrapping prose in the desktop WebUI sync guide. * docs: add one-command installer Maintainer edit: add auditable macOS/Linux and Windows install scripts that install nanobot-ai and start the onboarding wizard, then document the commands in the main onboarding entrypoints. * docs: add installer dry run mode Maintainer edit: add --dry-run to the one-command installer scripts so users can preview Python detection, install source, pip command, and wizard behavior without changing their environment. * docs: clean installer error output Maintainer edit: make PowerShell installer failures print a concise Error: message instead of Write-Error call-site details. * docs: add provider setup cookbook Maintainer edit: add pasteable provider recipes for common hosted, local, fallback, runtime switching, and Langfuse setups, then link the cookbook from onboarding and troubleshooting entrypoints. * docs: address review feedback * docs: clarify reader paths * docs: explain terminal basics for beginners * docs: clarify wizard navigation * docs: avoid duplicate onboarding steps * docs: add setup status check * docs: explain status output * docs: remove provider recommendation wording * docs: explain status diagnostics * docs: reduce hard-wrapped guidance * docs: migrate config examples to presets * docs: clarify python command fallbacks * docs: improve installer failure recovery * docs: expand install troubleshooting * docs: cover installer download failures * docs: put stable install paths first * docs: add bundled webui quick path * docs: clarify provider-neutral setup * docs: clarify gateway setup for chat surfaces * docs: improve docs navigation paths * docs: add configuration quick jump * docs: clarify provider secret variables * chore: request PR review acknowledgement Empty commit: please read the PR review comments and reply on the PR to confirm that you have received them. This commit intentionally changes no files; it exists only to notify the remote Codex run so it can end its active goal. * docs: add README start here guide * docs: avoid provider recommendation wording * docs: guide next steps after first reply * docs: explain merging JSON snippets * docs: add CLI command chooser * docs: add configuration task map * docs: add deployment readiness guide * docs: simplify WebUI entry paths * docs: add provider recipe chooser * docs: fix provider factual references Update OpenRouter and LongCat model examples, align Bedrock guidance, and make fallback snippets schema-valid. Also correct group policy wording and image-generation provider lists to match the current code. * fix: keep PowerShell installer from closing caller shell * docs: mention self-guided configuration --- README.md | 148 ++++++- desktop/README.md | 52 +-- desktop/docs/development.md | 33 +- desktop/docs/host-contract.md | 39 +- desktop/docs/webui-sync.md | 22 +- docs/README.md | 120 +++-- docs/architecture.md | 211 +++++++++ docs/channel-plugin-guide.md | 6 +- docs/chat-apps.md | 84 ++-- docs/chat-commands.md | 23 +- docs/cli-reference.md | 182 +++++++- docs/concepts.md | 151 +++++++ docs/configuration.md | 492 +++++++++++++++------ docs/deployment.md | 27 ++ docs/development.md | 27 +- docs/image-generation.md | 8 +- docs/multiple-instances.md | 16 +- docs/my-tool.md | 3 +- docs/openai-api.md | 7 +- docs/provider-cookbook.md | 443 +++++++++++++++++++ docs/providers.md | 446 +++++++++++++++++++ docs/python-sdk.md | 10 +- docs/quick-start.md | 325 +++++++++++--- docs/start-without-technical-background.md | 431 ++++++++++++++++++ docs/troubleshooting.md | 266 +++++++++++ nanobot/templates/AGENTS.md | 10 +- nanobot/templates/HEARTBEAT.md | 6 +- nanobot/templates/agent/tool_contract.md | 5 +- scripts/install.ps1 | 163 +++++++ scripts/install.sh | 129 ++++++ webui/README.md | 60 ++- 31 files changed, 3491 insertions(+), 454 deletions(-) create mode 100644 docs/architecture.md create mode 100644 docs/concepts.md create mode 100644 docs/provider-cookbook.md create mode 100644 docs/providers.md create mode 100644 docs/start-without-technical-background.md create mode 100644 docs/troubleshooting.md create mode 100644 scripts/install.ps1 create mode 100755 scripts/install.sh diff --git a/README.md b/README.md index ab0aa43cc..2d76f48be 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,17 @@ 🐈 **nanobot** is an open-source, ultra-lightweight personal AI agent you can truly own. It keeps the agent core small and readable while giving you the practical pieces for real long-running work: WebUI, chat channels, tools, memory, MCP, model routing, automation, and deployment. +## Start Here + +| You want to... | Go to | +|---|---| +| Install nanobot with no terminal/config background | [Start Without Technical Background](./docs/start-without-technical-background.md) | +| Install quickly and get one CLI reply | [Install](#-install) and [Quick Start](#-quick-start) | +| Open the bundled browser UI after the CLI works | [WebUI](#-webui) | +| Connect Telegram, Discord, WeChat, Slack, Email, or another chat app | [Chat Apps](./docs/chat-apps.md) | +| Configure providers, fallback models, Langfuse, MCP, web tools, or security | [Docs](./docs/README.md) and [Configuration](./docs/configuration.md) | +| Understand or extend the internals | [Architecture](./docs/architecture.md) and [Development](./docs/development.md) | + ## 📢 News - **2026-06-01** 🚀 Released **v0.2.1** — **The Workbench Release** turns the packaged WebUI into a daily agent workbench: clearer Thought/response timelines, live file-edit activity, project workspaces, model and context controls, steadier sustained goals, CLI Apps + MCP extensions, and broader provider/channel support. Please see [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.2.1) for details. @@ -144,13 +155,13 @@ - **2026-02-17** 🎉 Released **v0.1.4** — MCP support, progress streaming, new providers, and multiple channel improvements. Please see [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.1.4) for details. - **2026-02-16** 🦞 nanobot now integrates a [ClawHub](https://clawhub.ai) skill — search and install public agent skills. - **2026-02-15** 🔑 nanobot now supports OpenAI Codex provider with OAuth login support. -- **2026-02-14** 🔌 nanobot now supports MCP! See [MCP section](#mcp-model-context-protocol) for details. +- **2026-02-14** 🔌 nanobot now supports MCP! See [MCP section](./docs/configuration.md#mcp-model-context-protocol) for details. - **2026-02-13** 🎉 Released **v0.1.3.post7** — includes security hardening and multiple improvements. **Please upgrade to the latest version to address security issues**. See [release notes](https://github.com/HKUDS/nanobot/releases/tag/v0.1.3.post7) for more details. - **2026-02-12** 🧠 Redesigned memory system — Less code, more reliable. Join the [discussion](https://github.com/HKUDS/nanobot/discussions/566) about it! - **2026-02-11** ✨ Enhanced CLI experience and added MiniMax support! - **2026-02-10** 🎉 Released **v0.1.3.post6** with improvements! Check the updates [notes](https://github.com/HKUDS/nanobot/releases/tag/v0.1.3.post6) and our [roadmap](https://github.com/HKUDS/nanobot/discussions/431). - **2026-02-09** 💬 Added Slack, Email, and QQ support — nanobot now supports multiple chat platforms! -- **2026-02-08** 🔧 Refactored Providers—adding a new LLM provider now takes just 2 simple steps! Check [here](#providers). +- **2026-02-08** 🔧 Refactored Providers—adding a new LLM provider now takes just 2 simple steps! Check [here](./docs/configuration.md#providers). - **2026-02-07** 🚀 Released **v0.1.3.post5** with Qwen support & several key improvements! Check [here](https://github.com/HKUDS/nanobot/releases/tag/v0.1.3.post5) for details. - **2026-02-06** ✨ Added Moonshot/Kimi provider, Discord integration, and enhanced security hardening! - **2026-02-05** ✨ Added Feishu channel, DeepSeek provider, and enhanced scheduled tasks support! @@ -176,12 +187,54 @@ > > If you want the most stable day-to-day experience, install from PyPI or with `uv`. -**Install from source** +Pick **one** install method: + +Prerequisites: Python 3.11 or newer. Git is only needed for a source install; Node.js/Bun are only needed if you are developing the WebUI itself. + +If terminals, API keys, or config files are new to you, use the guided zero-background walkthrough in [Start Without Technical Background](./docs/start-without-technical-background.md) instead of this compact README path. + +**One-command setup** + +macOS / Linux: ```bash -git clone https://github.com/HKUDS/nanobot.git -cd nanobot -pip install -e . +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" +``` + +Windows PowerShell: + +```powershell +irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1 | iex +``` + +The default command installs or upgrades `nanobot-ai` from PyPI, then starts `nanobot onboard --wizard`. If you finish the wizard and save the config, skip the manual initialize/configure steps below and go straight to **Test one message**. + +To preview the plan without changing your environment, pass `--dry-run`; combine it with `--dev` when you want to preview the main-branch install. + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" -- --dry-run +``` + +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1))) --dry-run +``` + +To install the current `main` branch instead, pass `--dev`: + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" -- --dev +``` + +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1))) --dev +``` + +If you prefer to inspect the script first, open [`scripts/install.sh`](./scripts/install.sh) or [`scripts/install.ps1`](./scripts/install.ps1). + +**Install from PyPI** + +```bash +python -m pip install nanobot-ai ``` **Install with `uv`** @@ -190,25 +243,41 @@ pip install -e . uv tool install nanobot-ai ``` -**Install from PyPI** +**Install from source** ```bash -pip install nanobot-ai +git clone https://github.com/HKUDS/nanobot.git +cd nanobot +python -m pip install -e . +``` + +Verify the install: + +```bash +nanobot --version ``` ## 🚀 Quick Start **1. Initialize** +Skip this step if the one-command setup already started the wizard and you saved the config there. + ```bash nanobot onboard ``` +Use `nanobot onboard --wizard` if you prefer an interactive setup. + **2. Configure** (`~/.nanobot/config.json`) -Configure these **two parts** in your config (other options have defaults). Add or merge the following blocks into your existing config instead of replacing the whole file. +Skip this step if you already configured provider and model settings in the wizard. -*Set your API key* (e.g. [OpenRouter](https://openrouter.ai/keys), recommended for global users): +`nanobot onboard` creates `~/.nanobot/config.json` and `~/.nanobot/workspace/`. Configure these **two parts** in the config file. Add or merge the following blocks into the existing file instead of replacing the whole file. + +The example below uses [OpenRouter](https://openrouter.ai/keys) only so the JSON has concrete names. Provider examples are recipes, not rankings or endorsements. If you use another provider, replace the provider config key, API key, preset provider name, and model ID together. + +*Set your API key*: ```json { @@ -220,28 +289,61 @@ Configure these **two parts** in your config (other options have defaults). Add } ``` -*Set your model* (optionally pin a provider — defaults to auto-detection): +*Set a model preset and make it active*: ```json { + "modelPresets": { + "primary": { + "label": "Primary", + "provider": "openrouter", + "model": "anthropic/claude-opus-4.5", + "maxTokens": 8192, + "contextWindowTokens": 65536, + "temperature": 0.1 + } + }, "agents": { "defaults": { - "provider": "openrouter", - "model": "anthropic/claude-opus-4-6" + "modelPreset": "primary" } } } ``` -**3. Chat** +Direct `agents.defaults.provider` and `agents.defaults.model` still work for existing configs, but named presets are the recommended path because they also power `/model` switching and `fallbackModels`. + +For another provider, the same config shape still applies: + +| Replace | Where | +|---|---| +| Provider config key | `providers.` | +| API key | `providers..apiKey` | +| Preset provider name | `modelPresets.primary.provider` | +| Model ID | `modelPresets.primary.model` | +| Endpoint URL, only when needed | `providers..apiBase` | + +**3. Test one message** + +```bash +nanobot status +nanobot agent -m "Hello!" +``` + +In `nanobot status`, it is normal for most providers to say `not set`. The active preset's provider should be configured, and `Config` plus `Workspace` should show check marks. + +If that works, start an interactive chat: ```bash nanobot agent ``` +Need help with `PATH`, API keys, provider/model matching, or JSON errors? See the fuller [Install and Quick Start](./docs/quick-start.md) and [Troubleshooting](./docs/troubleshooting.md). -- Want different LLM providers, web search, MCP, security settings, or more config options? See [Configuration](./docs/configuration.md) -- Want to run locally? Use [Atomic Chat](./docs/configuration.md#atomic-chat-local), [vLLM](./docs/configuration.md#vllm-local-openai-compatible), [Ollama](./docs/configuration.md#ollama-local), and [others](./docs/configuration.md#local-providers). +- Want a pasteable provider setup? See [Provider Cookbook](./docs/provider-cookbook.md) +- Want to understand provider/model matching? See [Providers and Models](./docs/providers.md) +- Want web search, MCP, security settings, or more config options? See [Configuration](./docs/configuration.md) +- Want to run locally? See [Ollama](./docs/providers.md#ollama), [vLLM or another local OpenAI-compatible server](./docs/providers.md#vllm-or-other-local-openai-compatible-server), and the full [provider reference](./docs/configuration.md#providers). - Want to run nanobot in chat apps like Telegram, Discord, WeChat or Feishu? See [Chat Apps](./docs/chat-apps.md) - Want Docker or Linux service deployment? See [Deployment](./docs/deployment.md) @@ -255,6 +357,8 @@ The WebUI ships **inside the published wheel** — no extra build step. Just ena **1. Enable the WebSocket channel in `~/.nanobot/config.json`** +Merge this block into your existing config: + ```json { "channels": { "websocket": { "enabled": true } } } ``` @@ -269,6 +373,8 @@ nanobot gateway Visit [`http://127.0.0.1:8765`](http://127.0.0.1:8765) in your browser. To open it from another device on your LAN, see [WebUI docs → LAN access](./webui/README.md#access-from-another-device-lan). +The WebUI is served by the WebSocket channel on port `8765` by default. The gateway's `18790` port is for the health endpoint, not the browser UI. + > [!TIP] > Working on the WebUI itself? Check out [`webui/README.md`](./webui/README.md) for the Vite dev server (HMR) workflow. @@ -307,6 +413,13 @@ Visit [`http://127.0.0.1:8765`](http://127.0.0.1:8765) in your browser. To open Browse the [repo docs](./docs/README.md) for the latest features and GitHub development version, or visit [nanobot.wiki](https://nanobot.wiki/docs/latest/getting-started/nanobot-overview) for the stable release documentation. +- Start with no technical background: [Start Without Technical Background](./docs/start-without-technical-background.md) +- Start from zero with developer basics: [Install and Quick Start](./docs/quick-start.md) +- Understand the runtime model: [Concepts](./docs/concepts.md) +- Read the source-level map: [Architecture](./docs/architecture.md) +- Choose a provider/model: [Providers and Models](./docs/providers.md) +- Copy provider setup recipes: [Provider Cookbook](./docs/provider-cookbook.md) +- Debug setup and runtime failures: [Troubleshooting](./docs/troubleshooting.md) - Talk to your nanobot with familiar chat apps: [Chat Apps](./docs/chat-apps.md) - Configure providers, web search, MCP, and runtime behavior: [Configuration](./docs/configuration.md) - Integrate nanobot with local tools and automations: [OpenAI-Compatible API](./docs/openai-api.md) · [Python SDK](./docs/python-sdk.md) @@ -318,8 +431,7 @@ PRs welcome! The codebase is intentionally small and readable. 🤗 ### Contribution Flow -See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, review, and contribution -guidelines. +See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, review, and contribution guidelines. **Roadmap** — Pick an item and [open a PR](https://github.com/HKUDS/nanobot/pulls)! diff --git a/desktop/README.md b/desktop/README.md index ba2e30b6a..b5837ab8d 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -1,18 +1,10 @@ # nanobot Desktop -Mac-first desktop app for running nanobot locally with the same product UI as -the browser WebUI. +Mac-first desktop app for running nanobot locally with the same product UI as the browser WebUI. -For users, the desktop app is a local wrapper around nanobot: it starts the -engine for you, keeps config and chat state in the platform app data directory, -and uses the shared WebUI for chat, settings, apps, skills, and workspace -selection. +For users, the desktop app is a local wrapper around nanobot: it starts the engine for you, keeps config and chat state in the platform app data directory, and uses the shared WebUI for chat, settings, apps, skills, and workspace selection. -For contributors, this folder is a native host shell. It reuses the root WebUI -build at `nanobot/web/dist`; it does not copy or fork `webui/src`. Electron owns -the local engine lifecycle, exposes `window.nanobotHost` to the renderer, serves -the `nanobot-app://` app protocol, and proxies `/api/*` plus `/webui/bootstrap` -to a private Unix socket `nanobot desktop-gateway` process. +For contributors, this folder is a native host shell. It reuses the root WebUI build at `nanobot/web/dist`; it does not copy or fork `webui/src`. Electron owns the local engine lifecycle, exposes `window.nanobotHost` to the renderer, serves the `nanobot-app://` app protocol, and proxies `/api/*` plus `/webui/bootstrap` to a private Unix socket `nanobot desktop-gateway` process. ## What To Read @@ -37,17 +29,11 @@ cd desktop bun run dev:app ``` -`dev:app` points Electron at the Vite dev server so WebUI changes hot reload. -For source checkouts, the app uses `python3` by default and injects the repo -root into `PYTHONPATH`. Packaged builds look for a bundled interpreter at -`Resources/nanobot-engine/bin/python3`. +`dev:app` points Electron at the Vite dev server so WebUI changes hot reload. For source checkouts, the app uses `python3` by default and injects the repo root into `PYTHONPATH`. Packaged builds look for a bundled interpreter at `Resources/nanobot-engine/bin/python3`. ## Engine Bundle -Release builds prepare `resources/nanobot-engine/` from a macOS -`python-build-standalone` archive before running `electron-builder`. -By default the script discovers the latest `astral-sh/python-build-standalone` -CPython 3.12 `install_only` asset for the requested architecture. +Release builds prepare `resources/nanobot-engine/` from a macOS `python-build-standalone` archive before running `electron-builder`. By default the script discovers the latest `astral-sh/python-build-standalone` CPython 3.12 `install_only` asset for the requested architecture. ```sh cd desktop @@ -64,13 +50,11 @@ Useful overrides: - `PYTHON_STANDALONE_URL=https://.../cpython-...tar.gz` - `NANOBOT_WHEELHOUSE=/path/to/wheels` to install from a locked wheelhouse -The script installs the current checkout's `nanobot-ai[api]` into the bundled -runtime and writes `nanobot-engine.json` for diagnostics. +The script installs the current checkout's `nanobot-ai[api]` into the bundled runtime and writes `nanobot-engine.json` for diagnostics. ## Updating Builds -The native host does not copy the WebUI source or fork the Python agent code. A -release bundle is assembled from the current repository state: +The native host does not copy the WebUI source or fork the Python agent code. A release bundle is assembled from the current repository state: 1. Build the shared WebUI: @@ -78,8 +62,7 @@ release bundle is assembled from the current repository state: bun run build --prefix webui ``` - `electron-builder` packages the resulting `nanobot/web/dist` directory as - `Resources/nanobot-webui`. + `electron-builder` packages the resulting `nanobot/web/dist` directory as `Resources/nanobot-webui`. 2. Prepare the bundled Python engine: @@ -88,9 +71,7 @@ release bundle is assembled from the current repository state: NANOBOT_DESKTOP_ARCH=arm64 bun run prepare-engine ``` - The script installs the current checkout's `nanobot-ai[api]` package into - `resources/nanobot-engine/`, so agent, provider, tool, WebSocket, and config - changes flow into the next desktop build automatically. + The script installs the current checkout's `nanobot-ai[api]` package into `resources/nanobot-engine/`, so agent, provider, tool, WebSocket, and config changes flow into the next desktop build automatically. 3. Build the desktop app and DMG: @@ -99,18 +80,12 @@ release bundle is assembled from the current repository state: bun run make:mac:x64 ``` -User data is not stored in the app bundle. Config, sessions, logs, workspace -state, and the default workspace remain under the platform app data directory, -so updating the app replaces code without overwriting local user state. +User data is not stored in the app bundle. Config, sessions, logs, workspace state, and the default workspace remain under the platform app data directory, so updating the app replaces code without overwriting local user state. ## Runtime Contract -- User data lives under Electron's platform app data directory. In development - this is usually `~/Library/Application Support/@nanobot/desktop/` on macOS; - packaged builds use the packaged app name. -- Fresh installs start the private engine directly. The Python desktop gateway - creates the first `config.json` with defaults, then shared WebUI settings own - provider, model, and credential setup. +- User data lives under Electron's platform app data directory. In development this is usually `~/Library/Application Support/@nanobot/desktop/` on macOS; packaged builds use the packaged app name. +- Fresh installs start the private engine directly. The Python desktop gateway creates the first `config.json` with defaults, then shared WebUI settings own provider, model, and credential setup. - The gateway listens on a per-user Unix socket in the app data directory and uses a transient secret. - The gateway starts with only the WebSocket local channel enabled and does not serve the WebUI static bundle. - The renderer loads assets through `nanobot-app://app/...`; browser users cannot open the native UI from a localhost port. @@ -119,8 +94,7 @@ so updating the app replaces code without overwriting local user state. - Native WebUI responses include a restrictive Content Security Policy. - WebUI talks only to the generic `window.nanobotHost` contract. Product-specific native behavior stays in this folder. -Generated release artifacts, node modules, and bundled runtimes remain ignored -so the tracked desktop package stays source-only. +Generated release artifacts, node modules, and bundled runtimes remain ignored so the tracked desktop package stays source-only. See also: diff --git a/desktop/docs/development.md b/desktop/docs/development.md index b5adeed36..398b8b2db 100644 --- a/desktop/docs/development.md +++ b/desktop/docs/development.md @@ -1,12 +1,8 @@ # Desktop Development Guide -This guide is for GitHub contributors who want to change the desktop app. If -you are using nanobot rather than developing it, the important bit is simpler: -desktop runs the local engine for you and shows the same chat, settings, apps, -skills, and workspace UI as the browser WebUI. +This guide is for GitHub contributors who want to change the desktop app. If you are using nanobot rather than developing it, the important bit is simpler: desktop runs the local engine for you and shows the same chat, settings, apps, skills, and workspace UI as the browser WebUI. -`desktop` is the native host for the shared nanobot WebUI. It is not a fork of -the WebUI, and it should not grow a second copy of product UI. +`desktop` is the native host for the shared nanobot WebUI. It is not a fork of the WebUI, and it should not grow a second copy of product UI. The healthy mental model is: @@ -34,13 +30,9 @@ cd desktop bun run dev:app ``` -In development, Electron loads `http://127.0.0.1:5173`, so changes under -`webui/src` hot reload. Changes under `desktop/src` require restarting -`dev:app`. +In development, Electron loads `http://127.0.0.1:5173`, so changes under `webui/src` hot reload. Changes under `desktop/src` require restarting `dev:app`. -For source checkouts, the host starts the engine with local `python3` and -injects the repository root into `PYTHONPATH`. This means Python changes under -`nanobot/` are picked up from the current checkout. +For source checkouts, the host starts the engine with local `python3` and injects the repository root into `PYTHONPATH`. This means Python changes under `nanobot/` are picked up from the current checkout. ## Where Code Goes @@ -57,15 +49,11 @@ Use this table before adding a desktop feature: | WebSocket-over-Unix-socket bridge | `desktop/src/unixWebSocket.ts` | | Bundled Python runtime preparation | `desktop/scripts/prepare-engine.mjs` | -For example, if desktop Settings needs an "Open logs" button, the button belongs -in the shared WebUI settings page because it is product UI. The actual filesystem -operation belongs in the desktop host and is exposed through `window.nanobotHost`. +For example, if desktop Settings needs an "Open logs" button, the button belongs in the shared WebUI settings page because it is product UI. The actual filesystem operation belongs in the desktop host and is exposed through `window.nanobotHost`. ## Host Contract -The shared WebUI talks to desktop through `window.nanobotHost`. WebUI code may -check for host capabilities, but it must not import Electron, Node.js modules, -or desktop source files. +The shared WebUI talks to desktop through `window.nanobotHost`. WebUI code may check for host capabilities, but it must not import Electron, Node.js modules, or desktop source files. Prefer capability-driven UI: @@ -80,8 +68,7 @@ Avoid platform-driven UI: if desktop -> run Electron-specific logic in WebUI ``` -This keeps the WebUI usable in browsers and leaves room for future native hosts -without rewriting product screens. +This keeps the WebUI usable in browsers and leaves room for future native hosts without rewriting product screens. ## Adding A Desktop Feature @@ -101,8 +88,7 @@ Before implementing, answer these questions: - Do not add provider-specific onboarding screens to `desktop/`. - Do not duplicate WebUI settings or login flows in Electron-owned HTML. - Do not make `desktop/src/main.ts` own agent behavior. -- Do not commit `desktop/node_modules`, `desktop/build`, `desktop/dist`, DMGs, - or `desktop/resources/nanobot-engine`. +- Do not commit `desktop/node_modules`, `desktop/build`, `desktop/dist`, DMGs, or `desktop/resources/nanobot-engine`. ## Release Shape @@ -112,5 +98,4 @@ Release builds assemble three existing parts: 2. the Python engine prepared under `desktop/resources/nanobot-engine`, 3. the Electron host compiled from `desktop/src`. -User config, logs, sessions, workspace state, and the default workspace live in -the platform app data directory, not inside the app bundle. +User config, logs, sessions, workspace state, and the default workspace live in the platform app data directory, not inside the app bundle. diff --git a/desktop/docs/host-contract.md b/desktop/docs/host-contract.md index 9cb2d28bc..2b55138cd 100644 --- a/desktop/docs/host-contract.md +++ b/desktop/docs/host-contract.md @@ -1,13 +1,8 @@ # Native Host Contract -This is a contributor reference for the boundary between the shared WebUI and -the native desktop host. Users should not need this contract to run the app, but -it explains why the desktop app can use native capabilities without turning the -WebUI into Electron-specific code. +This is a contributor reference for the boundary between the shared WebUI and the native desktop host. Users should not need this contract to run the app, but it explains why the desktop app can use native capabilities without turning the WebUI into Electron-specific code. -`desktop` is a native host shell around the shared WebUI build. The renderer -must not import Electron directly. It receives a minimal bridge at -`window.nanobotHost`. +`desktop` is a native host shell around the shared WebUI build. The renderer must not import Electron directly. It receives a minimal bridge at `window.nanobotHost`. ## Runtime API @@ -47,20 +42,13 @@ type NanobotHost = { ## First Run -The desktop host starts the private engine immediately. If the native data -directory has no `config.json`, `nanobot desktop-gateway` creates one with -defaults before serving the shared WebUI. Provider, model, credential, and login -setup stay in WebUI settings instead of Electron-owned HTML. +The desktop host starts the private engine immediately. If the native data directory has no `config.json`, `nanobot desktop-gateway` creates one with defaults before serving the shared WebUI. Provider, model, credential, and login setup stay in WebUI settings instead of Electron-owned HTML. ## Socket Bridge -The engine listens on a per-user Unix socket under the app data directory. -`/webui/bootstrap` returns `runtime_surface: "native"` and a WebSocket URL in -the `nanobot-host://engine/...` scheme. WebUI never opens that URL directly in -the browser runtime; it hands the URL to `window.nanobotHost.openSocket`. +The engine listens on a per-user Unix socket under the app data directory. `/webui/bootstrap` returns `runtime_surface: "native"` and a WebSocket URL in the `nanobot-host://engine/...` scheme. WebUI never opens that URL directly in the browser runtime; it hands the URL to `window.nanobotHost.openSocket`. -The native host then performs the WebSocket handshake against the Unix socket -and forwards events over Electron IPC. +The native host then performs the WebSocket handshake against the Unix socket and forwards events over Electron IPC. ## Host Security Boundary @@ -68,22 +56,15 @@ The host bridge is intentionally narrower than a general Electron preload: - IPC calls are accepted only from renderer frames loaded from `nanobot-app://app/...`. - `openSocket` accepts only `nanobot-host://engine/...` URLs. -- External navigation is denied in the app window; safe web links are opened by - the operating system. -- Native WebUI responses carry a restrictive Content Security Policy and - `X-Content-Type-Options: nosniff`. -- The renderer runs with `nodeIntegration: false`, `contextIsolation: true`, - `sandbox: true`, and `webSecurity: true`. +- External navigation is denied in the app window; safe web links are opened by the operating system. +- Native WebUI responses carry a restrictive Content Security Policy and `X-Content-Type-Options: nosniff`. +- The renderer runs with `nodeIntegration: false`, `contextIsolation: true`, `sandbox: true`, and `webSecurity: true`. -Security-sensitive tool behavior still belongs in nanobot core. The host -protects the native app boundary; the engine protects file, network, and tool -permissions. +Security-sensitive tool behavior still belongs in nanobot core. The host protects the native app boundary; the engine protects file, network, and tool permissions. ## Data Directory -The host stores config, workspace, sessions, logs, and transient socket files -under Electron's platform app data directory. In development on macOS this is -usually: +The host stores config, workspace, sessions, logs, and transient socket files under Electron's platform app data directory. In development on macOS this is usually: ```text ~/Library/Application Support/@nanobot/desktop/ diff --git a/desktop/docs/webui-sync.md b/desktop/docs/webui-sync.md index 905e4bfbc..ed6b8d15c 100644 --- a/desktop/docs/webui-sync.md +++ b/desktop/docs/webui-sync.md @@ -1,12 +1,8 @@ # WebUI Sync Workflow -This workflow is for contributors keeping the desktop app and browser WebUI in -sync. Users should experience them as one product surface: desktop adds a native -host and local engine lifecycle, while chat, settings, apps, skills, and -workspace UI still come from the shared WebUI. +This workflow is for contributors keeping the desktop app and browser WebUI in sync. Users should experience them as one product surface: desktop adds a native host and local engine lifecycle, while chat, settings, apps, skills, and workspace UI still come from the shared WebUI. -`desktop` consumes the shared WebUI build output. It must not copy, fork, or -vendor `webui/src`. +`desktop` consumes the shared WebUI build output. It must not copy, fork, or vendor `webui/src`. ## Development @@ -24,8 +20,7 @@ cd desktop bun run dev:app ``` -The host loads `http://127.0.0.1:5173` in development, so React changes hot -reload. Main/preload changes still require restarting `dev:app`. +The host loads `http://127.0.0.1:5173` in development, so React changes hot reload. Main/preload changes still require restarting `dev:app`. ## Release Build @@ -49,12 +44,11 @@ reload. Main/preload changes still require restarting `dev:app`. bun run make:mac:x64 ``` -`electron-builder` packages `nanobot/web/dist` as `Resources/nanobot-webui`. + `electron-builder` packages `nanobot/web/dist` as `Resources/nanobot-webui`. ## Checklist -- WebUI source remains host-neutral: it may branch on generic runtime - capabilities, but it must not import Electron or desktop source files. +- WebUI source remains host-neutral: it may branch on generic runtime capabilities, but it must not import Electron or desktop source files. ```sh rg -n "from ['\\\"]electron|desktop/src|nanobotDesktop" webui/src @@ -63,9 +57,7 @@ reload. Main/preload changes still require restarting `dev:app`. This command should print nothing. - Native host behavior is implemented in `desktop/src`. -- Provider, model, credential, and login setup stay in shared WebUI settings. - Do not duplicate those flows in Electron-owned HTML. -- Shared UI behavior is implemented in `webui/src` through `window.nanobotHost` - and generic runtime capability checks. +- Provider, model, credential, and login setup stay in shared WebUI settings. Do not duplicate those flows in Electron-owned HTML. +- Shared UI behavior is implemented in `webui/src` through `window.nanobotHost` and generic runtime capability checks. - Do not copy React components from `webui/src` into this folder. - Do not commit bundled runtimes, DMGs, or `node_modules`. diff --git a/docs/README.md b/docs/README.md index 2623d0807..53281a459 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,36 +1,106 @@ # nanobot Docs -For the latest documentation, visit [nanobot.wiki](https://nanobot.wiki/docs/latest/getting-started/nanobot-overview). +For published release documentation, visit [nanobot.wiki](https://nanobot.wiki/docs/latest/getting-started/nanobot-overview). The pages in this directory track the current repository and may describe features that have not reached the published site yet. -The pages in this directory track the current repository and may move faster than the published website. +If you have never used a terminal or edited a config file before, start with [`start-without-technical-background.md`](./start-without-technical-background.md). Otherwise, start with [`quick-start.md`](./quick-start.md) and get one local `nanobot agent -m "Hello!"` reply working before connecting chat apps, WebUI, Docker, or custom tools. -## Core Docs +Most JSON examples in these docs are snippets to merge into `~/.nanobot/config.json`, not full replacement files. -Start here for setup, everyday usage, and deployment. +Provider examples are concrete walkthroughs, not rankings or endorsements. Use the provider whose key, endpoint, and model ID you actually control. -| Topic | Repo docs | What it covers | +If you find a docs mistake, outdated command, or confusing step, please open an issue: . + +## Pick a Track + +| You are | Start with | Then use | |---|---|---| -| Install and quick start | [`quick-start.md`](./quick-start.md) | Installation, onboarding, and first-run setup | -| Chat apps | [`chat-apps.md`](./chat-apps.md) | Connect nanobot to Telegram, Discord, WeChat, and more | -| Agent social network | [`agent-social-network.md`](./agent-social-network.md) | Join external agent communities from nanobot | -| Configuration | [`configuration.md`](./configuration.md) | Providers, tools, channels, MCP, and runtime settings | -| Image generation | [`image-generation.md`](./image-generation.md) | Configure image providers, WebUI image mode, and generated artifacts | -| WebUI | [`../webui/README.md`](../webui/README.md) | Open the bundled browser UI; LAN access; Vite dev server for contributors | -| Multiple instances | [`multiple-instances.md`](./multiple-instances.md) | Run isolated bots with separate configs and workspaces | -| CLI reference | [`cli-reference.md`](./cli-reference.md) | Core CLI commands and common entrypoints | -| In-chat commands | [`chat-commands.md`](./chat-commands.md) | Slash commands and periodic task behavior | -| OpenAI-compatible API | [`openai-api.md`](./openai-api.md) | Local API endpoints, request format, and file uploads | -| Deployment | [`deployment.md`](./deployment.md) | Docker, Linux service, and macOS LaunchAgent setup | +| New to terminals and config files | [`start-without-technical-background.md`](./start-without-technical-background.md) | [`troubleshooting.md`](./troubleshooting.md) if the first reply fails | +| Comfortable pasting commands and JSON | [`quick-start.md`](./quick-start.md) | [`provider-cookbook.md`](./provider-cookbook.md) for pasteable provider setups | +| Operating a long-running bot | [`concepts.md`](./concepts.md) | [`chat-apps.md`](./chat-apps.md), [`../webui/README.md`](../webui/README.md), and [`deployment.md`](./deployment.md) | +| Integrating or extending nanobot | [`architecture.md`](./architecture.md) | [`configuration.md`](./configuration.md), [`openai-api.md`](./openai-api.md), [`python-sdk.md`](./python-sdk.md), [`development.md`](./development.md), and [`channel-plugin-guide.md`](./channel-plugin-guide.md) | -## Advanced Docs +## Start Here -Use these when you want deeper customization, integration, or extension details. - -| Topic | Repo docs | What it covers | +| Goal | Read | Outcome | |---|---|---| +| Start with no technical background | [`start-without-technical-background.md`](./start-without-technical-background.md) | One-command setup, terminal basics, config, API keys, and the first reply | +| Install and get the first reply | [`quick-start.md`](./quick-start.md) | A working CLI agent and a known-good config path | +| Understand how the pieces fit | [`concepts.md`](./concepts.md) | Mental model for config, workspace, gateway, channels, tools, memory, and sessions | +| Choose or change a model provider | [`providers.md`](./providers.md) | Correct provider/model pairing without reading the full config reference | +| Copy a provider setup recipe | [`provider-cookbook.md`](./provider-cookbook.md) | Pasteable OpenRouter, OpenAI, Anthropic, local model, fallback, and Langfuse setups | +| Fix a first-run or runtime problem | [`troubleshooting.md`](./troubleshooting.md) | A diagnosis order and targeted checks for common failures | + +## After the First Reply Works + +Do not configure everything at once. Pick one next surface: + +If a local `nanobot agent` session can already answer normally, you can also ask nanobot to help configure itself: have it read the relevant docs, inspect your current config, make one specific next change, and tell you when to run `/restart`. + +| Next goal | Read | First check | +|---|---|---| +| Use nanobot in a browser | [`../webui/README.md`](../webui/README.md) | Enable WebSocket, run `nanobot gateway`, open `http://127.0.0.1:8765` | +| Talk through a chat app | [`chat-apps.md`](./chat-apps.md) | Merge one channel snippet, run `nanobot channels status`, keep `nanobot gateway` running | +| Change provider or add fallbacks | [`provider-cookbook.md`](./provider-cookbook.md) | Keep `modelPresets` named and set `agents.defaults.modelPreset` | +| Understand before operating long-term | [`concepts.md`](./concepts.md) | Know what config, workspace, gateway, sessions, memory, and tools mean | +| Diagnose a new failure | [`troubleshooting.md`](./troubleshooting.md) | Start with `nanobot status`, then `nanobot agent -m "Hello!"` | + +## Use nanobot + +| Goal | Read | Outcome | +|---|---|---| +| Open the bundled browser UI | [`../webui/README.md`](../webui/README.md) | WebUI on port `8765`, or Vite HMR when developing the frontend | +| Connect Telegram, Discord, WeChat, Slack, and other apps | [`chat-apps.md`](./chat-apps.md) | A gateway-backed chat channel with access control | +| Use slash commands and periodic tasks | [`chat-commands.md`](./chat-commands.md) | Pairing, model presets, heartbeat tasks, and chat-side controls | +| Generate images | [`image-generation.md`](./image-generation.md) | Image provider config, WebUI image mode, and artifact behavior | +| Run several isolated bots | [`multiple-instances.md`](./multiple-instances.md) | Separate configs, workspaces, ports, and sessions | +| Deploy outside a terminal | [`deployment.md`](./deployment.md) | Docker, systemd user services, and macOS LaunchAgent setup | +| Join agent communities | [`agent-social-network.md`](./agent-social-network.md) | External agent-community setup | + +## Reference + +| Area | Read | Best for | +|---|---|---| +| Full configuration schema | [`configuration.md`](./configuration.md) | Exact fields, defaults, provider tables, web tools, MCP, security, and runtime options | +| CLI commands | [`cli-reference.md`](./cli-reference.md) | Command names, common flags, and entrypoints | +| Architecture | [`architecture.md`](./architecture.md) | Source-level runtime map for core flow, providers, channels, tools, WebUI, memory, security, and extension points | | Development | [`development.md`](./development.md) | Contributor notes for adding providers and transcription adapters | -| Memory | [`memory.md`](./memory.md) | How nanobot stores, consolidates, and restores memory | -| Python SDK | [`python-sdk.md`](./python-sdk.md) | Use nanobot programmatically from Python | -| Channel plugin guide | [`channel-plugin-guide.md`](./channel-plugin-guide.md) | Build and test custom chat channel plugins | -| WebSocket channel | [`websocket.md`](./websocket.md) | Real-time WebSocket access and protocol details | -| Custom tools | [`my-tool.md`](./my-tool.md) | Inspect and tune runtime state with the `my` tool | +| Memory | [`memory.md`](./memory.md) | Session history, Dream consolidation, memory files, and versioning | +| Observability | [`configuration.md#langfuse-observability`](./configuration.md#langfuse-observability) | Langfuse tracing setup and required environment variables | +| WebSocket protocol | [`websocket.md`](./websocket.md) | Custom clients, token issuance, multiplexed chats, media, and protocol events | +| OpenAI-compatible API | [`openai-api.md`](./openai-api.md) | `/v1/chat/completions`, `/v1/models`, file uploads, and SDK-compatible usage | +| Python SDK | [`python-sdk.md`](./python-sdk.md) | Running nanobot from Python and attaching hooks | +| Runtime self-inspection | [`my-tool.md`](./my-tool.md) | Inspecting and tuning the current agent run | + +## Fast Lookup + +| Need | Jump to | +|---|---| +| Provider/model resolution order | [`providers.md#provider-resolution`](./providers.md#provider-resolution) | +| Model presets and fallback chains | [`providers.md#model-presets`](./providers.md#model-presets) and [`providers.md#fallback-models`](./providers.md#fallback-models) | +| Langfuse environment variables | [`configuration.md#langfuse-observability`](./configuration.md#langfuse-observability) | +| WebSocket/WebUI protocol details | [`websocket.md`](./websocket.md) | +| OpenAI-compatible API usage | [`openai-api.md`](./openai-api.md) | +| Multiple configs, workspaces, and ports | [`multiple-instances.md`](./multiple-instances.md) | +| Security, sandboxing, and SSRF controls | [`configuration.md#security`](./configuration.md#security) | +| Channel plugin development | [`channel-plugin-guide.md`](./channel-plugin-guide.md) | + +## Extend nanobot + +| Goal | Read | Outcome | +|---|---|---| +| Add a provider or transcription adapter | [`development.md`](./development.md) | A registry/schema-aligned implementation path | +| Add a chat channel plugin | [`channel-plugin-guide.md`](./channel-plugin-guide.md) | A packaged channel discovered through entry points | +| Add custom MCP servers | [`configuration.md#mcp-model-context-protocol`](./configuration.md#mcp-model-context-protocol) | External tools exposed to the agent through MCP | +| Tune tool safety | [`configuration.md#security`](./configuration.md#security) | Shell sandboxing, workspace restriction, and SSRF policy | + +## Reading Strategy + +Use the docs in this order when you are unsure where to go: + +1. If terminal commands or config files are new to you, [`start-without-technical-background.md`](./start-without-technical-background.md) explains the setup words and uses one concrete provider example so there is only one decision at a time. +2. [`quick-start.md`](./quick-start.md) proves installation, config loading, and provider access. +3. [`concepts.md`](./concepts.md) explains the runtime model so later pages are easier to scan. +4. [`provider-cookbook.md`](./provider-cookbook.md) gives pasteable provider, fallback, local model, and Langfuse recipes. +5. A task guide, such as [`chat-apps.md`](./chat-apps.md), [`image-generation.md`](./image-generation.md), or [`deployment.md`](./deployment.md), gets one workflow working. +6. [`configuration.md`](./configuration.md) is the source of truth when you need a specific field, default value, or advanced option. +7. [`troubleshooting.md`](./troubleshooting.md) helps isolate whether a failure is install, config, provider, gateway, channel, or tool related. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 000000000..665fad1c4 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,211 @@ +# Architecture + +This page maps nanobot's runtime behavior to source files. Use it when you are debugging internals, reviewing a PR, adding a provider/channel/tool, or trying to understand where a user-visible behavior comes from. + +For the product-level mental model, read [`concepts.md`](./concepts.md) first. + +## Core Flow + +```mermaid +flowchart LR + Channel["Channel
CLI, WebUI, chat apps"] --> Bus["MessageBus
InboundMessage"] + Bus --> Loop["AgentLoop
session, workspace, context"] + Loop --> Runner["AgentRunner
provider/tool loop"] + Runner --> Provider["Provider
LLM backend"] + Provider --> Runner + Runner --> Tools["Tools
files, shell, web, MCP, cron"] + Tools --> Runner + Runner --> Loop + Loop --> Outbound["MessageBus
OutboundMessage"] + Outbound --> Channel + + Loop -. reads/writes .-> State["Session, memory,
hooks, skills, templates"] +``` + +Main files: + +| Area | Files | +|---|---| +| Message events and queue | `nanobot/bus/events.py`, `nanobot/bus/queue.py` | +| Turn orchestration | `nanobot/agent/loop.py` | +| Provider/tool conversation loop | `nanobot/agent/runner.py` | +| Context construction | `nanobot/agent/context.py` | +| Session storage and compaction | `nanobot/session/manager.py` | +| Long-term memory and Dream | `nanobot/agent/memory.py` | + +## Agent Loop vs Agent Runner + +`AgentLoop` owns the channel-facing turn: + +- receives inbound messages; +- determines the effective session and workspace scope; +- builds context; +- wires hooks, progress, and channel metadata; +- publishes outbound messages. + +`AgentRunner` owns the model-facing loop: + +- sends messages to the selected provider; +- handles streaming deltas and reasoning blocks; +- executes tool calls; +- feeds tool results back into the model; +- stops when a final answer is produced or runtime limits are hit. + +Keep this split in mind when debugging. If a problem is about channel routing, session keys, workspace selection, or outbound delivery, start in `agent/loop.py`. If it is about provider calls, tool calls, streaming, or iteration limits, start in `agent/runner.py`. + +## Providers + +Provider metadata is centralized in `nanobot/providers/registry.py`. Configuration fields live in `nanobot/config/schema.py`. + +Provider selection uses: + +- explicit `agents.defaults.provider` or preset provider; +- provider registry keywords; +- API key prefixes and API base URL hints; +- local provider fallback when `apiBase` is configured; +- gateway fallback for providers that can route many model families. + +Provider implementations live in `nanobot/providers/`. Most hosted providers use the OpenAI-compatible implementation, while Anthropic, Azure OpenAI, AWS Bedrock, OpenAI Codex, and GitHub Copilot have specialized paths. + +Useful docs: + +- [`providers.md`](./providers.md) for practical setup; +- [`configuration.md#providers`](./configuration.md#providers) for exact provider reference. + +## Channels + +Channels translate external platforms into `InboundMessage` events and send `OutboundMessage` events back to the platform. + +Main files: + +| Area | Files | +|---|---| +| Base channel contract | `nanobot/channels/base.py` | +| Built-in channels | `nanobot/channels/*.py` | +| Discovery and lifecycle | `nanobot/channels/manager.py` | +| WebSocket/WebUI channel | `nanobot/channels/websocket.py` | + +Channels are discovered through built-in module scanning and plugin entry points. A custom channel should follow [`channel-plugin-guide.md`](./channel-plugin-guide.md). + +## WebUI and Gateway + +`nanobot gateway` starts: + +- enabled chat channels; +- the WebSocket channel when configured; +- workspace-scoped cron service; +- system jobs such as Dream and heartbeat; +- the health endpoint on `gateway.port`. + +The packaged WebUI is served by the WebSocket channel, not the health endpoint: + +| Surface | Default | +|---|---| +| Health endpoint | `http://127.0.0.1:18790/health` | +| WebUI/WebSocket | `http://127.0.0.1:8765` | + +WebUI source lives in `webui/`. The production build is written to `nanobot/web/dist/` and bundled into the wheel. + +Useful docs: + +- [`../webui/README.md`](../webui/README.md) for WebUI use and development; +- [`websocket.md`](./websocket.md) for protocol details. + +## Tools + +Tools are discovered from `nanobot/agent/tools/` and plugin entry points. + +Important files: + +| Tool area | Files | +|---|---| +| Tool base and schema | `nanobot/agent/tools/base.py`, `nanobot/agent/tools/schema.py` | +| Discovery | `nanobot/agent/tools/registry.py` | +| Shell execution | `nanobot/agent/tools/shell.py` | +| Filesystem tools | `nanobot/agent/tools/filesystem.py` | +| Web search/fetch | `nanobot/agent/tools/web.py` | +| MCP tools | `nanobot/agent/tools/mcp.py` | +| Cron | `nanobot/agent/tools/cron.py`, `nanobot/cron/` | +| Image generation | `nanobot/agent/tools/image_generation.py` | +| Runtime self-inspection | `nanobot/agent/tools/self.py` | + +Tool behavior is part of the model contract. Keep user-visible tool names, schemas, and error messages stable unless a change is intentional. + +## Config and Paths + +The config schema lives in `nanobot/config/schema.py`. Loading and saving live in `nanobot/config/loader.py`. Runtime path helpers live in `nanobot/config/paths.py`. + +Defaults: + +| Path | Default | +|---|---| +| Config | `~/.nanobot/config.json` | +| Workspace | `~/.nanobot/workspace/` | +| Sessions | `/sessions/*.jsonl` | +| Memory | `/memory/` | +| Cron store | `/cron/jobs.json` | +| WebUI/media/log runtime data | config directory subdirectories such as `webui/`, `media/`, and `logs/` | + +The schema accepts both camelCase and snake_case keys, but saves config with camelCase aliases. + +## Memory and Sessions + +Session history is the near-term conversation replay. Memory is the longer-term workspace state. + +| Store | File area | +|---|---| +| Session JSONL files | `/sessions/` | +| Long-term memory | `/memory/MEMORY.md` | +| Consolidation source history | `/memory/history.jsonl` | +| Bootstrap identity files | `/SOUL.md`, `/USER.md`, templates under `nanobot/templates/` | + +Dream is implemented in `nanobot/agent/memory.py` and scheduled by the runtime when enabled. + +## Security Boundaries + +Security-sensitive code paths include: + +| Boundary | Files | +|---|---| +| Workspace scope | `nanobot/security/workspace_access.py`, `nanobot/security/workspace_policy.py` | +| Shell sandboxing | `nanobot/agent/tools/shell.py` | +| SSRF/network checks | `nanobot/security/network.py`, `nanobot/agent/tools/web.py` | +| PTH guard and CLI startup security | `nanobot/security/` and CLI entrypoints | +| Channel access control | channel config in `nanobot/channels/*.py` | + +When changing tools, channels, file access, WebUI workspace behavior, or network fetching, treat security as part of the functional behavior and update docs if the user-facing boundary changes. + +## Extension Points + +| Extension | How | +|---|---| +| Provider | Add `ProviderSpec` in `providers/registry.py`, add schema field in `config/schema.py`, implement provider only if the generic backend is not enough | +| Channel | Implement `BaseChannel`, expose an entry point, follow [`channel-plugin-guide.md`](./channel-plugin-guide.md) | +| Tool | Implement a tool under `agent/tools/` or expose a plugin entry point | +| MCP | Add `tools.mcpServers` config | +| Skill | Add workspace skill files under `/skills/` or built-in skills under `nanobot/skills/` | + +Prefer existing registry/discovery patterns over ad hoc wiring. + +## Testing and Verification + +Common checks: + +```bash +pytest tests/test_openai_api.py::test_function -v +ruff check nanobot/ +cd webui && bun run test +cd webui && bun run build +``` + +Choose tests based on the changed surface: + +| Change | Minimum useful verification | +|---|---| +| Provider behavior | Provider unit tests or a mocked API path; `nanobot agent -m "Hello!"` with safe config when possible | +| Channel behavior | Channel tests plus `nanobot gateway` startup path | +| WebUI behavior | WebUI tests/build and, for routing/settings/chat changes, browser-level verification through the gateway | +| Tool behavior | Tool unit tests and an agent-run path when schema or model-facing behavior changes | +| Docs | Link checks, command accuracy against CLI/schema, and `git diff --check` | + +For user-facing flows, prefer at least one verification path through the public surface the user actually touches: CLI command, HTTP endpoint, WebSocket/WebUI, chat channel, or packaged import. diff --git a/docs/channel-plugin-guide.md b/docs/channel-plugin-guide.md index 10ceb83b3..292ad00f8 100644 --- a/docs/channel-plugin-guide.md +++ b/docs/channel-plugin-guide.md @@ -2,7 +2,7 @@ Build a custom nanobot channel in three steps: subclass, package, install. -> **Note:** We recommend developing channel plugins against a source checkout of nanobot (`pip install -e .`) rather than a PyPI release, so you always have access to the latest base-channel features and APIs. +> **Note:** We recommend developing channel plugins against a source checkout of nanobot (`python -m pip install -e .`) rather than a PyPI release, so you always have access to the latest base-channel features and APIs. ## How It Works @@ -153,7 +153,7 @@ The key (`webhook`) becomes the config section name. The value points to your `B ### 3. Install & Configure ```bash -pip install -e . +python -m pip install -e . nanobot plugins list # verify "Webhook" shows as "plugin" nanobot onboard # auto-adds default config for detected plugins ``` @@ -533,7 +533,7 @@ If not overridden, the base class returns `{"enabled": false}`. ```bash git clone https://github.com/you/nanobot-channel-webhook cd nanobot-channel-webhook -pip install -e . +python -m pip install -e . nanobot plugins list # should show "Webhook" as "plugin" nanobot gateway # test end-to-end ``` diff --git a/docs/chat-apps.md b/docs/chat-apps.md index a529ff00a..068e7edfc 100644 --- a/docs/chat-apps.md +++ b/docs/chat-apps.md @@ -2,6 +2,42 @@ Connect nanobot to your favorite chat platform. Want to build your own? See the [Channel Plugin Guide](./channel-plugin-guide.md). +Before configuring a chat app, make sure the local CLI path works: + +```bash +nanobot agent -m "Hello!" +``` + +If that fails, fix installation, config, provider, or model setup first with [`quick-start.md`](./quick-start.md), [`providers.md`](./providers.md), and [`troubleshooting.md`](./troubleshooting.md). Chat apps require `nanobot gateway` to stay running after the channel is configured. + +Most examples below are snippets to merge into `~/.nanobot/config.json`. + +## Common Setup Pattern + +Every chat app uses the same shape: + +1. Create or prepare the bot/account in the chat platform. +2. Copy the token, secret, QR login state, webhook URL, or account ID that platform gives you. +3. Merge that platform's JSON snippet into `~/.nanobot/config.json`. +4. Keep access control narrow at first with `allowFrom` or the platform-specific allow list. +5. Check that nanobot can see the configured channel: + +```bash +nanobot channels status +``` + +6. Start the gateway and leave that terminal running: + +```bash +nanobot gateway +``` + +7. Send a message from the allowed account. In group chats, follow that channel's `groupPolicy` behavior: many channels default to mention-only, while Matrix and WhatsApp default to open group replies. + +If `nanobot channels status` does not show the channel as enabled, the config snippet is in the wrong place, the channel name is misspelled, or the config file you edited is not the one nanobot is reading. If the channel is enabled but messages do not arrive, run `nanobot gateway --verbose` and compare the platform-side credentials, event permissions, and allow lists. + +> `["*"]` allows anyone who can reach that channel to talk to the bot. Use it only when that is intentional, or temporarily while testing in a private sandbox. + | Channel | What you need | |---------|---------------| | **Telegram** | Bot token from @BotFather | @@ -21,7 +57,7 @@ Connect nanobot to your favorite chat platform. Want to build your own? See the | **Signal** | signal-cli daemon + phone number |
-Telegram (Recommended) +Telegram **1. Create a bot** - Open Telegram, search `@BotFather` @@ -42,8 +78,7 @@ Connect nanobot to your favorite chat platform. Want to build your own? See the } ``` -> You can find your **User ID** in Telegram settings. It is shown as `@yourUserId`. -> Copy this value **without the `@` symbol** and paste it into the config file. +> You can find your **User ID** in Telegram settings. It is shown as `@yourUserId`. Copy this value **without the `@` symbol** and paste it into the config file. **3. Run** @@ -54,9 +89,7 @@ nanobot gateway **Webhook mode (optional)** -Telegram uses long polling by default. To receive updates through a webhook, expose -a public HTTPS URL that forwards to nanobot's local listener and set `mode` to -`webhook`: +Telegram uses long polling by default. To receive updates through a webhook, expose a public HTTPS URL that forwards to nanobot's local listener and set `mode` to `webhook`: ```json { @@ -77,17 +110,9 @@ a public HTTPS URL that forwards to nanobot's local listener and set `mode` to } ``` -> `webhookSecretToken` is required in webhook mode. Do not expose the local -> webhook listener directly to the public internet without a reverse proxy or -> tunnel in front of it. TLS/Host policy is handled by your proxy; nanobot only -> listens on `webhookListenHost:webhookListenPort` and validates Telegram's -> webhook secret token. `webhookMaxConnections` defaults to `4`; nanobot -> still serializes Telegram updates per conversation before forwarding them to -> the agent. +> `webhookSecretToken` is required in webhook mode. Do not expose the local webhook listener directly to the public internet without a reverse proxy or tunnel in front of it. TLS/Host policy is handled by your proxy; nanobot only listens on `webhookListenHost:webhookListenPort` and validates Telegram's webhook secret token. `webhookMaxConnections` defaults to `4`; nanobot still serializes Telegram updates per conversation before forwarding them to the agent. > -> `webhookUrl` is the public HTTPS URL registered with Telegram. -> `webhookPath` is the local path nanobot listens on. They often use the same -> path, but may differ when a reverse proxy or tunnel rewrites the request path. +> `webhookUrl` is the public HTTPS URL registered with Telegram. `webhookPath` is the local path nanobot listens on. They often use the same path, but may differ when a reverse proxy or tunnel rewrites the request path.
@@ -209,15 +234,11 @@ nanobot gateway Install Matrix dependencies first: ```bash -pip install nanobot-ai[matrix] +python -m pip install "nanobot-ai[matrix]" ``` > [!NOTE] -> Matrix is not supported on Windows. `matrix-nio[e2e]` depends on -> `python-olm`, which has no pre-built Windows wheel and is skipped by the -> `matrix` extra on `sys_platform == 'win32'`. The command above will still -> succeed on Windows but without `matrix-nio` installed, so enabling the -> Matrix channel will fail at startup. Use macOS, Linux, or WSL2. +> Matrix is not supported on Windows. `matrix-nio[e2e]` depends on `python-olm`, which has no pre-built Windows wheel and is skipped by the `matrix` extra on `sys_platform == 'win32'`. The command above will still succeed on Windows but without `matrix-nio` installed, so enabling the Matrix channel will fail at startup. Use macOS, Linux, or WSL2. **1. Create/choose a Matrix account** @@ -230,9 +251,7 @@ pip install nanobot-ai[matrix] - `userId` (example: `@nanobot:matrix.org`) - `password` -(Note: `accessToken` and `deviceId` are still supported for legacy reasons, but -for reliable encryption, password login is recommended instead. If the -`password` is provided, `accessToken` and `deviceId` will be ignored.) +(Note: `accessToken` and `deviceId` are still supported for legacy reasons, but for reliable encryption, password login is recommended instead. If the `password` is provided, `accessToken` and `deviceId` will be ignored.) **3. Configure** @@ -314,8 +333,7 @@ nanobot channels login whatsapp nanobot gateway ``` -> WhatsApp bridge updates are not applied automatically for existing installations. -> After upgrading nanobot, rebuild the local bridge with: +> WhatsApp bridge updates are not applied automatically for existing installations. After upgrading nanobot, rebuild the local bridge with: > `rm -rf ~/.nanobot/bridge && nanobot channels login whatsapp` @@ -432,7 +450,7 @@ Connects to a [Napcat](https://github.com/NapNeko/NapCatQQ) instance over its ** **1. Set up Napcat** -- Install and log into Napcat, then enable a **Forward WebSocket** server. Recommends: [official napcat docker tutorial](https://github.com/NapNeko/NapCat-Docker) +- Install and log into Napcat, then enable a **Forward WebSocket** server. See the [official Napcat Docker tutorial](https://github.com/NapNeko/NapCat-Docker). - In the webui, follow "网络配置" -> "新建" -> "Websocket 服务器" to create a forward websocket server. By default, the URL is `ws://127.0.0.1:3001` - Copy the forward websocket server's token - (Optional) In the webui, follow "系统配置" -> "登陆配置" -> "快速登录QQ" to automatically login after restarts @@ -501,9 +519,7 @@ Uses **Stream Mode** — no public IP required. > `allowFrom`: Add your staff ID. Use `["*"]` to allow all users. > -> `groupUserIsolation`: Optional. Defaults to `false`, which keeps one shared session per -> group chat. Set it to `true` to give each sender in a DingTalk group chat a separate -> session while replies still go back to the same group. +> `groupUserIsolation`: Optional. Defaults to `false`, which keeps one shared session per group chat. Set it to `true` to give each sender in a DingTalk group chat a separate session while replies still go back to the same group. **3. Run** @@ -629,7 +645,7 @@ Uses **HTTP long-poll** with QR-code login via the ilinkai personal WeChat API. **1. Install with WeChat support** ```bash -pip install "nanobot-ai[weixin]" +python -m pip install "nanobot-ai[weixin]" ``` **2. Configure** @@ -681,7 +697,7 @@ nanobot gateway **1. Install the optional dependency** ```bash -pip install nanobot-ai[wecom] +python -m pip install "nanobot-ai[wecom]" ``` **2. Create a WeCom AI Bot** @@ -720,7 +736,7 @@ nanobot gateway **1. Install the optional dependency** ```bash -pip install nanobot-ai[msteams] +python -m pip install "nanobot-ai[msteams]" ``` **2. Create a Teams / Azure bot app registration** diff --git a/docs/chat-commands.md b/docs/chat-commands.md index a65c9e137..fded560d6 100644 --- a/docs/chat-commands.md +++ b/docs/chat-commands.md @@ -43,7 +43,7 @@ Use `/model` to inspect the current runtime model: /model ``` -The response shows the current model, the current preset, and the available preset names. `default` is always available and represents the model settings from `agents.defaults.*`. +The response shows the current model, the current preset, and the available preset names. Named presets come from the top-level `modelPresets` config and are the recommended way to configure model choices. `default` is always available and represents the model settings from direct `agents.defaults.*` fields. To switch presets for future turns: @@ -57,17 +57,32 @@ Preset names come from the top-level `modelPresets` config. Switching is runtime ## Periodic Tasks -The gateway wakes up every 30 minutes and checks `HEARTBEAT.md` in your workspace (`~/.nanobot/workspace/HEARTBEAT.md`). If the file has tasks under `## Active Tasks`, the agent executes them and delivers results to your most recently active chat channel. If there are no active tasks, the heartbeat is skipped silently. +Periodic tasks are driven by `HEARTBEAT.md` in your workspace (`~/.nanobot/workspace/HEARTBEAT.md`). When `nanobot gateway` starts, it registers a protected heartbeat cron job by default. Every 30 minutes, that job checks the file; if it finds tasks under `## Active Tasks`, the agent executes them and delivers results to your most recently active chat channel. If there are no active tasks, the heartbeat is skipped silently. **Setup:** edit `~/.nanobot/workspace/HEARTBEAT.md` (created automatically by `nanobot onboard`): ```markdown ## Active Tasks -- [ ] Check weather forecast and send a summary -- [ ] Scan inbox for urgent emails +- Check weather forecast and send a summary +- Scan inbox for urgent emails ``` The agent can also manage this file itself — ask it to "add a periodic task" and it will update `HEARTBEAT.md` for you. Completed tasks should be deleted from the file, not moved to another section. +You can change the interval or disable the built-in heartbeat in `~/.nanobot/config.json`: + +```json +{ + "gateway": { + "heartbeat": { + "enabled": true, + "intervalS": 1800 + } + } +} +``` + +The heartbeat job is visible in `cron(action="list")` as `heartbeat`, but it is system-managed and cannot be removed with the `cron` tool. To stop it, set `gateway.heartbeat.enabled` to `false` and restart the gateway. + > **Note:** The gateway must be running (`nanobot gateway`) and you must have chatted with the bot at least once so it knows which channel to deliver to. diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 667f8c13d..278c2bbe8 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -1,21 +1,167 @@ # CLI Reference -| Command | Description | -|---------|-------------| -| `nanobot onboard` | Initialize config & workspace at `~/.nanobot/` | -| `nanobot onboard --wizard` | Launch the interactive onboarding wizard | -| `nanobot onboard -c -w ` | Initialize or refresh a specific instance config and workspace | -| `nanobot agent -m "..."` | Chat with the agent | -| `nanobot agent -w ` | Chat against a specific workspace | -| `nanobot agent -w -c ` | Chat against a specific workspace/config | -| `nanobot agent` | Interactive chat mode | -| `nanobot agent --no-markdown` | Show plain-text replies | -| `nanobot agent --logs` | Show runtime logs during chat | -| `nanobot serve` | Start the OpenAI-compatible API | -| `nanobot gateway` | Start the gateway | -| `nanobot status` | Show status | -| `nanobot provider login openai-codex` | OAuth login for providers | -| `nanobot channels login ` | Authenticate a channel interactively | -| `nanobot channels status` | Show channel status | +Use this page when you know what you want to run and need the command shape. For a guided first run, start with [`quick-start.md`](./quick-start.md). -Interactive mode exits: `exit`, `quit`, `/exit`, `/quit`, `:q`, or `Ctrl+D`. +## Choose a Command + +| Goal | Command | Notes | +|---|---|---| +| Check the install | `nanobot --version` | If this fails, try `python -m nanobot --version` | +| Create or refresh config | `nanobot onboard` | Creates `~/.nanobot/config.json` and `~/.nanobot/workspace/` | +| Use guided setup | `nanobot onboard --wizard` | Best when you prefer prompts over hand-editing JSON | +| Check config without calling a model | `nanobot status` | Reads the default config and summarizes the active model/provider | +| Send one test message | `nanobot agent -m "Hello!"` | First proof that install, config, provider, model, and workspace all work | +| Chat in the terminal | `nanobot agent` | Interactive local chat; exit with `exit`, `/exit`, `:q`, or `Ctrl+D` | +| Use WebUI or chat apps | `nanobot gateway` | Keep this terminal running while those surfaces are in use | +| Serve an OpenAI-compatible API | `nanobot serve` | Starts `/v1/chat/completions`, `/v1/models`, and `/health` | +| Check chat channel setup | `nanobot channels status` | Useful before starting `nanobot gateway` | +| Log in to QR/OAuth-style channels | `nanobot channels login ` | Used by channels such as WhatsApp and WeChat | +| Log in to OAuth model providers | `nanobot provider login ` | Used by OAuth providers such as OpenAI Codex and GitHub Copilot | + +## Global + +```bash +nanobot --help +nanobot --version +python -m nanobot --help +python -m nanobot --version +``` + +`python -m nanobot ...` is useful when the package is installed but the `nanobot` script is not on `PATH`. + +## Common Patterns + +Most day-to-day commands use the default config and workspace. Advanced or multi-instance runs usually pass both paths explicitly: + +```bash +nanobot agent --config ./bot-a/config.json --workspace ./bot-a/workspace -m "Hello" +nanobot gateway --config ./bot-a/config.json --workspace ./bot-a/workspace +nanobot serve --config ./bot-a/config.json --workspace ./bot-a/workspace +``` + +Use `--verbose` on long-running processes when you need startup or runtime logs: + +```bash +nanobot gateway --verbose +nanobot serve --verbose +``` + +Long-running commands keep working until you stop them. Press `Ctrl+C` in that terminal to stop `nanobot gateway` or `nanobot serve`. + +## Setup + +| Command | Description | +|---|---| +| `nanobot onboard` | Initialize or refresh the default config and workspace | +| `nanobot onboard --wizard` | Use the interactive setup wizard | +| `nanobot onboard --config --workspace ` | Initialize or refresh a specific instance | + +Default paths: + +| Path | Default | +|---|---| +| Config | `~/.nanobot/config.json` | +| Workspace | `~/.nanobot/workspace/` | + +## Agent CLI + +| Command | Description | +|---|---| +| `nanobot agent -m "Hello!"` | Send one message and exit | +| `nanobot agent` | Start interactive terminal chat | +| `nanobot agent --session ` | Use a specific session key | +| `nanobot agent --workspace ` | Override workspace | +| `nanobot agent --config ` | Use a specific config file | +| `nanobot agent --no-markdown` | Print plain text instead of Rich-rendered Markdown | +| `nanobot agent --logs` | Show runtime logs while chatting | + +Interactive mode exits with `exit`, `quit`, `/exit`, `/quit`, `:q`, or `Ctrl+D`. + +## Gateway + +`nanobot gateway` starts enabled chat channels, WebUI/WebSocket when configured, cron-backed system jobs, Dream, heartbeat, and the health endpoint. + +| Command | Description | +|---|---| +| `nanobot gateway` | Start the gateway with config defaults | +| `nanobot gateway --verbose` | Show verbose runtime output | +| `nanobot gateway --port ` | Override `gateway.port` for the health endpoint | +| `nanobot gateway --workspace ` | Override workspace | +| `nanobot gateway --config ` | Use a specific config file | + +Default health endpoint: + +```text +http://127.0.0.1:18790/health +``` + +The bundled WebUI is served by the WebSocket channel, usually on port `8765`, not by the gateway health endpoint. + +## OpenAI-Compatible API + +| Command | Description | +|---|---| +| `nanobot serve` | Start `/v1/chat/completions`, `/v1/models`, and `/health` | +| `nanobot serve --host ` | Override API bind host | +| `nanobot serve --port ` | Override API port | +| `nanobot serve --timeout ` | Override per-request timeout | +| `nanobot serve --verbose` | Show runtime logs | +| `nanobot serve --workspace ` | Override workspace | +| `nanobot serve --config ` | Use a specific config file | + +Default API endpoint: + +```text +http://127.0.0.1:8900 +``` + +See [`openai-api.md`](./openai-api.md) for request examples. + +## Status + +```bash +nanobot status +``` + +Shows the default config path, workspace path, active model, and provider summary. This command does not currently accept `--config`; use explicit `--config` and `--workspace` on `agent`, `gateway`, or `serve` when debugging a specific instance. + +## Channels + +| Command | Description | +|---|---| +| `nanobot channels status` | Show configured channel status | +| `nanobot channels status --config ` | Show channel status for a specific config | +| `nanobot channels login ` | Run interactive login for supported channels | +| `nanobot channels login --force` | Re-authenticate even if credentials already exist | +| `nanobot channels login --config ` | Use a specific config file | + +Examples: + +```bash +nanobot channels login whatsapp +nanobot channels login weixin +nanobot channels status +``` + +See [`chat-apps.md`](./chat-apps.md) for channel-specific setup. + +## Provider OAuth + +| Command | Description | +|---|---| +| `nanobot provider login openai-codex` | Authenticate OpenAI Codex provider | +| `nanobot provider login github-copilot` | Authenticate GitHub Copilot provider | +| `nanobot provider logout openai-codex` | Remove OpenAI Codex OAuth state | +| `nanobot provider logout github-copilot` | Remove GitHub Copilot OAuth state | + +See [`providers.md`](./providers.md#oauth-providers) for when OAuth providers need explicit provider/model selection. + +## Useful First Checks + +```bash +nanobot --version +nanobot status +nanobot agent -m "Hello!" +``` + +If these fail, use [`troubleshooting.md`](./troubleshooting.md) before debugging WebUI, chat apps, Docker, systemd, or SDK integrations. diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 000000000..405e65404 --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,151 @@ +# Concepts + +Use this page when you want to understand nanobot before changing advanced settings. It explains the moving parts without requiring you to read the source first. + +If you want source-file ownership and extension points, read [`architecture.md`](./architecture.md) after this page. + +## Runtime Shape + +nanobot has one small core loop and several ways to enter it: + +| Part | What it does | +|---|---| +| Agent loop | Builds context, selects the session, calls the provider, runs tools, and publishes replies | +| Providers | LLM backends such as OpenRouter, Anthropic, OpenAI, Bedrock, Ollama, vLLM, and other OpenAI-compatible APIs | +| Channels | User-facing transports such as CLI, WebUI/WebSocket, Telegram, Discord, Slack, Feishu, WeChat, Email, and others | +| Tools | Capabilities the model may call, including files, shell, web search/fetch, MCP, cron, image generation, and subagents | +| Memory | Workspace files and session history that keep useful context across turns | +| Gateway | Long-running process that connects enabled channels and serves the health endpoint | + +The simplest path is `nanobot agent -m "Hello!"`: one inbound message goes through the agent loop and prints the reply in your terminal. The long-running path is `nanobot gateway`: channels receive messages from chat apps or the WebUI, publish them to the same agent loop, and send replies back to the originating channel. + +## Config vs Workspace + +The default instance lives under `~/.nanobot/`: + +| Path | Meaning | +|---|---| +| `~/.nanobot/config.json` | Instance configuration: providers, model defaults, channels, tools, gateway, API, and runtime options | +| `~/.nanobot/workspace/` | Agent workspace: memory, sessions, heartbeat tasks, cron jobs, skills, and generated artifacts | + +You can override both with command flags: + +```bash +nanobot onboard --config ./bot-a/config.json --workspace ./bot-a/workspace +nanobot agent --config ./bot-a/config.json --workspace ./bot-a/workspace -m "Hello" +nanobot gateway --config ./bot-a/config.json --workspace ./bot-a/workspace +``` + +The config file controls what nanobot may use. The workspace is where nanobot keeps state for that instance. + +## Config Format + +`config.json` accepts both camelCase and snake_case keys. The docs use camelCase because nanobot writes config back to disk with camelCase aliases, for example `apiKey`, `modelPresets`, `intervalS`, and `maxToolResultChars`. + +Most examples are partial snippets. Merge them into the existing file created by `nanobot onboard`; do not replace the whole file unless you want to reset the instance. + +## One Agent Turn + +A normal turn follows this flow: + +1. A channel receives a user message and publishes it to the message bus. +2. The agent loop chooses a session key and builds context from the workspace, skills, memory, recent messages, channel metadata, and runtime settings. +3. The provider receives the model request. +4. If the model asks for tools, the runner executes them and feeds results back to the model. +5. The final reply is saved to the session and sent back through the channel. + +That flow is the same whether the message starts in the CLI, WebUI, Telegram, Discord, or another channel. + +## CLI, Gateway, API, and WebUI + +| Entry point | Command | Use it for | +|---|---|---| +| CLI one-shot | `nanobot agent -m "..."` | First-run checks, scripts, and quick local questions | +| CLI interactive | `nanobot agent` | Terminal chat with persistent session history | +| Gateway | `nanobot gateway` | Chat apps, WebUI, heartbeat, Dream, and long-running service mode | +| OpenAI-compatible API | `nanobot serve` | Programmatic access through `/v1/chat/completions` | +| WebUI | `nanobot gateway` plus WebSocket channel | Browser workbench served by the WebSocket channel on port `8765` | + +The gateway health endpoint is on `gateway.port` (`18790` by default). The browser WebUI is served by the WebSocket channel (`8765` by default), not by the health endpoint. + +## Provider and Model Selection + +The active model should normally come from a named `modelPresets` entry selected by `agents.defaults.modelPreset`. Direct `agents.defaults.provider` and `agents.defaults.model` still form the implicit `default` preset for older or minimal configs. The active provider is resolved in this order: + +1. If the active preset provider or implicit default provider is not `"auto"`, nanobot uses that provider. +2. If provider is `"auto"`, nanobot tries to infer the provider from the model name, configured API keys, local provider base URLs, or gateway providers. +3. OAuth providers such as OpenAI Codex and GitHub Copilot require explicit login and explicit provider/model selection inside the active preset. + +Pin the provider inside the preset when setting up for the first time. It is easier to debug: + +```json +{ + "modelPresets": { + "primary": { + "provider": "openrouter", + "model": "anthropic/claude-opus-4.5" + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +See [`providers.md`](./providers.md) for practical examples and [`configuration.md#providers`](./configuration.md#providers) for the full provider reference. + +## Channels and Sessions + +Each channel maps inbound messages to a session key. That lets independent conversations keep separate history. The WebUI also supports multiple chats and workspace-scoped metadata for project workspaces. + +`agents.defaults.unifiedSession` can intentionally share one session across channels for a single-user multi-device setup. Leave it off if you expect separate people, groups, channels, or projects to keep separate context. + +## Memory, Sessions, and Dream + +nanobot uses two related stores: + +| Store | Location | Purpose | +|---|---|---| +| Sessions | `/sessions/*.jsonl` | Recent conversation turns replayed into context | +| Memory | `/memory/MEMORY.md` and `/memory/history.jsonl` | Long-term facts and consolidated history | + +Dream is a periodic consolidation job. It reads accumulated history and updates workspace memory so useful context can survive beyond short session replay. + +See [`memory.md`](./memory.md) for the detailed design. + +## Tools and Safety + +Tools are discovered automatically from built-in modules and plugin entry points. Common tool groups include: + +- file read/write/edit and patching; +- shell execution with configurable sandboxing; +- web search and web fetch with SSRF checks; +- MCP servers; +- cron reminders and heartbeat tasks; +- image generation; +- subagents and runtime self-inspection. + +Security-sensitive controls live in [`configuration.md#security`](./configuration.md#security). For production or shared chat apps, also configure channel access controls such as `allowFrom`, pairing, or WebSocket tokens. + +## Background Jobs + +When `nanobot gateway` starts, it creates workspace-scoped cron storage at `/cron/jobs.json` and registers system jobs: + +- `dream`, when `agents.defaults.dream.enabled` is true; +- `heartbeat`, when `gateway.heartbeat.enabled` is true. + +Heartbeat reads `/HEARTBEAT.md`. If the file has tasks under `## Active Tasks`, nanobot executes them and sends useful results to the most recently active chat target. + +User-created reminders use the same cron service but are not the same as the protected heartbeat system job. + +## Where to Go Next + +| Need | Read | +|---|---| +| First working install | [`quick-start.md`](./quick-start.md) | +| Provider/model setup | [`providers.md`](./providers.md) | +| Chat app setup | [`chat-apps.md`](./chat-apps.md) | +| Complete config reference | [`configuration.md`](./configuration.md) | +| Runtime debugging | [`troubleshooting.md`](./troubleshooting.md) | diff --git a/docs/configuration.md b/docs/configuration.md index 1fbbd5db5..5bb54b53a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,10 +2,53 @@ Config file: `~/.nanobot/config.json` +This is the full reference. If this is your first install, start with [`quick-start.md`](./quick-start.md). If you are trying to choose a model or fix provider/model matching, use [`providers.md`](./providers.md) first and come back here for exact fields and advanced options. + +The JSON examples below are usually partial snippets to merge into your existing config, not full replacement files. For the mental model behind config, workspace, gateway, channels, sessions, tools, and memory, see [`concepts.md`](./concepts.md). + +The generated `config.json` uses camelCase keys such as `apiKey` and `intervalS`. snake_case keys are also accepted for compatibility, but the docs prefer camelCase because that is what nanobot writes back to disk. + +For setup and runtime failures, follow the diagnosis order in [`troubleshooting.md`](./troubleshooting.md) before changing multiple config areas at once. + > [!NOTE] -> If your config file is older than the current schema, you can refresh it without overwriting your existing values: -> run `nanobot onboard`, then answer `N` when asked whether to overwrite the config. -> nanobot will merge in missing default fields and keep your current settings. +> If your config file is older than the current schema, you can refresh it without overwriting your existing values: run `nanobot onboard`, then answer `N` when asked whether to overwrite the config. nanobot will merge in missing default fields and keep your current settings. + +## Quick Jump + +| Need | Section | +|---|---| +| Keep secrets out of `config.json` | [Environment Variables for Secrets](#environment-variables-for-secrets) | +| Trace model calls | [Langfuse Observability](#langfuse-observability) | +| Configure credentials and endpoints | [Providers](#providers) | +| Name and switch model choices | [Model Presets](#model-presets) | +| Add fallback chains | [Model Fallbacks](#model-fallbacks) | +| Configure voice transcription | [Transcription Settings](#transcription-settings) | +| Tune channel defaults | [Channel Settings](#channel-settings) | +| Configure web search and fetch | [Web Tools](#web-tools) | +| Enable image generation | [Image Generation](#image-generation) | +| Add MCP servers | [MCP](#mcp-model-context-protocol) | +| Review shell, workspace, and SSRF controls | [Security](#security) | +| Control access and pairing | [Pairing](#pairing) | +| Tune gateway jobs, sessions, and tools | [Gateway Heartbeat](#gateway-heartbeat), [Auto Compact](#auto-compact), [Unified Session](#unified-session), [Tool Hint Max Length](#tool-hint-max-length) | + +## Where to Edit First + +If you are not sure where a setting belongs, start from the task you are trying to complete. Most changes touch one config section and one verification command. + +| Task | First keys to check | Verify with | Deep dive | +|---|---|---|---| +| Make the first model reply work | `providers..apiKey`, optional `providers..apiBase`, `modelPresets.`, `agents.defaults.modelPreset` | `nanobot status`, then `nanobot agent -m "Hello!"` | [Providers](#providers), [Model Presets](#model-presets) | +| Add fallback models | `modelPresets.`, `agents.defaults.fallbackModels` | `nanobot status`, then a normal agent run | [Model Fallbacks](#model-fallbacks) | +| Keep secrets out of the config file | `${ENV_VAR}` placeholders inside any string value | Start nanobot from the same environment that sets the variable | [Environment Variables for Secrets](#environment-variables-for-secrets) | +| Open the bundled WebUI | `channels.websocket.enabled`, optional `channels.websocket.port`, `channels.websocket.tokenIssueSecret` | `nanobot gateway`, then open `http://127.0.0.1:8765` | [Channel Settings](#channel-settings), [WebSocket docs](./websocket.md) | +| Connect one chat app | `channels..enabled`, channel credentials, `channels..allowFrom` | `nanobot channels status`, then `nanobot gateway --verbose` | [Channel Settings](#channel-settings), [Chat Apps](./chat-apps.md) | +| Enable voice transcription | `transcription.enabled`, `transcription.provider`, matching `providers..apiKey` | Send or upload a short voice message through a configured surface | [Transcription Settings](#transcription-settings) | +| Enable web search or fetch | `tools.web.search.*`, `tools.web.fetch.*`, optional `tools.ssrfWhitelist` | Ask a question that requires current web information, then inspect logs if needed | [Web Tools](#web-tools), [Security](#security) | +| Enable image generation | `tools.imageGeneration.enabled`, `tools.imageGeneration.provider`, `tools.imageGeneration.model`, matching provider credentials | Enable Image Generation in the WebUI and send one image request | [Image Generation](#image-generation) | +| Add external tools through MCP | `tools.mcpServers.` | Start `nanobot gateway --verbose` and check startup/tool logs | [MCP](#mcp-model-context-protocol) | +| Tighten tool and network safety | `tools.restrictToWorkspace`, `tools.exec.sandbox`, `tools.ssrfWhitelist`, `channels.*.allowFrom` | Run the same workflow through the channel or CLI you plan to expose | [Security](#security), [Pairing](#pairing) | +| Run multiple isolated bots | separate `--config` and `--workspace` paths, plus distinct `gateway.port` or channel ports when processes run together | Start each process with explicit paths and run `nanobot status` for the default instance only | [Multiple Instances](./multiple-instances.md), [CLI Reference](./cli-reference.md) | +| Observe model calls | `LANGFUSE_SECRET_KEY`, `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_BASE_URL` environment variables | Run one model call, then check the matching Langfuse project | [Langfuse Observability](#langfuse-observability) | ## Environment Variables for Secrets @@ -116,14 +159,46 @@ ANTHROPIC_API_KEY="$(pass show api/anthropic)" nanobot agent ANTHROPIC_API_KEY="$(bw get password api/anthropic)" nanobot agent ``` +## Langfuse Observability + +nanobot can trace OpenAI-compatible provider calls through Langfuse's OpenAI SDK wrapper. This is configured with environment variables, not `config.json`. + +Install the optional package in the same Python environment that runs nanobot: + +```bash +python -m pip install langfuse +``` + +Set Langfuse credentials before starting `nanobot agent`, `nanobot gateway`, or `nanobot serve`: + +```bash +export LANGFUSE_SECRET_KEY="sk-lf-..." +export LANGFUSE_PUBLIC_KEY="pk-lf-..." +export LANGFUSE_BASE_URL="https://cloud.langfuse.com" +``` + +For PowerShell: + +```powershell +$env:LANGFUSE_SECRET_KEY = "sk-lf-..." +$env:LANGFUSE_PUBLIC_KEY = "pk-lf-..." +$env:LANGFUSE_BASE_URL = "https://cloud.langfuse.com" +``` + +When `LANGFUSE_SECRET_KEY` is set and the `langfuse` package is installed, nanobot uses `langfuse.openai.AsyncOpenAI` for OpenAI-compatible providers so model requests are sent to Langfuse in the background. If the secret key is set but `langfuse` is missing, nanobot logs a warning and falls back to the regular OpenAI client. + +Use the Langfuse region or self-hosted URL that matches your project. The [Langfuse OpenAI SDK docs](https://langfuse.com/integrations/model-providers/openai-py) use `LANGFUSE_BASE_URL` for cloud regions and self-hosted instances. + +Tracing covers the providers that go through nanobot's OpenAI-compatible client path. Native providers that do not use that client may not produce Langfuse OpenAI-wrapper traces. + ## Providers > [!TIP] -> - **Voice transcription**: Voice messages and WebUI/desktop microphone input use the shared top-level `transcription` settings. By default Groq Whisper is used; set `transcription.provider` to `"openai"` for OpenAI Whisper, `"openrouter"` for OpenRouter speech-to-text models, `"xiaomi_mimo"` for Xiaomi MiMo ASR, or `"assemblyai"` for AssemblyAI. API keys still live in the matching `providers.` config. +> - **Voice transcription**: Voice messages and WebUI/desktop microphone input use the shared top-level `transcription` settings. The default `transcription.provider` value is `"groq"`; set it to `"openai"` for OpenAI Whisper, `"openrouter"` for OpenRouter speech-to-text models, `"xiaomi_mimo"` for Xiaomi MiMo ASR, or `"assemblyai"` for AssemblyAI. API keys still live in the matching `providers.` config. > - **MiniMax Coding Plan**: Exclusive discount links for the nanobot community: [Overseas](https://platform.minimax.io/subscribe/coding-plan?code=9txpdXw04g&source=link) · [Mainland China](https://platform.minimaxi.com/subscribe/token-plan?code=GILTJpMTqZ&source=link) > - **MiniMax (Mainland China)**: If your API key is from MiniMax's mainland China platform (minimaxi.com), set `"apiBase": "https://api.minimaxi.com/v1"` in your minimax provider config. -> - **MiniMax thinking mode**: Use `providers.minimaxAnthropic` when you want `reasoningEffort` / thinking mode. MiniMax exposes that capability through its Anthropic-compatible endpoint, so nanobot keeps it as a separate provider instead of guessing MiniMax-specific thinking parameters on the generic OpenAI-compatible `minimax` endpoint. It uses the same `MINIMAX_API_KEY`. Default Anthropic-compatible base URL: `https://api.minimax.io/anthropic`; for mainland China use `https://api.minimaxi.com/anthropic`. -> - **VolcEngine / BytePlus Coding Plan**: Use dedicated providers `volcengineCodingPlan` or `byteplusCodingPlan` instead of the pay-per-use `volcengine` / `byteplus` providers. +> - **MiniMax thinking mode**: `providers.minimaxAnthropic` is the config block for `reasoningEffort` / thinking mode. MiniMax exposes that capability through its Anthropic-compatible endpoint, so nanobot keeps it as a separate provider instead of guessing MiniMax-specific thinking parameters on the generic OpenAI-compatible `minimax` endpoint. It uses the same `MINIMAX_API_KEY`. Default Anthropic-compatible base URL: `https://api.minimax.io/anthropic`; for mainland China use `https://api.minimaxi.com/anthropic`. +> - **VolcEngine / BytePlus Coding Plan**: Subscription endpoints are configured through dedicated providers `volcengineCodingPlan` or `byteplusCodingPlan`, separate from the pay-per-use `volcengine` / `byteplus` providers. > - **Zhipu Coding Plan**: If you're on Zhipu's coding plan, set `"apiBase": "https://open.bigmodel.cn/api/coding/paas/v4"` in your zhipu provider config. > - **Alibaba Cloud BaiLian**: If you're using Alibaba Cloud BaiLian's OpenAI-compatible endpoint, set `"apiBase": "https://dashscope.aliyuncs.com/compatible-mode/v1"` in your dashscope provider config. > - **StepFun Step Plan**: If you're on StepFun's Step Plan subscription, set `"apiBase": "https://api.stepfun.com/step_plan/v1"` in your stepfun provider config. Supported models include `step-3.5-flash`, `step-3.5-flash-2603`, and `step-router-v1`. @@ -134,11 +209,13 @@ ANTHROPIC_API_KEY="$(bw get password api/anthropic)" nanobot agent | Provider | Purpose | Get API Key | |----------|---------|-------------| | `custom` | Any OpenAI-compatible endpoint | — | -| `openrouter` | LLM (recommended, access to all models) + Voice transcription (STT models) | [openrouter.ai](https://openrouter.ai) | +| `openrouter` | LLM gateway for hosted model families + Voice transcription (STT models) | [openrouter.ai](https://openrouter.ai) | | `huggingface` | LLM (Hugging Face Inference Providers) | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) | | `skywork` | LLM (Skywork / APIFree API gateway) | [apifree.ai](https://www.apifree.ai) | | `volcengine` | LLM (VolcEngine, pay-per-use) | [Coding Plan](https://www.volcengine.com/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) · [volcengine.com](https://www.volcengine.com) | +| `volcengine_coding_plan` | LLM (VolcEngine Coding Plan subscription endpoint) | [volcengine.com](https://www.volcengine.com/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) | | `byteplus` | LLM (VolcEngine international, pay-per-use) | [Coding Plan](https://www.byteplus.com/en/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) · [byteplus.com](https://www.byteplus.com) | +| `byteplus_coding_plan` | LLM (BytePlus Coding Plan subscription endpoint) | [byteplus.com](https://www.byteplus.com/en/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) | | `anthropic` | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) | | `azure_openai` | LLM (Azure OpenAI) | [portal.azure.com](https://portal.azure.com) | | `bedrock` | LLM (AWS Bedrock Converse, Claude/Nova/Llama/etc.) | [aws.amazon.com/bedrock](https://aws.amazon.com/bedrock/) | @@ -155,7 +232,7 @@ ANTHROPIC_API_KEY="$(bw get password api/anthropic)" nanobot agent | `dashscope` | LLM (Qwen) | [dashscope.console.aliyun.com](https://dashscope.console.aliyun.com) | | `moonshot` | LLM (Moonshot/Kimi) | [platform.moonshot.cn](https://platform.moonshot.cn) | | `zhipu` | LLM (Zhipu GLM) | [open.bigmodel.cn](https://open.bigmodel.cn) | -| `mimo` | LLM (MiMo) | [platform.xiaomimimo.com](https://platform.xiaomimimo.com) | +| `xiaomi_mimo` | LLM (MiMo) | [platform.xiaomimimo.com](https://platform.xiaomimimo.com) | | `longcat` | LLM (LongCat) | [longcat.chat](https://longcat.chat/platform/docs/zh/) | | `ant_ling` | LLM (Ant Ling / 蚂蚁百灵) | [developer.ant-ling.com](https://developer.ant-ling.com/en/docs/api-reference/openai/) | | `ollama` | LLM (local, Ollama) | — | @@ -165,6 +242,7 @@ ANTHROPIC_API_KEY="$(bw get password api/anthropic)" nanobot agent | `stepfun` | LLM (Step Fun/阶跃星辰) | [platform.stepfun.com](https://platform.stepfun.com) | | `ovms` | LLM (local, OpenVINO Model Server) | [docs.openvino.ai](https://docs.openvino.ai/2026/model-server/ovms_docs_llm_quickstart.html) | | `vllm` | LLM (local, any OpenAI-compatible server) | — | +| `nvidia` | LLM (NVIDIA NIM) | [build.nvidia.com](https://build.nvidia.com/) | | `openai_codex` | LLM (Codex, OAuth) | `nanobot provider login openai-codex` | | `github_copilot` | LLM (GitHub Copilot, OAuth) | `nanobot provider login github-copilot` | | `qianfan` | LLM (Baidu Qianfan) | [cloud.baidu.com](https://cloud.baidu.com/doc/qianfan/s/Hmh4suq26) | @@ -221,11 +299,16 @@ The `azure_openai` provider talks to your Azure OpenAI resource via the OpenAI * "apiBase": "https://my-resource.openai.azure.com" } }, - "agents": { - "defaults": { + "modelPresets": { + "azure": { "provider": "azure_openai", "model": "my-gpt-5-deployment" } + }, + "agents": { + "defaults": { + "modelPreset": "azure" + } } } ``` @@ -241,11 +324,16 @@ Omit `apiKey` (or leave it empty / unset). The provider falls back to [`DefaultA "apiBase": "https://my-resource.openai.azure.com" } }, - "agents": { - "defaults": { + "modelPresets": { + "azure": { "provider": "azure_openai", "model": "my-gpt-5-deployment" } + }, + "agents": { + "defaults": { + "modelPreset": "azure" + } } } ``` @@ -253,7 +341,7 @@ Omit `apiKey` (or leave it empty / unset). The provider falls back to [`DefaultA Install the optional dependency: ```bash -pip install 'nanobot-ai[azure]' +python -m pip install 'nanobot-ai[azure]' ``` `DefaultAzureCredential` walks this chain in order and uses the first identity that succeeds: @@ -268,15 +356,14 @@ pip install 'nanobot-ai[azure]' The identity that ends up signing the request **must be assigned the `Cognitive Services OpenAI User` RBAC role** (or higher) on the Azure OpenAI resource. Without that role you will see `401`/`403` errors at the first request. -> `apiBase` remains mandatory in both modes — it's your Azure resource endpoint and cannot be inferred. If neither `apiKey` is set nor `azure-identity` is installed, the provider raises a clear error pointing you at `pip install 'nanobot-ai[azure]'`. +> `apiBase` remains mandatory in both modes — it's your Azure resource endpoint and cannot be inferred. If neither `apiKey` is set nor `azure-identity` is installed, the provider raises a clear error pointing you at `python -m pip install 'nanobot-ai[azure]'`.
Skywork / APIFree -Skywork uses APIFree's OpenAI-compatible Agent API endpoint. Configure the provider -once, then use Skywork model IDs such as `skywork-ai/skyclaw-v1`. +Skywork uses APIFree's OpenAI-compatible Agent API endpoint. Configure the provider once, then use Skywork model IDs such as `skywork-ai/skyclaw-v1`. ```json { @@ -286,19 +373,23 @@ once, then use Skywork model IDs such as `skywork-ai/skyclaw-v1`. "apiBase": "https://api.apifree.ai/agent/v1" } }, - "agents": { - "defaults": { + "modelPresets": { + "skywork": { "provider": "skywork", "model": "skywork-ai/skyclaw-v1", "maxTokens": 32768, "contextWindowTokens": 131072 } + }, + "agents": { + "defaults": { + "modelPreset": "skywork" + } } } ``` -You can also reference `${APIFREE_API_KEY}` in `apiKey` if that is how your -environment names the credential. +You can also reference `${APIFREE_API_KEY}` in `apiKey` if that is how your environment names the credential.
@@ -344,12 +435,17 @@ For a non-Anthropic model such as Amazon Nova: "region": "us-east-1" } }, - "agents": { - "defaults": { + "modelPresets": { + "bedrockNova": { "provider": "bedrock", "model": "bedrock/amazon.nova-lite-v1:0", "reasoningEffort": null } + }, + "agents": { + "defaults": { + "modelPreset": "bedrockNova" + } } } ``` @@ -364,12 +460,17 @@ With a Bedrock API key: "apiKey": "${AWS_BEARER_TOKEN_BEDROCK}" } }, - "agents": { - "defaults": { + "modelPresets": { + "bedrockNova": { "provider": "bedrock", "model": "bedrock/amazon.nova-lite-v1:0", "reasoningEffort": null } + }, + "agents": { + "defaults": { + "modelPreset": "bedrockNova" + } } } ``` @@ -384,11 +485,16 @@ With a named AWS profile: "profile": "my-bedrock-profile" } }, - "agents": { - "defaults": { + "modelPresets": { + "bedrockNova": { "provider": "bedrock", "model": "bedrock/amazon.nova-lite-v1:0" } + }, + "agents": { + "defaults": { + "modelPreset": "bedrockNova" + } } } ``` @@ -402,13 +508,18 @@ With a named AWS profile: "region": "us-east-1" } }, - "agents": { - "defaults": { + "modelPresets": { + "bedrockClaude": { "provider": "bedrock", "model": "bedrock/global.anthropic.claude-opus-4-7", "reasoningEffort": "medium", "maxTokens": 8192 } + }, + "agents": { + "defaults": { + "modelPreset": "bedrockClaude" + } } } ``` @@ -483,8 +594,7 @@ nanobot agent -m "Reply with one short sentence."
OpenAI Codex (OAuth) -Codex uses OAuth instead of API keys. Requires a ChatGPT Plus or Pro account. -No `providers.openaiCodex` block is needed in `config.json`; `nanobot provider login` stores the OAuth session outside config. +Codex uses OAuth instead of API keys. Requires a ChatGPT Plus or Pro account. No `providers.openaiCodex` block is needed in `config.json`; `nanobot provider login` stores the OAuth session outside config. **1. Login:** ```bash @@ -494,9 +604,15 @@ nanobot provider login openai-codex **2. Set model** (merge into `~/.nanobot/config.json`): ```json { + "modelPresets": { + "codex": { + "provider": "openai_codex", + "model": "openai-codex/gpt-5.1-codex" + } + }, "agents": { "defaults": { - "model": "openai-codex/gpt-5.1-codex" + "modelPreset": "codex" } } } @@ -521,8 +637,7 @@ nanobot agent -c ~/.nanobot-telegram/config.json -w /tmp/nanobot-telegram-test -
GitHub Copilot (OAuth) -GitHub Copilot uses OAuth instead of API keys. Requires a [GitHub account with a plan](https://github.com/features/copilot/plans) configured. -No `providers.githubCopilot` block is needed in `config.json`; `nanobot provider login` stores the OAuth session outside config. +GitHub Copilot uses OAuth instead of API keys. Requires a [GitHub account with a plan](https://github.com/features/copilot/plans) configured. No `providers.githubCopilot` block is needed in `config.json`; `nanobot provider login` stores the OAuth session outside config. **1. Login:** ```bash @@ -532,9 +647,15 @@ nanobot provider login github-copilot **2. Set model** (merge into `~/.nanobot/config.json`): ```json { + "modelPresets": { + "copilot": { + "provider": "github_copilot", + "model": "github-copilot/gpt-4.1" + } + }, "agents": { "defaults": { - "model": "github-copilot/gpt-4.1" + "modelPreset": "copilot" } } } @@ -558,9 +679,7 @@ nanobot agent -c ~/.nanobot-telegram/config.json -w /tmp/nanobot-telegram-test -
LongCat (OpenAI-compatible) -LongCat is available through nanobot's built-in OpenAI-compatible provider flow. -The default API base already points to `https://api.longcat.chat/openai/v1`, so you -usually only need to set `apiKey`. +LongCat is available through nanobot's built-in OpenAI-compatible provider flow. The default API base already points to `https://api.longcat.chat/openai/v1`, so you usually only need to set `apiKey`. ```json { @@ -569,29 +688,32 @@ usually only need to set `apiKey`. "apiKey": "${LONGCAT_API_KEY}" } }, + "modelPresets": { + "longcat": { + "provider": "longcat", + "model": "LongCat-2.0-Preview", + "maxTokens": 8192, + "contextWindowTokens": 1048576 + } + }, "agents": { "defaults": { - "provider": "longcat", - "model": "LongCat-Flash-Chat" + "modelPreset": "longcat" } } } ``` -Official model names include `LongCat-Flash-Chat`, `LongCat-Flash-Thinking`, -`LongCat-Flash-Thinking-2601`, and `LongCat-Flash-Lite`. +Current LongCat API docs list `LongCat-2.0-Preview` as the supported model. The older `LongCat-Flash-*` models were retired by LongCat on 2026-05-29.
Xiaomi MiMo -Xiaomi MiMo models are automatically detected by the `xiaomi_mimo` provider when -the model name contains `mimo`. The default API base is -`https://api.xiaomimimo.com/v1`. +Xiaomi MiMo models are automatically detected by the `xiaomi_mimo` provider when the model name contains `mimo`. The default API base is `https://api.xiaomimimo.com/v1`. -> **Token Plan**: If you're using MiMo's token plan, override `apiBase` with the -> dedicated endpoint: +> **Token Plan**: If you're using MiMo's token plan, override `apiBase` with the dedicated endpoint: > > ```json > { @@ -601,27 +723,28 @@ the model name contains `mimo`. The default API base is > "apiBase": "https://token-plan-sgp.xiaomimimo.com/v1" > } > }, +> "modelPresets": { +> "mimo": { +> "provider": "xiaomi_mimo", +> "model": "xiaomi/mimo-v2.5-pro" +> } +> }, > "agents": { > "defaults": { -> "model": "xiaomi/mimo-v2.5-pro" +> "modelPreset": "mimo" > } > } > } > ``` > -> No need to set `provider` explicitly — the model name contains `mimo`, which -> auto-matches to the `xiaomi_mimo` provider spec. Use an API key from the MiMo -> token plan console and check the MiMo platform for the latest supported model -> names. +> Use the model ID and API key from the MiMo token plan console, and check the MiMo platform for the latest supported model names.
StepFun Step Plan (subscription) -Step Plan is StepFun's subscription-based service for high-frequency AI developers. -If you're on a Step Plan subscription, override `apiBase` in the existing `stepfun` -provider config to point to the dedicated Step Plan endpoint. +Step Plan is StepFun's subscription-based service for high-frequency AI developers. If you're on a Step Plan subscription, override `apiBase` in the existing `stepfun` provider config to point to the dedicated Step Plan endpoint. ```json { @@ -631,26 +754,28 @@ provider config to point to the dedicated Step Plan endpoint. "apiBase": "https://api.stepfun.com/step_plan/v1" } }, - "agents": { - "defaults": { + "modelPresets": { + "stepfun": { "provider": "stepfun", "model": "step-3.5-flash" } + }, + "agents": { + "defaults": { + "modelPreset": "stepfun" + } } } ``` -Supported models include `step-3.5-flash`, `step-3.5-flash-2603`, and -`step-router-v1`. +Supported models include `step-3.5-flash`, `step-3.5-flash-2603`, and `step-router-v1`.
Ant Ling (OpenAI-compatible) -Ant Ling is available through nanobot's built-in OpenAI-compatible provider flow. -The default API base points to `https://api.ant-ling.com/v1`, so you usually -only need to set `apiKey`. +Ant Ling is available through nanobot's built-in OpenAI-compatible provider flow. The default API base points to `https://api.ant-ling.com/v1`, so you usually only need to set `apiKey`. ```json { @@ -659,17 +784,21 @@ only need to set `apiKey`. "apiKey": "${ANT_LING_API_KEY}" } }, - "agents": { - "defaults": { + "modelPresets": { + "antLing": { "provider": "ant_ling", "model": "Ling-2.6-flash" } + }, + "agents": { + "defaults": { + "modelPreset": "antLing" + } } } ``` -Official OpenAI-compatible model names include `Ling-2.6-1T`, -`Ling-2.6-flash`, `Ling-2.5-1T`, `Ling-1T`, `Ring-2.5-1T`, and `Ring-1T`. +Official OpenAI-compatible model names include `Ling-2.6-1T`, `Ling-2.6-flash`, `Ling-2.5-1T`, `Ling-1T`, `Ring-2.5-1T`, and `Ring-1T`.
@@ -686,9 +815,15 @@ Connects directly to any OpenAI-compatible endpoint — llama.cpp, Together AI, "apiBase": "https://api.your-provider.com/v1" } }, + "modelPresets": { + "custom": { + "provider": "custom", + "model": "your-model-name" + } + }, "agents": { "defaults": { - "model": "your-model-name" + "modelPreset": "custom" } } } @@ -698,7 +833,7 @@ Connects directly to any OpenAI-compatible endpoint — llama.cpp, Together AI, > > `custom` is the right choice for providers that expose an OpenAI-compatible **chat completions** API. It does **not** force third-party endpoints onto the OpenAI/Azure **Responses API**. > -> If your proxy or gateway is specifically Responses-API-compatible, use the `azure_openai` provider shape instead and point `apiBase` at that endpoint: +> If your proxy or gateway is specifically Responses-API-compatible, configure the `azure_openai` provider shape and point `apiBase` at that endpoint: > > ```json > { @@ -709,11 +844,16 @@ Connects directly to any OpenAI-compatible endpoint — llama.cpp, Together AI, > "defaultModel": "your-model-name" > } > }, -> "agents": { -> "defaults": { +> "modelPresets": { +> "responsesProxy": { > "provider": "azure_openai", > "model": "your-model-name" > } +> }, +> "agents": { +> "defaults": { +> "modelPreset": "responsesProxy" +> } > } > } > ``` @@ -761,16 +901,21 @@ ollama run llama3.2 "apiBase": "http://localhost:11434" } }, - "agents": { - "defaults": { + "modelPresets": { + "ollama": { "provider": "ollama", "model": "llama3.2" } + }, + "agents": { + "defaults": { + "modelPreset": "ollama" + } } } ``` -> `provider: "auto"` also works when `providers.ollama.apiBase` is configured, but setting `"provider": "ollama"` is the clearest option. +> `provider: "auto"` also works when `providers.ollama.apiBase` is configured, but pinning `"provider": "ollama"` inside the preset is the clearest option.
@@ -794,17 +939,21 @@ ollama run llama3.2 "apiBase": "http://localhost:1234/v1" } }, - "agents": { - "defaults": { + "modelPresets": { + "lmStudio": { "provider": "lm_studio", "model": "local-model" } + }, + "agents": { + "defaults": { + "modelPreset": "lmStudio" + } } } ``` -> **Note:** Set `apiKey` to `null` for LM Studio since it runs locally and doesn't require authentication. The model name should match what's shown in the LM Studio UI. -> `provider: "auto"` also works when `providers.lm_studio.apiBase` is configured, but setting `"provider": "lm_studio"` is the clearest option. +> **Note:** Set `apiKey` to `null` for LM Studio since it runs locally and doesn't require authentication. The model name should match what's shown in the LM Studio UI. `provider: "auto"` also works when `providers.lm_studio.apiBase` is configured, but pinning `"provider": "lm_studio"` inside the preset is the clearest option.
@@ -812,7 +961,7 @@ ollama run llama3.2
Atomic Chat (local) -[Atomic Chat](https://atomic.chat/) is a local-first desktop app that exposes an **OpenAI-compatible** HTTP API (default `http://localhost:1337/v1`). Use it when you want to run nanobot against a model on your own machine instead of a hosted API provider. +[Atomic Chat](https://atomic.chat/) is a local-first desktop app that exposes an **OpenAI-compatible** HTTP API (default `http://localhost:1337/v1`). This setup applies when you want to run nanobot against a model on your own machine instead of a hosted API provider. **1. Start Atomic Chat** @@ -830,18 +979,23 @@ ollama run llama3.2 "apiBase": "http://localhost:1337/v1" } }, - "agents": { - "defaults": { + "modelPresets": { + "atomic": { "provider": "atomic_chat", "model": "qwen3-32b" } + }, + "agents": { + "defaults": { + "modelPreset": "atomic" + } } } ``` > **Note:** Replace `qwen3-32b` with the model ID from Atomic Chat. Set `apiKey` to `null` if your Atomic Chat server does not require a key. If it does, set `apiKey` (or the `ATOMIC_CHAT_API_KEY` environment variable) to the value Atomic Chat expects. -> `provider: "auto"` also works when `providers.atomic_chat.apiBase` is configured, but setting `"provider": "atomic_chat"` is the clearest option. +> `provider: "auto"` also works when `providers.atomic_chat.apiBase` is configured, but pinning `"provider": "atomic_chat"` inside the preset is the clearest option.
@@ -907,17 +1061,21 @@ docker run -d \ "apiBase": "http://localhost:8000/v3" } }, - "agents": { - "defaults": { + "modelPresets": { + "ovms": { "provider": "ovms", "model": "openai/gpt-oss-20b" } + }, + "agents": { + "defaults": { + "modelPreset": "ovms" + } } } ``` -> OVMS is a local server — no API key required. Supports tool calling (`--tool_parser gptoss`), reasoning (`--reasoning_parser gptoss`), and streaming. -> See the [official OVMS docs](https://docs.openvino.ai/2026/model-server/ovms_docs_llm_quickstart.html) for more details. +> OVMS is a local server — no API key required. Supports tool calling (`--tool_parser gptoss`), reasoning (`--reasoning_parser gptoss`), and streaming. See the [official OVMS docs](https://docs.openvino.ai/2026/model-server/ovms_docs_llm_quickstart.html) for more details. @@ -945,12 +1103,18 @@ vllm serve meta-llama/Llama-3.1-8B-Instruct --port 8000 } ``` -*Model:* +*Model preset:* ```json { + "modelPresets": { + "vllm": { + "provider": "vllm", + "model": "meta-llama/Llama-3.1-8B-Instruct" + } + }, "agents": { "defaults": { - "model": "meta-llama/Llama-3.1-8B-Instruct" + "modelPreset": "vllm" } } } @@ -958,31 +1122,34 @@ vllm serve meta-llama/Llama-3.1-8B-Instruct --port 8000 -Contributor notes for adding new providers live in -[`development.md`](./development.md#adding-an-llm-provider). +Contributor notes for adding new providers live in [`development.md`](./development.md#adding-an-llm-provider). ## Model Presets -Model presets let you name a complete model configuration and switch it at runtime with `/model `. +Model presets let you name a complete model configuration and switch it at runtime with `/model `. They are the recommended way to configure models because the same names can be reused for startup selection, chat-command switching, and fallback chains. -Existing configs do not need to change. If you do not set `modelPresets` or `agents.defaults.modelPreset`, nanobot keeps using `agents.defaults.*` exactly as before. +Existing configs do not need to change. Direct `agents.defaults.model`, `provider`, `maxTokens`, `contextWindowTokens`, `temperature`, and `reasoningEffort` fields still define the implicit `default` preset. For new configs, prefer top-level `modelPresets` plus `agents.defaults.modelPreset`. ```json { + "modelPresets": { + "fast": { + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536 + } + }, "agents": { "defaults": { - "model": "openai/gpt-4.1", - "provider": "openai", - "maxTokens": 8192, - "contextWindowTokens": 128000, - "temperature": 0.1, "modelPreset": "fast", - "fallbackModels": ["deep"] + "fallbackModels": ["deep", "localSmall"] } }, "modelPresets": { "fast": { - "model": "openai/gpt-4.1-mini", + "label": "Fast", + "model": "gpt-4.1-mini", "provider": "openai", "maxTokens": 4096, "contextWindowTokens": 128000, @@ -990,11 +1157,20 @@ Existing configs do not need to change. If you do not set `modelPresets` or `age "reasoningEffort": "low" }, "deep": { - "model": "anthropic/claude-opus-4-5", + "label": "Deep", + "model": "claude-opus-4-5", "provider": "anthropic", "maxTokens": 8192, "contextWindowTokens": 200000, "reasoningEffort": "high" + }, + "localSmall": { + "label": "Local Small", + "model": "llama3.2", + "provider": "ollama", + "maxTokens": 4096, + "contextWindowTokens": 32768, + "temperature": 0.2 } } } @@ -1004,6 +1180,7 @@ Existing configs do not need to change. If you do not set `modelPresets` or `age | Field | Description | |-------|-------------| +| `label` | Optional display name shown in model lists. | | `model` | Model name to use for this preset. | | `provider` | Provider name, or `"auto"` to use provider auto-detection. | | `maxTokens` | Maximum completion/output tokens. | @@ -1011,24 +1188,72 @@ Existing configs do not need to change. If you do not set `modelPresets` or `age | `temperature` | Sampling temperature. | | `reasoningEffort` | Optional reasoning/thinking setting. Provider support varies. | -`default` is reserved and always means the implicit preset built from `agents.defaults.*`; do not define `modelPresets.default`. Use `/model default` to switch back to `agents.defaults.*`. +`default` is reserved and always means the implicit preset built from direct `agents.defaults.*` fields; do not define `modelPresets.default`. Use `/model default` to switch back to those direct fields in an existing config. + +Set `agents.defaults.modelPreset` to choose the startup preset. When `modelPreset` is `null` or omitted, startup uses the implicit `default` preset from direct `agents.defaults.*` fields. Runtime changes made with `/model ` are not written back to `config.json`; they affect future turns until the process restarts or another model/config change replaces them. ### Model Fallbacks -`agents.defaults.fallbackModels` defines an ordered failover chain for the active model configuration. The primary model is still selected by `agents.defaults.modelPreset` (or the implicit default config when no preset is active). +`agents.defaults.fallbackModels` defines an ordered failover chain for the active model configuration. The primary model is still selected by `agents.defaults.modelPreset` or, in older configs, by the implicit `default` preset from direct `agents.defaults.*` fields. Each fallback candidate can be either: -- A preset name from `modelPresets`, such as `"deep"`. The preset's full model, provider, generation, and context-window config is used. +- A preset name from `modelPresets`, such as `"deep"`. This is the recommended form. The preset's full model, provider, generation, and context-window config is used. - An inline fallback object with at least `provider` and `model`. Optional `maxTokens`, `contextWindowTokens`, and `temperature` fields inherit from the active primary config when omitted. `reasoningEffort` does not inherit; omit it to leave reasoning off for that fallback, or set it explicitly for models that support reasoning. +Preset fallback chain: + ```json { + "modelPresets": { + "fast": { + "model": "gpt-4.1-mini", + "provider": "openai", + "maxTokens": 4096, + "contextWindowTokens": 128000, + "temperature": 0.2 + }, + "deep": { + "model": "claude-opus-4-5", + "provider": "anthropic", + "maxTokens": 8192, + "contextWindowTokens": 200000, + "reasoningEffort": "high" + }, + "localSmall": { + "model": "llama3.2", + "provider": "ollama", + "maxTokens": 4096, + "contextWindowTokens": 32768 + } + }, + "agents": { + "defaults": { + "modelPreset": "fast", + "fallbackModels": ["deep", "localSmall"] + } + } +} +``` + +String entries are preset names, not raw model names. In the example above, `"deep"` means `modelPresets.deep`; nanobot will not interpret it as a provider model ID. Changing a preset updates both `/model ` switching and any fallback chain that references it. + +Inline fallback object: + +```json +{ + "modelPresets": { + "fast": { + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536 + } + }, "agents": { "defaults": { "modelPreset": "fast", "fallbackModels": [ - "deep", { "provider": "deepseek", "model": "deepseek-v4-pro", @@ -1041,26 +1266,12 @@ Each fallback candidate can be either: } ``` -String entries are preset names, not raw model names. If you want to use a model that is not already a preset, use the inline object form. +Use inline objects only when a fallback is not worth naming as a reusable preset. `fallbackModels` belongs under `agents.defaults`, not inside individual `modelPresets` entries. Failover only runs when the primary provider returns a retryable model/provider error before any answer text has been streamed. Typical fallback cases include timeouts, connection errors, 5xx server errors, 429 rate limits, overloads, and quota/balance exhaustion. It does not run for malformed requests, authentication/permission errors, content filtering/refusals, or context-length/message-format errors. If fallback candidates use smaller `contextWindowTokens` values, nanobot builds context using the smallest window in the active chain so every candidate can receive the same prompt. -Set `agents.defaults.modelPreset` to start with a named preset: - -```json -{ - "agents": { - "defaults": { - "modelPreset": "fast" - } - } -} -``` - -When `modelPreset` is `null` or omitted, startup uses the implicit `default` preset from `agents.defaults.*`. Runtime changes made with `/model ` are not written back to `config.json`; they affect future turns until the process restarts or another model/config change replaces them. - ## Transcription Settings Audio transcription is a shared capability used by chat-channel voice messages and by WebUI/desktop microphone input. Chat-channel voice messages are transcribed automatically before they enter the agent. WebUI and desktop microphone input is transcribed into the composer first, so you can edit the text before sending. @@ -1116,8 +1327,7 @@ Transcription credentials are intentionally not stored in `transcription`. Put t Selecting a transcription provider does not configure credentials by itself. For example, the effective provider may default to Groq for compatibility, but transcription is only usable when `providers.groq.apiKey` or the matching environment-backed config is available. The Settings UI writes only the top-level `transcription` fields. -If you are adding a new transcription provider, see -[`development.md`](./development.md#adding-a-transcription-provider). +If you are adding a new transcription provider, see [`development.md`](./development.md#adding-a-transcription-provider). ## Channel Settings @@ -1130,7 +1340,9 @@ Global settings that apply to all channels. Configure under the `channels` secti "sendToolHints": false, "extractDocumentText": true, "sendMaxRetries": 3, - "telegram": { ... } + "telegram": { + "enabled": false + } } } ``` @@ -1145,8 +1357,7 @@ Global settings that apply to all channels. Configure under the `channels` secti `channels.transcriptionProvider` and `channels.transcriptionLanguage` are deprecated compatibility fields. They remain as a read-only fallback for older configs, but new configuration should use top-level `transcription.provider` and `transcription.language`. -`sendProgress` and `sendToolHints` can also be overridden per channel. The -global values stay as defaults for channels that do not set their own value: +`sendProgress` and `sendToolHints` can also be overridden per channel. The global values stay as defaults for channels that do not set their own value: ```json { @@ -1330,10 +1541,7 @@ You can also set `OLOSTEP_API_KEY` in the environment instead of storing it in c } ``` -You can also set `WEB_SEARCH_API_KEY` for compatibility with the Volcengine web-search skill. -Create the key in the [Volcengine web search console](https://console.volcengine.com/search-infinity/web-search), -then copy it from [API keys](https://console.volcengine.com/search-infinity/api-key). -Volcengine Ark keys are separate and do not work for this search provider. +You can also set `WEB_SEARCH_API_KEY` for compatibility with the Volcengine web-search skill. Create the key in the [Volcengine web search console](https://console.volcengine.com/search-infinity/web-search), then copy it from [API keys](https://console.volcengine.com/search-infinity/api-key). Volcengine Ark keys are separate and do not work for this search provider. **SearXNG** (self-hosted, no API key needed): ```json @@ -1572,12 +1780,36 @@ nanobot agent -m "/pairing approve ABCD-EFGH" ``` +## Gateway Heartbeat + +The gateway can run a protected heartbeat cron job that periodically checks `HEARTBEAT.md` in the active workspace. This is enabled by default when you run `nanobot gateway`. + +```json +{ + "gateway": { + "heartbeat": { + "enabled": true, + "intervalS": 1800, + "keepRecentMessages": 8 + } + } +} +``` + +If `HEARTBEAT.md` has tasks under `## Active Tasks`, the agent executes them and delivers useful results to the most recently active chat target. If the file has no active tasks, the heartbeat is skipped silently. + +The heartbeat job is backed by the same cron service as user-created reminders. It is stored under the active workspace (`/cron/jobs.json`) and shows up in `cron(action="list")` as `heartbeat`, but it is system-managed and cannot be removed with the `cron` tool. Disable it through config and restart the gateway if you do not want periodic heartbeat checks. + +| Option | Default | Description | +|--------|---------|-------------| +| `gateway.heartbeat.enabled` | `true` | Register the built-in heartbeat cron job on gateway startup. | +| `gateway.heartbeat.intervalS` | `1800` | Seconds between heartbeat checks. | +| `gateway.heartbeat.keepRecentMessages` | `8` | Number of recent heartbeat-session messages to retain after each run. | + + ## Subagent Concurrency -By default, nanobot only allows one spawned subagent at a time. When the limit is -reached, the `spawn` tool returns an error so the agent can decide to wait or -rearrange its work. This protects local LLM servers from loading multiple KV caches -at once. If your provider can handle more parallel work, raise the limit: +By default, nanobot only allows one spawned subagent at a time. When the limit is reached, the `spawn` tool returns an error so the agent can decide to wait or rearrange its work. This protects local LLM servers from loading multiple KV caches at once. If your provider can handle more parallel work, raise the limit: ```json { diff --git a/docs/deployment.md b/docs/deployment.md index 8ac652f56..e076a8f1b 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,5 +1,32 @@ # Deployment +Use this page after `nanobot agent -m "Hello!"` works locally. Deployment keeps long-running surfaces online: WebUI, chat apps, heartbeat, Dream, cron jobs, and channel connections. + +## Before You Deploy + +Check these once before Docker, systemd, or LaunchAgent: + +| Check | Why it matters | +|---|---| +| `nanobot status` shows the expected config and workspace | Confirms the process will read the instance you meant to run | +| `nanobot agent -m "Hello!"` works | Proves install, config, provider, model, and workspace writes before adding a service layer | +| Secrets are in environment variables or protected config files | API keys, bot tokens, OAuth state, and chat credentials should not be world-readable | +| `~/.nanobot/` or your custom config/workspace path is persistent | Sessions, memory, channel login state, generated artifacts, and cron jobs live there | +| Channel access control is intentional | Use `allowFrom`, pairing, WebSocket `token`/`tokenIssueSecret`, or private test channels before exposing the bot | +| Ports are planned | Gateway health defaults to `18790`; WebUI/WebSocket defaults to `8765`; `nanobot serve` defaults to `8900` | +| Logs are easy to reach | Use `docker compose logs`, `journalctl`, LaunchAgent log files, or `nanobot gateway --verbose` while diagnosing startup | + +Restart the deployed process after editing `config.json`. Long-running processes read config at startup. + +## Choose a Runtime + +| Runtime | Use it for | State location | Useful first command | +|---|---|---|---| +| Docker Compose | Repeatable container runs on Linux servers or workstations | Bind-mount `~/.nanobot` to `/home/nanobot/.nanobot` | `docker compose run --rm nanobot-cli agent -m "Hello!"` | +| Docker CLI | Manual container testing or small one-off hosts | Bind-mount `~/.nanobot` to `/home/nanobot/.nanobot` | `docker run -v ~/.nanobot:/home/nanobot/.nanobot --rm nanobot status` | +| systemd user service | Linux user-level gateway that restarts automatically | Host user's `~/.nanobot` unless you pass explicit paths | `systemctl --user status nanobot-gateway` | +| macOS LaunchAgent | macOS gateway that starts after login | Host user's `~/.nanobot` unless the plist passes explicit paths | `launchctl list | grep ai.nanobot.gateway` | + ## Docker > [!TIP] diff --git a/docs/development.md b/docs/development.md index f19014314..3915714c1 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,13 +1,10 @@ # Development -This page collects contributor-facing notes for extending nanobot. User-facing setup -and runtime options live in [`configuration.md`](./configuration.md). +This page collects contributor-facing notes for extending nanobot. User-facing setup and runtime options live in [`configuration.md`](./configuration.md). ## Adding an LLM Provider -nanobot uses the provider registry in `nanobot/providers/registry.py` as the -source of truth for LLM provider metadata. Most OpenAI-compatible providers need -only two changes. +nanobot uses the provider registry in `nanobot/providers/registry.py` as the source of truth for LLM provider metadata. Most OpenAI-compatible providers need only two changes. 1. Add a `ProviderSpec` entry to `PROVIDERS`: @@ -29,8 +26,7 @@ class ProvidersConfig(BaseModel): myprovider: ProviderConfig = Field(default_factory=ProviderConfig) ``` -Environment variables, config matching, provider status, and WebUI credential -display derive from those two entries. +Environment variables, config matching, provider status, and WebUI credential display derive from those two entries. Useful `ProviderSpec` options: @@ -50,12 +46,10 @@ Useful `ProviderSpec` options: Transcription is intentionally split into two layers: -- `nanobot/audio/transcription_registry.py` owns provider names, aliases, default - models, and adapter loading. +- `nanobot/audio/transcription_registry.py` owns provider names, aliases, default models, and adapter loading. - `nanobot/providers/transcription.py` owns provider-specific HTTP behavior. -Credentials still live under `providers.` so chat channels, WebUI, and -desktop resolve API keys and API bases the same way. +Credentials still live under `providers.` so chat channels, WebUI, and desktop resolve API keys and API bases the same way. 1. Add provider credentials to `ProvidersConfig`. @@ -67,8 +61,7 @@ class ProvidersConfig(BaseModel): 2. Add a `ProviderSpec` in `nanobot/providers/registry.py`. -For transcription-only providers, set `is_transcription_only=True` so they show up -in credential/settings surfaces but stay out of chat model selection. +For transcription-only providers, set `is_transcription_only=True` so they show up in credential/settings surfaces but stay out of chat model selection. ```python ProviderSpec( @@ -83,9 +76,7 @@ ProviderSpec( 3. Add an adapter class in `nanobot/providers/transcription.py`. -Adapters receive resolved credentials and settings. They return an empty string -for provider errors so channel voice messages fail quietly instead of crashing the -agent loop. +Adapters receive resolved credentials and settings. They return an empty string for provider errors so channel voice messages fail quietly instead of crashing the agent loop. ```python class MySTTTranscriptionProvider: @@ -127,6 +118,4 @@ At minimum, cover: 6. Update user-facing docs. -Add the provider to [`configuration.md`](./configuration.md) where users choose -`transcription.provider`, but keep implementation details in this development -guide. +Add the provider to [`configuration.md`](./configuration.md) where users choose `transcription.provider`, but keep implementation details in this development guide. diff --git a/docs/image-generation.md b/docs/image-generation.md index bf34ba620..c749d4520 100644 --- a/docs/image-generation.md +++ b/docs/image-generation.md @@ -6,6 +6,8 @@ The feature is disabled by default. Enable it in `~/.nanobot/config.json`, confi ## Quick Setup +This snippet uses the current built-in image-generation default so the JSON has concrete names. It is not a provider recommendation; replace `provider` and `model` with any supported image provider and model you intend to use. + ```json { "providers": { @@ -46,7 +48,7 @@ The WebUI hides provider storage details from the user. The agent sees the saved | Option | Type | Default | Description | |--------|------|---------|-------------| | `tools.imageGeneration.enabled` | boolean | `false` | Register the `generate_image` tool | -| `tools.imageGeneration.provider` | string | `"openrouter"` | Image provider name. Supported values: `openrouter`, `custom`, `aihubmix`, `minimax`, `gemini`, `ollama`, `stepfun`, `zhipu` | +| `tools.imageGeneration.provider` | string | `"openrouter"` | Current built-in image provider default. Supported values: `openrouter`, `openai`, `openai_codex`, `custom`, `aihubmix`, `minimax`, `gemini`, `ollama`, `stepfun`, `zhipu` | | `tools.imageGeneration.model` | string | `"openai/gpt-5.4-image-2"` | Provider model name | | `tools.imageGeneration.defaultAspectRatio` | string | `"1:1"` | Default ratio when the prompt/tool call does not specify one | | `tools.imageGeneration.defaultImageSize` | string | `"1K"` | Default size hint, for example `1K`, `2K`, `4K`, or `1024x1024` | @@ -86,7 +88,7 @@ Use a model that supports image generation and image editing if you want referen ### Custom (OpenAI-compatible) -Use the `custom` provider for services that implement the synchronous OpenAI Images API: +The `custom` image provider fits services that implement the synchronous OpenAI Images API: ```text POST /v1/images/generations @@ -364,7 +366,7 @@ Use the reference image. Keep the same robot and composition, change the palette |---------|-------| | `generate_image` is not available | Set `tools.imageGeneration.enabled` to `true` and restart the gateway | | Missing API key error | Configure `providers..apiKey`; if using `${VAR_NAME}`, confirm the environment variable is visible to the gateway process | -| `unsupported image generation provider` | Use `openrouter`, `custom`, `aihubmix`, `minimax`, `gemini`, `ollama`, `stepfun`, or `zhipu` | +| `unsupported image generation provider` | Use `openrouter`, `openai`, `openai_codex`, `custom`, `aihubmix`, `minimax`, `gemini`, `ollama`, `stepfun`, or `zhipu` | | AIHubMix says `Incorrect model ID` | Use `model: "gpt-image-2-free"`; nanobot expands it to the required `openai/gpt-image-2-free` model path internally | | Generation times out | Try a smaller/default image size, set AIHubMix `extraBody.quality` to `"low"`, or retry later | | Reference image rejected | Reference image paths must be inside the workspace or nanobot media directory and must be valid image files | diff --git a/docs/multiple-instances.md b/docs/multiple-instances.md index d7c54cc00..04ab9b8f4 100644 --- a/docs/multiple-instances.md +++ b/docs/multiple-instances.md @@ -52,7 +52,7 @@ nanobot agent -c ~/.nanobot-telegram/config.json -w /tmp/nanobot-telegram-test |-----------|---------------|---------| | **Config** | `--config` path | `~/.nanobot-A/config.json` | | **Workspace** | `--workspace` or config | `~/.nanobot-A/workspace/` | -| **Cron Jobs** | config directory | `~/.nanobot-A/cron/` | +| **Cron Jobs** | workspace directory | `~/.nanobot-A/workspace/cron/` | | **Media / runtime state** | config directory | `~/.nanobot-A/media/` | ## How It Works @@ -67,14 +67,13 @@ nanobot agent -c ~/.nanobot-telegram/config.json -w /tmp/nanobot-telegram-test 2. Set a different `agents.defaults.workspace` for that instance. 3. Start the instance with `--config`. -Example config: +Example config fragment: ```json { "agents": { "defaults": { - "workspace": "~/.nanobot-telegram/workspace", - "model": "anthropic/claude-sonnet-4-6" + "workspace": "~/.nanobot-telegram/workspace" } }, "channels": { @@ -90,6 +89,8 @@ Example config: } ``` +The copied base config can keep using the same `modelPresets` and `agents.defaults.modelPreset`. If this instance needs a different model, add another preset and set `agents.defaults.modelPreset` to that preset name. + Start separate instances: ```bash @@ -97,10 +98,7 @@ nanobot gateway --config ~/.nanobot-telegram/config.json nanobot gateway --config ~/.nanobot-discord/config.json ``` -Each gateway instance also exposes a lightweight HTTP health endpoint on -`gateway.host:gateway.port`. By default, the gateway binds to `127.0.0.1`, -so the endpoint stays local unless you explicitly set `gateway.host` to a -public or LAN-facing address. +Each gateway instance also exposes a lightweight HTTP health endpoint on `gateway.host:gateway.port`. By default, the gateway binds to `127.0.0.1`, so the endpoint stays local unless you explicitly set `gateway.host` to a public or LAN-facing address. - `GET /health` returns `{"status":"ok"}` - Other paths return `404` @@ -123,4 +121,4 @@ nanobot gateway --config ~/.nanobot-telegram/config.json --workspace /tmp/nanobo - Each instance must use a different port if they run at the same time - Use a different workspace per instance if you want isolated memory, sessions, and skills - `--workspace` overrides the workspace defined in the config file -- Cron jobs and runtime media/state are derived from the config directory +- Cron jobs are stored in the active workspace; runtime media/state is derived from the config directory diff --git a/docs/my-tool.md b/docs/my-tool.md index bc22ed5a9..8a72645e9 100644 --- a/docs/my-tool.md +++ b/docs/my-tool.md @@ -25,8 +25,7 @@ tools: To allow the agent to set its configuration (e.g. switch models, adjust parameters), set `tools.my.allow_set: true`. -Legacy `tools.myEnabled` / `tools.mySet` keys are auto-migrated on load, and -rewritten in-place the next time `nanobot onboard` refreshes the config. +Legacy `tools.myEnabled` / `tools.mySet` keys are auto-migrated on load, and rewritten in-place the next time `nanobot onboard` refreshes the config. All modifications are held in memory only — restart restores defaults. diff --git a/docs/openai-api.md b/docs/openai-api.md index c88a8beda..0307258a8 100644 --- a/docs/openai-api.md +++ b/docs/openai-api.md @@ -3,11 +3,14 @@ nanobot can expose a minimal OpenAI-compatible endpoint for local integrations: ```bash -pip install "nanobot-ai[api]" +python -m pip install "nanobot-ai[api]" +nanobot agent -m "Hello!" nanobot serve ``` -By default, the API binds to `127.0.0.1:8900`. You can change this in `config.json`. +Run the CLI check first. If `nanobot agent -m "Hello!"` fails, fix provider or config setup before debugging the API server. By default, the API binds to `127.0.0.1:8900`. You can change this in `config.json`. + +For setup help, see [`quick-start.md`](./quick-start.md), [`providers.md`](./providers.md), and [`troubleshooting.md`](./troubleshooting.md). ## Behavior diff --git a/docs/provider-cookbook.md b/docs/provider-cookbook.md new file mode 100644 index 000000000..92315c6eb --- /dev/null +++ b/docs/provider-cookbook.md @@ -0,0 +1,443 @@ +# Provider Cookbook + +This page is for cases where you already know what you want to connect and need a pasteable setup. Each recipe shows what to set, what to run, and what a failure usually means. + +If this is your first install and terminal commands are new to you, start with [`start-without-technical-background.md`](./start-without-technical-background.md). If you want the field-by-field explanation, read [`providers.md`](./providers.md) and then [`configuration.md#providers`](./configuration.md#providers). + +Most examples below are snippets to merge into `~/.nanobot/config.json`. Keep any existing sections you still need, and replace placeholder keys such as `${OPENROUTER_API_KEY}` with environment-variable references or real values only on your own machine. + +Recipes are examples, not rankings. Pick the recipe that matches the credential, endpoint, and model ID you already intend to use. + +## Choose a Recipe + +Match the recipe to the credential or endpoint you already have: + +| What you have | Recipe | Must match | +|---|---|---| +| A gateway key and model IDs that include a model family path, such as `provider/model-name` | [OpenRouter Gateway](#recipe-openrouter-gateway) | API key, provider config key, preset provider, and gateway model ID | +| An OpenAI platform API key and OpenAI model ID | [OpenAI Direct](#recipe-openai-direct) | `OPENAI_API_KEY`, `provider: "openai"`, and an OpenAI model available to that account | +| An Anthropic API key and Anthropic model ID | [Anthropic Direct](#recipe-anthropic-direct) | `ANTHROPIC_API_KEY`, `provider: "anthropic"`, and a non-gateway model ID | +| An OpenAI-compatible `/v1` endpoint that is not a named nanobot provider | [Custom OpenAI-Compatible Provider](#recipe-custom-openai-compatible-provider) | `apiBase`, optional API key, and the model ID served by that endpoint | +| Ollama already running locally | [Ollama Local Model](#recipe-ollama-local-model) | Ollama `apiBase`, pulled model name, and local server availability | +| vLLM, LM Studio, or another local OpenAI-compatible server | [vLLM or LM Studio](#recipe-vllm-or-lm-studio) | Local `/v1` base URL, any required key, and served model name | +| A primary model plus one or more backups | [Fallback Presets](#recipe-fallback-presets) | Named presets in `modelPresets`, referenced from `agents.defaults.fallbackModels` | +| A working agent and a Langfuse project | [Langfuse Tracing](#recipe-langfuse-tracing) | Langfuse env vars in the same process environment that starts nanobot | + +## How to Use a Recipe + +1. Install nanobot and run `nanobot onboard` or `nanobot onboard --wizard` once so `~/.nanobot/config.json` exists. +2. Put secrets in environment variables when possible. +3. Merge the recipe snippet into `~/.nanobot/config.json`. +4. Run `nanobot status`. +5. Run `nanobot agent -m "Hello!"`. +6. If the CLI works, then connect WebUI, gateway, or chat apps. + +The active model should normally come from `agents.defaults.modelPreset`, and that name should point to an entry in `modelPresets`. Direct `agents.defaults.provider` and `agents.defaults.model` still work for older configs, but presets are easier to switch and easier to reuse as fallbacks. + +## Secret Setup + +Environment variables keep API keys out of the config file. + +Use the variable name shown by the recipe you picked. The commands below use `OPENROUTER_API_KEY` only as an example; an OpenAI direct recipe uses `OPENAI_API_KEY`, an Anthropic direct recipe uses `ANTHROPIC_API_KEY`, and a custom endpoint can use any variable name you reference in `config.json`. + +**macOS / Linux** + +```bash +export OPENROUTER_API_KEY="sk-or-v1-..." +nanobot agent -m "Hello!" +``` + +**Windows PowerShell** + +```powershell +$env:OPENROUTER_API_KEY = "sk-or-v1-..." +nanobot agent -m "Hello!" +``` + +Environment variables set this way apply only to the current terminal. For long-running services such as systemd, Docker, LaunchAgent, or a remote shell, set the variables in that service environment before starting nanobot. + +## Recipe: OpenRouter Gateway + +This recipe applies when one API key routes many hosted model families. + +```json +{ + "providers": { + "openrouter": { + "apiKey": "${OPENROUTER_API_KEY}" + } + }, + "modelPresets": { + "primary": { + "label": "Primary", + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536, + "temperature": 0.1 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Verify: + +```bash +nanobot status +nanobot agent -m "Hello!" +``` + +If this fails with `401` or `unauthorized`, check that `OPENROUTER_API_KEY` is visible in the same terminal or service that starts nanobot. If it fails with `model not found`, choose a model ID that OpenRouter lists for your account. + +## Recipe: OpenAI Direct + +This recipe applies when you have an OpenAI API key and want to call OpenAI directly instead of through a gateway. + +```json +{ + "providers": { + "openai": { + "apiKey": "${OPENAI_API_KEY}" + } + }, + "modelPresets": { + "primary": { + "label": "OpenAI", + "provider": "openai", + "model": "gpt-5", + "maxTokens": 4096, + "contextWindowTokens": 128000, + "temperature": 0.1 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Verify: + +```bash +OPENAI_API_KEY="sk-..." nanobot agent -m "Hello!" +``` + +If your shell cannot use inline environment variables, set `OPENAI_API_KEY` first and then run `nanobot agent -m "Hello!"`. If the provider rejects `apiType`, remove `apiType` unless you are using a documented OpenAI-specific mode. + +## Recipe: Anthropic Direct + +This recipe applies when your key comes from Anthropic and your model name is an Anthropic model ID, not an OpenRouter model path. + +```json +{ + "providers": { + "anthropic": { + "apiKey": "${ANTHROPIC_API_KEY}" + } + }, + "modelPresets": { + "primary": { + "label": "Anthropic", + "provider": "anthropic", + "model": "claude-sonnet-4-5", + "maxTokens": 4096, + "contextWindowTokens": 200000, + "temperature": 0.1 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Verify: + +```bash +ANTHROPIC_API_KEY="sk-ant-..." nanobot agent -m "Hello!" +``` + +If you copied a model name such as `anthropic/claude-sonnet-4.5`, that is a gateway-style model path and belongs under `provider: "openrouter"`, not `provider: "anthropic"`. + +## Recipe: Custom OpenAI-Compatible Provider + +This recipe applies to an OpenAI-compatible service that is not a named nanobot provider. + +```json +{ + "providers": { + "custom": { + "apiKey": "${CUSTOM_API_KEY}", + "apiBase": "https://api.example.com/v1" + } + }, + "modelPresets": { + "primary": { + "label": "Custom", + "provider": "custom", + "model": "provider-model-name", + "maxTokens": 4096, + "contextWindowTokens": 65536, + "temperature": 0.1 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Verify the endpoint before blaming nanobot: + +```bash +curl -sS https://api.example.com/v1/models +nanobot agent -m "Hello!" +``` + +`apiBase` is the HTTP base URL, not the model name. Include the version path when the service expects it, such as `/v1`. If the service requires a non-empty key but does not validate it, use a placeholder such as `"apiKey": "EMPTY"`. + +## Recipe: Ollama Local Model + +This recipe applies when Ollama is already installed and the model has been pulled locally. + +```bash +ollama serve +ollama pull llama3.2 +``` + +```json +{ + "providers": { + "ollama": { + "apiBase": "http://localhost:11434/v1" + } + }, + "modelPresets": { + "local": { + "label": "Local", + "provider": "ollama", + "model": "llama3.2", + "maxTokens": 2048, + "contextWindowTokens": 32768, + "temperature": 0.2 + } + }, + "agents": { + "defaults": { + "modelPreset": "local" + } + } +} +``` + +Verify: + +```bash +curl -sS http://localhost:11434/v1/models +nanobot agent -m "Hello!" +``` + +If you see `connection refused`, Ollama is not running or `apiBase` points to the wrong port. If the response is very slow, try a smaller local model or lower `contextWindowTokens`. + +## Recipe: vLLM or LM Studio + +This recipe applies when a local server exposes an OpenAI-compatible `/v1` API. + +```json +{ + "providers": { + "vllm": { + "apiBase": "http://127.0.0.1:8000/v1", + "apiKey": "EMPTY" + } + }, + "modelPresets": { + "local": { + "label": "Local", + "provider": "vllm", + "model": "served-model-name", + "maxTokens": 4096, + "contextWindowTokens": 65536, + "temperature": 0.2 + } + }, + "agents": { + "defaults": { + "modelPreset": "local" + } + } +} +``` + +For LM Studio, use its local base URL and provider name: + +```json +{ + "providers": { + "lmStudio": { + "apiBase": "http://localhost:1234/v1" + } + }, + "modelPresets": { + "local": { + "label": "LM Studio", + "provider": "lm_studio", + "model": "local-model", + "maxTokens": 2048, + "contextWindowTokens": 32768 + } + }, + "agents": { + "defaults": { + "modelPreset": "local" + } + } +} +``` + +The config key can be `lmStudio` or `lm_studio`, but the preset provider should use the registry name `lm_studio`. + +## Recipe: Fallback Presets + +This recipe applies when one provider sometimes rate-limits, one model is expensive, or you want a local backup. + +```json +{ + "modelPresets": { + "fast": { + "label": "Fast", + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536, + "temperature": 0.1 + }, + "deep": { + "label": "Deep", + "provider": "anthropic", + "model": "claude-sonnet-4-5", + "maxTokens": 4096, + "contextWindowTokens": 200000, + "temperature": 0.1 + }, + "local": { + "label": "Local", + "provider": "ollama", + "model": "llama3.2", + "maxTokens": 2048, + "contextWindowTokens": 32768, + "temperature": 0.2 + } + }, + "agents": { + "defaults": { + "modelPreset": "fast", + "fallbackModels": ["deep", "local"] + } + } +} +``` + +`fallbackModels` belongs under `agents.defaults`. String entries are preset names, not raw model names. nanobot tries the active preset first, then the fallback presets in order. + +Keep fallback candidates realistic. If the local fallback has a smaller context window, nanobot must build context that fits the smallest window in the active chain. + +## Recipe: Langfuse Tracing + +This recipe applies after the agent works and you want observability for OpenAI-compatible provider calls. + +Install the optional package in the same Python environment that runs nanobot: + +```bash +python -m pip install langfuse +``` + +Set the environment variables before starting nanobot: + +```bash +export LANGFUSE_SECRET_KEY="sk-lf-..." +export LANGFUSE_PUBLIC_KEY="pk-lf-..." +export LANGFUSE_BASE_URL="https://cloud.langfuse.com" +nanobot agent -m "Hello!" +``` + +PowerShell: + +```powershell +$env:LANGFUSE_SECRET_KEY = "sk-lf-..." +$env:LANGFUSE_PUBLIC_KEY = "pk-lf-..." +$env:LANGFUSE_BASE_URL = "https://cloud.langfuse.com" +nanobot agent -m "Hello!" +``` + +Langfuse is not a model provider in `config.json`. It is configured through environment variables and traces supported OpenAI-compatible provider calls. Native providers that do not use that client path may not produce Langfuse OpenAI-wrapper traces. + +## Recipe: Switch Models at Runtime + +Use this after you have more than one preset and are chatting through a supported channel. + +```json +{ + "modelPresets": { + "fast": { + "label": "Fast", + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536 + }, + "local": { + "label": "Local", + "provider": "ollama", + "model": "llama3.2", + "maxTokens": 2048, + "contextWindowTokens": 32768 + } + }, + "agents": { + "defaults": { + "modelPreset": "fast" + } + } +} +``` + +In chat: + +```text +/model +/model local +/model fast +``` + +`/model` switching is runtime-only. It does not rewrite `config.json`, and an in-progress turn keeps using the model it started with. + +## Quick Failure Map + +| Symptom | Usually means | First check | +|---|---|---| +| `401`, `unauthorized`, or `invalid API key` | The key is missing, wrong, expired, or under the wrong provider | Print or re-set the environment variable in the same terminal or service | +| `model not found` | The model ID does not belong to the selected provider or gateway | Compare `modelPresets..provider` and `modelPresets..model` | +| `connection refused` | Local server is not running or `apiBase` has the wrong port/path | Run `curl /models` | +| `provider not found` | Provider name is misspelled or uses the config key instead of registry name | Use names such as `openrouter`, `openai`, `anthropic`, `ollama`, `vllm`, `lm_studio` | +| Langfuse shows no traces | Env vars are missing, `langfuse` is not installed in the active Python environment, or the provider path is native | Run `python -m pip show langfuse` and restart nanobot from the same environment | + +## Next References + +| Need | Read | +|---|---| +| Field meanings and provider resolution | [`providers.md`](./providers.md) | +| Full schema and provider table | [`configuration.md#providers`](./configuration.md#providers) | +| Langfuse details | [`configuration.md#langfuse-observability`](./configuration.md#langfuse-observability) | +| First-run diagnosis | [`troubleshooting.md`](./troubleshooting.md) | diff --git a/docs/providers.md b/docs/providers.md new file mode 100644 index 000000000..5e6a381c1 --- /dev/null +++ b/docs/providers.md @@ -0,0 +1,446 @@ +# Providers and Models + +Use this page when the first reply fails because of provider/model mismatch, or when you want to adapt the concrete setup example to a different provider. If you already know which provider you want and only need a pasteable setup, use [`provider-cookbook.md`](./provider-cookbook.md). + +For every setup, answer three questions: + +1. Which provider owns the credential or endpoint? +2. What model name does that provider expect? +3. Does the provider need `apiKey`, `apiBase`, OAuth login, cloud credentials, or only a local server URL? + +Prefer a named `modelPresets` entry for the model/provider pair, then select it with `agents.defaults.modelPreset`. Direct `agents.defaults.provider` and `agents.defaults.model` still work for existing configs, but presets make runtime `/model` switching and fallback chains clearer. Pin `provider` inside the preset while setting up; you can switch back to `"auto"` later. + +## Choose a Provider Without Guessing + +The docs show concrete provider names so the JSON is copyable, not because nanobot ranks providers. Start from the service or endpoint you actually control: + +| If you have... | Configure... | +|---|---| +| An API key from a hosted provider or gateway | That provider's `providers..apiKey`, then a preset with that provider name and a model ID from that service. | +| A company proxy or regional endpoint | The matching provider block plus `apiBase` if the proxy gives you a URL. | +| A local OpenAI-compatible server | A local provider block such as `ollama`, `vllm`, `lmStudio`, or `custom`, usually with `apiBase`. | +| An OAuth-based account | Run the matching `nanobot provider login ...` command, then select that provider explicitly in a preset. | +| No provider yet | Pick one outside nanobot based on account access, pricing, regional availability, privacy requirements, and the model IDs you need. Then come back with its key and model ID. | + +## Minimal Shape + +```json +{ + "providers": { + "openrouter": { + "apiKey": "sk-or-v1-xxx" + } + }, + "modelPresets": { + "primary": { + "provider": "openrouter", + "model": "anthropic/claude-opus-4.5", + "maxTokens": 8192, + "contextWindowTokens": 65536, + "temperature": 0.1 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +The provider config gives nanobot credentials and endpoint details. The model preset names the provider/model pair. The agent defaults choose which named preset to use for normal turns. Replace the example provider and model together; mixing an API key from one provider with a model ID from another is the most common first-run failure. + +## Provider, Model, API Key, and Base URL + +These fields answer different questions: + +| Field | Where it lives | Meaning | +|---|---|---| +| `provider` | `modelPresets..provider` | Which nanobot provider adapter should send the request. | +| `model` | `modelPresets..model` | The model ID expected by that provider or gateway. | +| `apiKey` | `providers..apiKey` | Credential for that provider. Use `${ENV_VAR}` for secrets. | +| `apiBase` | `providers..apiBase` | HTTP base URL of the provider endpoint. | + +You usually omit `apiBase` for hosted built-in providers such as OpenRouter, Anthropic direct, OpenAI direct, Groq, or Bedrock because nanobot knows their default endpoints. Set `apiBase` for `custom`, local OpenAI-compatible servers, provider proxies, regional endpoints, or subscription endpoints. Include the API version path when the endpoint requires it, for example `https://api.example.com/v1` or `http://localhost:11434/v1`. + +## Common Provider Patterns + +### OpenRouter Gateway + +Gateway-style setup for model IDs served through OpenRouter. + +```json +{ + "providers": { + "openrouter": { + "apiKey": "${OPENROUTER_API_KEY}" + } + }, + "modelPresets": { + "primary": { + "provider": "openrouter", + "model": "anthropic/claude-opus-4.5", + "maxTokens": 8192, + "contextWindowTokens": 65536 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Use the model ID exactly as OpenRouter lists it. + +### Anthropic Direct + +```json +{ + "providers": { + "anthropic": { + "apiKey": "${ANTHROPIC_API_KEY}" + } + }, + "modelPresets": { + "primary": { + "provider": "anthropic", + "model": "claude-opus-4-5", + "maxTokens": 8192, + "contextWindowTokens": 200000 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Anthropic direct uses the native Anthropic provider. Do not use an OpenRouter model ID unless the provider is OpenRouter. + +### OpenAI Direct + +```json +{ + "providers": { + "openai": { + "apiKey": "${OPENAI_API_KEY}" + } + }, + "modelPresets": { + "primary": { + "provider": "openai", + "model": "gpt-5", + "maxTokens": 8192, + "contextWindowTokens": 128000 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +`providers.openai.apiType` may be set when you need to force a specific OpenAI API surface. Other providers reject `apiType`; leave it unset outside `providers.openai`. Replace the model with a model ID available to your OpenAI account. + +### Custom OpenAI-Compatible Endpoint + +The `custom` provider fits OpenAI-compatible endpoints that are not represented by a named provider. + +```json +{ + "providers": { + "custom": { + "apiKey": "${CUSTOM_API_KEY}", + "apiBase": "https://example.com/v1" + } + }, + "modelPresets": { + "primary": { + "provider": "custom", + "model": "provider-model-name", + "maxTokens": 8192, + "contextWindowTokens": 65536 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +`custom` does not infer a default base URL. Set `apiBase`. + +### Ollama + +Start Ollama separately, then point nanobot at the OpenAI-compatible endpoint. + +```json +{ + "providers": { + "ollama": { + "apiBase": "http://localhost:11434/v1" + } + }, + "modelPresets": { + "primary": { + "provider": "ollama", + "model": "llama3.2", + "maxTokens": 4096, + "contextWindowTokens": 32768 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Most Ollama setups do not require an API key. + +### vLLM or Other Local OpenAI-Compatible Server + +```json +{ + "providers": { + "vllm": { + "apiBase": "http://127.0.0.1:8000/v1", + "apiKey": "EMPTY" + } + }, + "modelPresets": { + "primary": { + "provider": "vllm", + "model": "served-model-name", + "maxTokens": 8192, + "contextWindowTokens": 65536 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Some OpenAI-compatible local servers require any non-empty API key even when they do not validate it. + +### LM Studio + +```json +{ + "providers": { + "lmStudio": { + "apiBase": "http://localhost:1234/v1" + } + }, + "modelPresets": { + "primary": { + "provider": "lm_studio", + "model": "local-model", + "maxTokens": 4096, + "contextWindowTokens": 32768 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Config keys may be camelCase or snake_case. Provider names in model presets should use the registry name, such as `lm_studio`. + +### AWS Bedrock + +Bedrock can use the AWS credential chain, profile, region, or Bedrock bearer token depending on your AWS setup. + +```json +{ + "providers": { + "bedrock": { + "region": "us-east-1", + "profile": "default" + } + }, + "modelPresets": { + "primary": { + "provider": "bedrock", + "model": "bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0", + "maxTokens": 8192, + "contextWindowTokens": 200000 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +See [`configuration.md#providers`](./configuration.md#providers) for Bedrock-specific notes. + +### OAuth Providers + +Some providers do not use API keys in `config.json`. + +```bash +nanobot provider login openai-codex +nanobot provider login github-copilot +``` + +Then explicitly select the provider and model in a preset. OAuth providers are not valid automatic fallbacks. + +## Provider Resolution + +The recommended path is a named preset selected by `agents.defaults.modelPreset`. The effective model parameters come from: + +1. the named `modelPresets` entry referenced by `agents.defaults.modelPreset`; +2. otherwise the implicit `default` preset built from `agents.defaults.model`, `provider`, `maxTokens`, `contextWindowTokens`, `temperature`, and related fields. + +Provider selection follows this practical rule: + +- Explicit `provider` in the active preset or implicit default config wins. +- `provider: "auto"` tries model-name keywords, configured keys, local base URLs, and gateway providers. +- Gateway providers such as OpenRouter and AiHubMix can route many model families, so the model name must be valid for that gateway. +- Local providers should normally be explicit because generic local model names such as `llama3.2` do not always contain provider keywords. + +## Model Presets + +Model presets are the recommended model configuration surface. Use them when you want named model choices, runtime `/model` switching, or reusable fallback targets. + +```json +{ + "modelPresets": { + "fast": { + "label": "Fast", + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536, + "temperature": 0.1 + }, + "deep": { + "label": "Deep", + "provider": "anthropic", + "model": "claude-opus-4-5", + "maxTokens": 8192, + "contextWindowTokens": 200000, + "temperature": 0.1 + } + }, + "agents": { + "defaults": { + "modelPreset": "fast" + } + } +} +``` + +The preset name `default` is reserved for the implicit `agents.defaults` settings. Do not define `modelPresets.default`; use `/model default` to return to the direct `agents.defaults.*` fields in older configs. + +## Fallback Models + +Fallbacks are useful for transient provider failures, rate limits, or model availability issues. Keep fallbacks compatible with the task size and tool use. Prefer fallback presets so each candidate has a name and a complete provider, model, generation, and context-window configuration. + +```json +{ + "modelPresets": { + "fast": { + "label": "Fast", + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536, + "temperature": 0.1 + }, + "deep": { + "label": "Deep", + "provider": "anthropic", + "model": "claude-opus-4-5", + "maxTokens": 8192, + "contextWindowTokens": 200000, + "temperature": 0.1 + }, + "localSmall": { + "label": "Local Small", + "provider": "ollama", + "model": "llama3.2", + "maxTokens": 4096, + "contextWindowTokens": 32768, + "temperature": 0.2 + } + }, + "agents": { + "defaults": { + "modelPreset": "fast", + "fallbackModels": ["deep", "localSmall"] + } + } +} +``` + +String entries in `fallbackModels` are preset names, not raw model names. nanobot tries them in order after the active preset. Each fallback preset uses its own `provider`, `model`, `maxTokens`, `contextWindowTokens`, `temperature`, and optional `reasoningEffort`. + +Use inline fallback objects only when a model is not worth naming as a preset: + +```json +{ + "modelPresets": { + "fast": { + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536 + } + }, + "agents": { + "defaults": { + "modelPreset": "fast", + "fallbackModels": [ + { + "provider": "deepseek", + "model": "deepseek-v4-pro", + "maxTokens": 4096, + "contextWindowTokens": 262144 + } + ] + } + } +} +``` + +`fallbackModels` belongs under `agents.defaults`, not inside each preset. If fallback candidates use smaller context windows, nanobot builds context using the smallest window in the active chain so every candidate can receive the same prompt. See [`configuration.md#model-fallbacks`](./configuration.md#model-fallbacks) for failure conditions. + +## Quick Checks + +Run these before debugging a chat app: + +```bash +nanobot status +nanobot agent -m "Hello!" +``` + +If `nanobot agent -m "Hello!"` fails: + +| Symptom | Likely cause | +|---|---| +| 401, unauthorized, invalid API key | Key is missing, expired, copied with whitespace, or stored under the wrong provider | +| model not found | Model ID does not exist for the selected provider or gateway | +| connection refused | Local provider server is not running or `apiBase` points to the wrong port | +| provider not found | The active preset uses a misspelled provider; use registry names such as `openrouter`, `anthropic`, `ollama`, `vllm`, `lm_studio` | +| works in CLI but not chat app | Provider is fine; debug gateway/channel setup in [`chat-apps.md`](./chat-apps.md) or [`troubleshooting.md`](./troubleshooting.md) | + +For the complete provider table and advanced provider-specific notes, see [`configuration.md#providers`](./configuration.md#providers). diff --git a/docs/python-sdk.md b/docs/python-sdk.md index 14609c143..7c475c2f6 100644 --- a/docs/python-sdk.md +++ b/docs/python-sdk.md @@ -2,6 +2,14 @@ Use nanobot as a library — no CLI, no gateway, just Python. +Before debugging SDK code, prove the same config works from the CLI: + +```bash +nanobot agent -m "Hello!" +``` + +`Nanobot.from_config()` reuses your normal `~/.nanobot/config.json`, so provider, model, tools, and workspace behavior match the CLI unless you override them. + ## Quick Start ```python @@ -19,8 +27,6 @@ async def main() -> None: asyncio.run(main()) ``` -`Nanobot.from_config()` reuses your normal `~/.nanobot/config.json`, so the SDK follows the same provider, model, tools, and workspace defaults as the CLI unless you override them. - Use `async with` when possible so MCP connections and background cleanup work are closed before the event loop exits. If you manage the instance manually, call `await bot.aclose()` in a `finally` block. ## Common Patterns diff --git a/docs/quick-start.md b/docs/quick-start.md index 7112ba8ca..2e1aa15db 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -1,78 +1,128 @@ # Install and Quick Start -## Install +This page gets one local nanobot reply working. After that, you can add the WebUI, chat apps, local models, web search, MCP, deployment, or custom plugins. + +If you have never used a terminal or edited a config file before, use [`start-without-technical-background.md`](./start-without-technical-background.md) first. This page assumes you are comfortable pasting commands and editing JSON snippets. + +## Before You Start + +You need: + +- Python 3.11 or newer. +- One LLM provider, company endpoint, subscription endpoint, or local model server you can call. The examples below use OpenRouter only so the snippets are concrete; any supported provider works when the key, provider name, and model ID match. +- Git only if you install from source. +- Node.js or Bun only if you are developing the WebUI itself. > [!IMPORTANT] -> This README may describe features that are available first in the latest source code. -> If you want the newest features and experiments, install from source. -> If you want the most stable day-to-day experience, install from PyPI or with `uv`. +> Repository docs may describe features that are available first in source. Install from PyPI or `uv` for the stable day-to-day release; install from source when you want the newest repository behavior or plan to contribute. -**Install from source** (latest features, experimental changes may land here first; recommended for development) +## 1. Install + +Pick one install method. + +**One-command setup:** + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" +``` + +On Windows PowerShell: + +```powershell +irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1 | iex +``` + +The default command installs or upgrades `nanobot-ai` from PyPI, then starts `nanobot onboard --wizard`. If you finish the wizard and save the config, skip the manual initialize/configure steps and go straight to [Check the Setup](#4-check-the-setup). + +To preview the plan without changing your environment, pass `--dry-run`; combine it with `--dev` when you want to preview the main-branch install. + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" -- --dry-run +``` + +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1))) --dry-run +``` + +To install the current `main` branch instead, pass `--dev`: + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" -- --dev +``` + +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1))) --dev +``` + +If `curl` or `irm` is unavailable, or GitHub raw downloads are blocked on your network, use one of the manual install methods below. + +If you prefer to inspect the script first, open [`../scripts/install.sh`](../scripts/install.sh) or [`../scripts/install.ps1`](../scripts/install.ps1). + +**Stable release with `uv`:** + +```bash +uv tool install nanobot-ai +nanobot --version +``` + +**Stable release with pip:** + +```bash +python -m pip install nanobot-ai +nanobot --version +``` + +**Latest source checkout:** ```bash git clone https://github.com/HKUDS/nanobot.git cd nanobot -pip install -e . -``` - -**Install with [uv](https://github.com/astral-sh/uv)** (stable release, fast) - -```bash -uv tool install nanobot-ai -``` - -**Install from PyPI** (stable release) - -```bash -pip install nanobot-ai -``` - -### Update to latest version - -**PyPI / pip** - -```bash -pip install -U nanobot-ai +python -m pip install -e . nanobot --version ``` -**uv** +If your shell cannot find `nanobot` after a pip install, run the module form: ```bash -uv tool upgrade nanobot-ai -nanobot --version +python -m nanobot --version +python -m nanobot onboard ``` -**Using WhatsApp?** Rebuild the local bridge after upgrading: +On Windows, `~` in the docs means your user profile directory, for example `C:\Users\you`. -```bash -rm -rf ~/.nanobot/bridge -nanobot channels login whatsapp -``` +The docs use `python` in commands. If your system exposes Python 3.11+ as `python3` or `py`, use that command in the same place, for example `python3 -m pip install nanobot-ai` or `py -m nanobot --version`. -## Quick Start +## 2. Initialize -> [!TIP] -> Set your API key in `~/.nanobot/config.json`. -> Get API keys: [OpenRouter](https://openrouter.ai/keys) (Global) -> -> For other LLM providers, please see [`configuration.md`](./configuration.md). -> -> For web search capability setup, please see the web-search section in [`configuration.md`](./configuration.md#web-search). - -**1. Initialize** +Skip this section if the one-command setup already started the wizard and you saved the config there. ```bash nanobot onboard ``` -Use `nanobot onboard --wizard` if you want the interactive setup wizard. +Use the wizard if you prefer prompts instead of editing JSON by hand: -**2. Configure** (`~/.nanobot/config.json`) +```bash +nanobot onboard --wizard +``` -Configure these **two parts** in your config (other options have defaults). +Initialization creates: + +| Path | What it is | +|------|------------| +| `~/.nanobot/config.json` | Main settings file for providers, models, channels, tools, gateway, and API | +| `~/.nanobot/workspace/` | Agent workspace for memory, sessions, heartbeat tasks, skills, and artifacts | + +If you already have a config, `nanobot onboard` can refresh missing default fields without overwriting your existing values. + +## 3. Configure a Provider + +Skip this section if you already configured provider and model settings in the wizard. + +Open `~/.nanobot/config.json`. Add or merge these blocks into the file created by `nanobot onboard`; do not replace the whole file unless you want to reset the config. + +**API key:** -*Set your API key* (e.g. OpenRouter, recommended for global users): ```json { "providers": { @@ -83,22 +133,191 @@ Configure these **two parts** in your config (other options have defaults). } ``` -*Set your model* (optionally pin a provider — defaults to auto-detection): +**Model preset:** + ```json { + "modelPresets": { + "primary": { + "label": "Primary", + "provider": "openrouter", + "model": "anthropic/claude-opus-4.5", + "maxTokens": 8192, + "contextWindowTokens": 65536, + "temperature": 0.1 + } + }, "agents": { "defaults": { - "model": "anthropic/claude-opus-4-5", - "provider": "openrouter" + "modelPreset": "primary" } } } ``` -**3. Chat** +The provider and model inside a preset must match. The snippet above is only an example. For another provider, replace these values together: + +| Replace | Where | +|---|---| +| Provider config key, such as `openrouter` | `providers.` | +| API key or environment variable | `providers..apiKey` | +| Preset provider name | `modelPresets.primary.provider` | +| Model ID | `modelPresets.primary.model` | +| Endpoint URL, only when needed | `providers..apiBase` | + +Direct `agents.defaults.provider` and `agents.defaults.model` still work for existing configs, but named presets are the recommended path because they also power `/model` switching and fallback chains. For provider-specific examples across direct, gateway, OAuth, cloud, and local setups, see [`providers.md`](./providers.md). + +**What about `apiBase` / base URL?** + +`apiBase` is the HTTP base URL of the provider endpoint, not the model name. Most hosted providers in nanobot already know their default endpoint, so you usually only set `apiKey` and a model preset. Set `apiBase` when you are using: + +- `custom` for a third-party or self-hosted OpenAI-compatible API; +- a local OpenAI-compatible server such as Ollama, vLLM, or LM Studio; +- a provider-specific alternate endpoint, regional endpoint, proxy, or subscription endpoint. + +Examples: + +```json +{ + "providers": { + "custom": { + "apiKey": "${CUSTOM_API_KEY}", + "apiBase": "https://api.example.com/v1" + } + } +} +``` + +```json +{ + "providers": { + "ollama": { + "apiBase": "http://localhost:11434/v1" + } + } +} +``` + +If the provider's docs say the endpoint is `/v1`, include `/v1` in `apiBase`. The model ID still belongs in the active `modelPresets` entry. + +If you prefer not to store secrets in `config.json`, reference an environment variable and set it before starting nanobot: + +```json +{ + "providers": { + "openrouter": { + "apiKey": "${OPENROUTER_API_KEY}" + } + } +} +``` + +## 4. Check the Setup + +```bash +nanobot status +``` + +This should show the config path, workspace path, active model or preset, and provider summary. It does not send a message to the model, so use it as a quick config check before the first real request. + +Read it like this: + +| Status line | What you want | +|---|---| +| `Config` | A check mark. | +| `Workspace` | A check mark. | +| `Model` | The model or preset you expect. | +| Provider list | Most providers can say `not set`; the provider used by the active preset should show a check mark, OAuth status, or local URL. | + +## 5. Test One Message + +Run a one-shot CLI message: + +```bash +nanobot agent -m "Hello!" +``` + +A successful first run proves that: + +- the `nanobot` command is installed; +- `~/.nanobot/config.json` can be loaded; +- the selected provider and model can answer; +- the default workspace can be created and used. + +The reply text itself will vary. Any normal assistant answer means the install, config, provider, model, and workspace path are all usable. + +If that works, start an interactive CLI chat: ```bash nanobot agent ``` -That's it! You have a working AI agent in 2 minutes. +After the interactive session can answer normally, nanobot can help with its own next setup step. Ask it to read the relevant docs, inspect your current `~/.nanobot/config.json`, and make one concrete change such as enabling WebUI, adding a provider preset, or configuring one chat channel. When nanobot says the config is updated, run `/restart` in the chat or restart the nanobot process manually so long-running processes reload `config.json`. + +Example prompt: + +```text +Read docs/quick-start.md, docs/providers.md, and docs/configuration.md in this checkout. +Then update ~/.nanobot/config.json to add an OpenRouter model preset named "primary". +Tell me exactly what changed and whether I need to run /restart. +``` + +Exit interactive mode with `exit`, `quit`, `/exit`, `/quit`, `:q`, or `Ctrl+D`. + +## 6. Choose Your Next Step + +| Want to... | Go to | +|---|---| +| Understand config, workspace, gateway, channels, memory, and tools | [`concepts.md`](./concepts.md) | +| Copy another provider or local model setup | [`provider-cookbook.md`](./provider-cookbook.md) | +| Understand provider/model matching | [`providers.md`](./providers.md) | +| Open the bundled browser UI | [`../webui/README.md`](../webui/README.md) | +| Connect Telegram, Discord, WeChat, Slack, Email, or another chat app | [`chat-apps.md`](./chat-apps.md) | +| Configure web search, MCP, security, memory, gateway, or runtime settings | [`configuration.md`](./configuration.md) | +| Run with Docker, systemd, or LaunchAgent | [`deployment.md`](./deployment.md) | +| Debug a failure | [`troubleshooting.md`](./troubleshooting.md) | + +## Updating + +**pip:** + +```bash +python -m pip install -U nanobot-ai +nanobot --version +``` + +**uv:** + +```bash +uv tool upgrade nanobot-ai +nanobot --version +``` + +**Source checkout:** + +```bash +git pull +python -m pip install -e . +nanobot --version +``` + +If you use WhatsApp, rebuild the local bridge after upgrading: + +```bash +rm -rf ~/.nanobot/bridge +nanobot channels login whatsapp +``` + +## First-Run Troubleshooting + +| Symptom | What to check | +|---------|---------------| +| `nanobot: command not found` | Use `python -m nanobot ...`, or add your Python scripts directory to `PATH`. | +| `ModuleNotFoundError: nanobot` | Confirm you installed into the same Python environment that is running the command. | +| JSON parse errors | Check commas and braces in `~/.nanobot/config.json`; examples above are partial snippets to merge. | +| Authentication or 401 errors | Check that the API key is valid, copied without spaces, and placed under the provider you selected. | +| Provider/model errors | Make sure the active preset uses the provider that owns your API key and that the model exists there. | +| The CLI works but a chat app does not reply | First keep `nanobot gateway` running, then follow [`chat-apps.md`](./chat-apps.md). | +| WebUI does not open | Enable the WebSocket channel and open port `8765`, not the gateway health port `18790`. | + +For a fuller diagnosis flow, see [`troubleshooting.md`](./troubleshooting.md). diff --git a/docs/start-without-technical-background.md b/docs/start-without-technical-background.md new file mode 100644 index 000000000..f604e9f09 --- /dev/null +++ b/docs/start-without-technical-background.md @@ -0,0 +1,431 @@ +# Start Without Technical Background + +This page is for you if you have never used a terminal, edited a JSON file, or configured an AI model before. + +The goal is small: get one local nanobot reply. Do not connect Telegram, Discord, WebUI, Docker, local models, or deployment yet. Those are easier after the first reply works. + +## What You Are Setting Up + +You will see these words during setup: + +| Word | Plain meaning | +|---|---| +| Terminal | A text window where you paste commands and press Enter. | +| Command | One line of text you run in the terminal. | +| API key | A password-like token from an AI provider. Do not share it publicly. | +| Provider | The service that owns the API key or local model endpoint. | +| Model | The AI model ID that the provider can run. | +| Config file | The settings file nanobot reads when it starts. | +| Wizard | An interactive terminal menu that edits the config file for you. | +| Model preset | A named model choice in the config file. | +| `apiBase` | The HTTP address of a provider endpoint. Leave it blank unless your provider, proxy, or local server tells you to set one. | + +## 1. Open a Terminal + +You will paste commands into a terminal. Copy only the command text inside each code block; do not copy the ``` marks. + +| System | How to open it | +|---|---| +| Windows | Press `Win`, type `PowerShell`, then open **Windows PowerShell**. | +| macOS | Press `Command` + `Space`, type `Terminal`, then press `Enter`. | +| Linux | Open your app launcher, search for `Terminal`, then open it. | + +When the terminal opens, click inside it, paste the command, and press `Enter`. If a command prints text and returns to a prompt, that is usually normal. + +## 2. Install Python + +Install Python 3.11 or newer from [python.org](https://www.python.org/downloads/). + +On Windows, enable **Add python.exe to PATH** during installation if the installer shows that option. + +In that terminal, check Python: + +```bash +python --version +``` + +If Windows says `python` is not found, close and reopen PowerShell. If it still does not work, try: + +```bash +py --version +``` + +If `py` works but `python` does not, replace `python` with `py` in the commands below. + +If macOS or Linux says `python` is not found, try: + +```bash +python3 --version +``` + +If `python3` works but `python` does not, replace `python` with `python3` in the manual commands below. The one-command installer already checks both `python3` and `python`. + +## 3. Get a Provider API Key + +nanobot does not create AI accounts or API keys for you. Use an AI provider account, company endpoint, subscription endpoint, or local model server that you already control. The steps below use OpenRouter only as a concrete example so the commands and wizard choices have real names; it is not a ranking, default choice, or endorsement. + +If you use another provider, keep the same shape but replace the provider name, API key, and model ID with values from that provider. [`provider-cookbook.md`](./provider-cookbook.md) has copyable snippets for several common patterns. + +For the example path: + +1. Open [openrouter.ai/keys](https://openrouter.ai/keys). +2. Create or copy an API key. +3. Keep the key private. + +An OpenRouter key usually starts with `sk-or-v1-`. Other providers use different key shapes. Keep the key nearby because the setup wizard will ask you to paste it. + +## 4. Install nanobot + +The easiest path is the one-command installer. It installs or upgrades nanobot, then starts the setup wizard. + +**macOS / Linux** + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" +``` + +**Windows PowerShell** + +```powershell +irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1 | iex +``` + +These commands install the stable PyPI package. To preview what the installer would do without changing your environment, pass `--dry-run`: + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" -- --dry-run +``` + +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1))) --dry-run +``` + +Use the development installer only when a maintainer asks you to test the current `main` branch: + +```bash +sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)" -- --dev +``` + +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.ps1))) --dev +``` + +If the command says `curl` or `irm` is not found, or it cannot download from GitHub, use the manual install command below. + +If you prefer to install manually, run: + +```bash +python -m pip install nanobot-ai +``` + +Then check that nanobot is installed: + +```bash +nanobot --version +``` + +If the terminal cannot find `nanobot`, use the module form: + +```bash +python -m nanobot --version +``` + +Use `python3 -m nanobot --version` or `py -m nanobot --version` if that is the Python command that worked in step 2. + +## 5. Run the Setup Wizard + +The one-command installer starts this for you after installation. If you installed manually, run: + +```bash +nanobot onboard --wizard +``` + +If `nanobot` is not found, run: + +```bash +python -m nanobot onboard --wizard +``` + +Use `python3 -m nanobot onboard --wizard` or `py -m nanobot onboard --wizard` if that is the Python command that worked in step 2. + +The wizard is a terminal menu. It is not a graphical app, but it lets you choose options instead of hand-editing every JSON field. + +You will see a menu like this: + +```text +> What would you like to configure? + [P] LLM Provider + [M] Model Presets + [C] Chat Channel + [H] Channel Common + [A] Agent Settings + [I] API Server + [G] Gateway + [T] Tools + [V] View Configuration Summary + [S] Save and Exit + [X] Exit Without Saving +``` + +Move through the wizard like this: + +| When you see | Do this | +|---|---| +| A menu | Use the arrow keys to highlight an option, then press `Enter`. | +| A text field | Type or paste the value, then press `Enter`. | +| A field you do not need | Keep the shown default or leave it blank, then press `Enter`. | +| A back option | Choose it to return to the previous menu. | + +For the first setup, only configure the model provider and one model preset. + +If you are following the OpenRouter example: + +1. Choose `[P] LLM Provider`. +2. Select OpenRouter. +3. Paste your OpenRouter API key. +4. Keep the default `apiBase`, or leave it blank if the wizard shows no default. Only change it if OpenRouter or your deployment guide explicitly tells you to set one. +5. Return to the main menu. +6. Choose `[M] Model Presets`. +7. Add or edit a preset named `primary`. +8. Set: + +```text +label: Primary +provider: openrouter +model: anthropic/claude-sonnet-4.5 +maxTokens: 4096 +contextWindowTokens: 65536 +temperature: 0.1 +``` + +If OpenRouter says your account cannot use that model, use another OpenRouter model ID that your account can access. + +If you are using another provider, use the same wizard choices but substitute that provider's values: + +| Wizard field | What to enter | +|---|---| +| Provider menu | The provider that owns your API key or endpoint. | +| API key | The key from that provider, or leave it blank only if the provider does not use one. | +| `apiBase` | Leave blank unless the provider docs, proxy docs, or local server docs give you a URL. | +| Preset `provider` | The nanobot provider name, such as the one shown in [`provider-cookbook.md`](./provider-cookbook.md). | +| Preset `model` | A model ID that provider can actually serve. | +| Preset name | `primary` is fine for the first setup. | + +Then choose `[S] Save and Exit`. + +The wizard creates or updates: + +| Path | Meaning | +|---|---| +| `~/.nanobot/config.json` | Settings file. | +| `~/.nanobot/workspace/` | Working folder for memory, sessions, and generated files. | + +## How to Merge JSON Snippets + +Most docs examples are snippets, not whole files. Your `config.json` has one outer `{ ... }`. Add new top-level sections such as `providers`, `modelPresets`, `agents`, or `channels` inside that same outer object. + +Do not paste two separate JSON objects into one file: + +```text +{ + "providers": { "...": "..." } +} +{ + "channels": { "...": "..." } +} +``` + +Merge them into one object: + +```json +{ + "providers": { + "openrouter": { + "apiKey": "sk-or-v1-your-key-here" + } + }, + "channels": { + "websocket": { + "enabled": true + } + } +} +``` + +Notice the comma after the `providers` block. JSON needs commas between sibling sections, but not after the last section. If this feels hard, use `nanobot onboard --wizard` whenever possible. + +## 6. Manual Config Fallback + +Use this only if the wizard is unavailable or you prefer opening the file yourself. + +Use one of these commands: + +**Windows PowerShell** + +```powershell +notepad "$env:USERPROFILE\.nanobot\config.json" +``` + +**macOS** + +```bash +open -e ~/.nanobot/config.json +``` + +**Linux** + +```bash +xdg-open ~/.nanobot/config.json +``` + +If this is a brand-new install and you have not configured anything else yet, replace the file with this minimal config: + +```json +{ + "providers": { + "openrouter": { + "apiKey": "sk-or-v1-your-key-here" + } + }, + "modelPresets": { + "primary": { + "label": "Primary", + "provider": "openrouter", + "model": "anthropic/claude-sonnet-4.5", + "maxTokens": 4096, + "contextWindowTokens": 65536, + "temperature": 0.1 + } + }, + "agents": { + "defaults": { + "modelPreset": "primary" + } + } +} +``` + +Replace `sk-or-v1-your-key-here` with your real OpenRouter key. + +If you use another provider, replace `openrouter`, `sk-or-v1-your-key-here`, and the `model` value with that provider's values. If the provider needs `apiBase`, add it under that provider's config block. + +Save the file. + +## 7. Send the First Message + +First check that nanobot can read the saved setup: + +```bash +nanobot status +``` + +This should show the config file path, workspace path, and the active model or preset. If `nanobot` is not found, use `python -m nanobot status`, `python3 -m nanobot status`, or `py -m nanobot status`, matching the Python command that worked in step 2. + +It is normal for most providers to say `not set`. Only the provider you selected for the active preset needs to look configured. + +Run: + +```bash +nanobot agent -m "Hello!" +``` + +If that works, nanobot is installed and can call the model. + +You should see a normal assistant reply in the terminal. The exact words will differ, but it should look like this shape: + +```text +Hello! How can I help you today? +``` + +If `nanobot` is not found, run: + +```bash +python -m nanobot agent -m "Hello!" +``` + +Use `python3 -m nanobot agent -m "Hello!"` or `py -m nanobot agent -m "Hello!"` if that is the Python command that worked in step 2. + +Once this works, nanobot can help with its own next setup step. Run `nanobot agent`, ask it to read these docs and update your current config for one specific goal, then run `/restart` when nanobot tells you the config is ready. For example, ask it to enable the browser UI, add one provider preset, or configure one chat app. + +## 8. If Something Fails + +Do not change many things at once. Check the exact error: + +| Error or symptom | What it usually means | +|---|---| +| `JSON parse error` | The config file has a missing comma, extra comma, or mismatched brace. Copy the example again. | +| `401`, `unauthorized`, or `invalid API key` | The API key is wrong, expired, has extra spaces, or was pasted under the wrong provider. | +| `model not found` | The model ID is not available through the selected provider or your account cannot use it. | +| `nanobot: command not found` | The install worked in Python, but your shell cannot find the script. Use `python -m nanobot ...`, `python3 -m nanobot ...`, or `py -m nanobot ...`, matching the Python command that worked earlier. | +| No response after editing config | Restart the command. Long-running processes read config when they start. | + +For a fuller diagnosis path, see [`troubleshooting.md`](./troubleshooting.md). + +## What Not to Configure Yet + +Skip these until the first local message works: + +- `apiBase`: hosted built-in providers often already have default endpoints. You only need `apiBase` for local models, proxies, custom OpenAI-compatible providers, or special regional/subscription endpoints. +- WebUI and chat apps: first prove `nanobot agent -m "Hello!"`. +- fallback models: useful later, but not needed for the first reply. +- Langfuse: useful for observability, but not needed for first setup. + +## Next Steps + +After the first reply works, choose only one next goal. Keep the terminal that runs `nanobot gateway` open whenever you use the WebUI or a chat app. + +### Open the Browser UI + +1. Add this snippet to `~/.nanobot/config.json`. Merge it into the existing file instead of replacing the whole file: + +```json +{ "channels": { "websocket": { "enabled": true } } } +``` + +2. Run: + +```bash +nanobot gateway +``` + +3. Leave that terminal open. +4. Open `http://127.0.0.1:8765` in your browser. + +To stop the WebUI later, return to the gateway terminal and press `Ctrl+C`. + +If `nanobot` is not found, run `python -m nanobot gateway`, `python3 -m nanobot gateway`, or `py -m nanobot gateway`, matching the Python command that worked earlier. More details are in [`../webui/README.md`](../webui/README.md). + +### Connect a Chat App + +1. Read the section for one app in [`chat-apps.md`](./chat-apps.md). +2. Add only that app's config snippet. Merge it into the existing file instead of replacing the whole file. +3. Run: + +```bash +nanobot channels status +nanobot gateway +``` + +4. Leave the gateway terminal open, then send a message from the allowed account. + +Start with a private chat or a test server. Do not set `allowFrom` to `["*"]` unless you intentionally want anyone who can reach that channel to talk to the bot. + +### Change Models or Add Backups + +Use [`providers.md`](./providers.md) when a provider/model pair fails, and [`provider-cookbook.md`](./provider-cookbook.md) when you want copyable snippets. Keep model choices in `modelPresets`, then select the active one with `agents.defaults.modelPreset`. + +### Ask for Help + +When you ask for help, include: + +- your operating system; +- the command you ran; +- `nanobot --version`; +- `nanobot status`; +- whether `nanobot agent -m "Hello!"` works; +- the exact error text; +- a config snippet with API keys and tokens removed. + +Never paste real API keys, bot tokens, OAuth tokens, or private chat IDs into a public issue or chat. + +If you find a docs mistake, outdated command, or confusing step, please open an issue: . diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 000000000..8e26101cf --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,266 @@ +# Troubleshooting + +Use this page to isolate where a failure lives. Start with the smallest surface that proves the most: local CLI first, then gateway, then WebUI or chat apps. + +## Fast Diagnosis Order + +Run these in order: + +```bash +nanobot --version +nanobot status +nanobot agent -m "Hello!" +``` + +Then, only if the CLI works: + +```bash +nanobot gateway +``` + +This separates failures into layers: + +| Layer | What it proves | +|---|---| +| `nanobot --version` | Install and shell command discovery | +| `nanobot status` | Config path, workspace path, active model, and provider summary | +| `nanobot agent -m "Hello!"` | Config loading, provider/model access, workspace writes, and agent loop | +| `nanobot gateway` | Channel startup, cron system jobs, heartbeat, WebUI/WebSocket, and health endpoint | + +If `nanobot agent -m "Hello!"` fails, fix that before debugging WebUI, Telegram, Discord, Docker, systemd, or any chat app. + +## How to Read `nanobot status` + +`nanobot status` does not call a model. It only checks whether nanobot can find the default config, default workspace, active model or preset, and provider setup summary. + +The output has this shape: + +```text +nanobot Status + +Config: /path/to/config.json ✓ +Workspace: /path/to/workspace ✓ +Model: provider/model-name (preset: primary) +Provider A: not set +Provider B: ✓ +Local Provider: ✓ http://localhost:11434/v1 +OAuth Provider: ✓ (OAuth) +``` + +Read it like this: + +| Line | Good sign | What to do if it looks wrong | +|---|---|---| +| `Config` | It points to the config file you meant to use and shows `✓`. | Run `nanobot onboard`, or pass `--config` to `nanobot agent`, `gateway`, or `serve` when testing a non-default instance. | +| `Workspace` | It points to the workspace you meant to use and shows `✓`. | Run `nanobot onboard`, create the folder, fix permissions, or pass `--workspace` on commands that support it. | +| `Model` | It shows the active model or the preset name you expect. | Set `agents.defaults.modelPreset` to the intended preset, or check `/model` if you changed models during a chat session. | +| Provider rows | The provider used by the active preset shows `✓`, an OAuth marker, or a local URL. | Configure only the active provider first. It is normal for unused providers to say `not set`. | + +If `nanobot status` looks right but `nanobot agent -m "Hello!"` fails, the install and config paths are probably fine. Continue with [Provider and Model Problems](#provider-and-model-problems). + +## Installation Problems + +Use the same Python command for install checks and module fallback. On macOS/Linux that may be `python3`; on Windows it may be `python` or `py`. + +| Symptom | Check | +|---|---| +| `python: command not found` | Try `python3 --version` on macOS/Linux or `py --version` on Windows. Then replace `python` in docs commands with the command that worked. | +| `curl: command not found` | The macOS/Linux one-command installer could not download the script. Install curl, or use manual install: `python -m pip install nanobot-ai`, replacing `python` with `python3` if needed. | +| `irm` is not recognized | PowerShell could not run the download helper. Use manual install: `python -m pip install nanobot-ai`, or `py -m pip install nanobot-ai` on Windows. | +| Could not download `raw.githubusercontent.com` | Your network, proxy, or firewall blocked the installer script download. Use manual install from PyPI, or configure your proxy and rerun the command. | +| `nanobot: command not found` | Use the module form, for example `python -m nanobot ...`, `python3 -m nanobot ...`, or `py -m nanobot ...`. Reinstall with the same Python command, or add that Python's scripts directory to `PATH`. | +| `No module named nanobot` | You are running a different Python than the one used for installation. Run `python -m pip show nanobot-ai`, `python3 -m pip show nanobot-ai`, or `py -m pip show nanobot-ai`, matching the command that installed nanobot. | +| `pip is not available` | The installer tries `python -m ensurepip --upgrade` first. If that fails, install pip for that Python, or use a Python installer/distribution that includes pip. | +| `externally-managed-environment` | Your system Python blocks global pip installs. The one-command installer retries with `--user`; if that still fails, create a virtual environment or install with `uv`/`pipx`. | +| Installer chose the wrong Python | Set `PYTHON` before running the installer, such as `PYTHON=python3 sh -c "$(curl -fsSL https://raw.githubusercontent.com/HKUDS/nanobot/main/scripts/install.sh)"` or `$env:PYTHON="py"` before the PowerShell command. | +| Editable source install does not update | From the repo root, run `python -m pip install -e .` again with the Python command used for development, then check `python -m nanobot --version` or `nanobot --version`. | +| WebUI build tools missing | They are only needed for WebUI development. Packaged installs already include the WebUI bundle. | + +## Config Problems + +Default config path: + +```text +~/.nanobot/config.json +``` + +Default workspace path: + +```text +~/.nanobot/workspace/ +``` + +`nanobot status` reads the default config. Use explicit paths on commands that support them when debugging multiple instances: + +```bash +nanobot agent --config ./bot-a/config.json --workspace ./bot-a/workspace -m "Hello" +nanobot gateway --config ./bot-a/config.json --workspace ./bot-a/workspace +``` + +Common config mistakes: + +| Symptom | Check | +|---|---| +| JSON parse error | Validate commas, braces, and quotes. Most docs examples are partial snippets to merge. | +| Unknown or missing provider | Use provider registry names such as `openrouter`, `anthropic`, `openai`, `ollama`, `vllm`, `lm_studio`. | +| snake_case vs camelCase confusion | Both are accepted, but docs use camelCase because nanobot writes config with aliases such as `apiKey`, `modelPresets`, `intervalS`. | +| Environment variable error | `${VAR_NAME}` references are resolved at startup. Set the variable before running nanobot. | +| Edited config but behavior did not change | Restart `nanobot gateway`; long-running processes read config at startup. | + +To refresh missing defaults without overwriting existing settings, run: + +```bash +nanobot onboard +``` + +When prompted about overwriting the config, choose the option that keeps current values and merges missing defaults. + +## Provider and Model Problems + +First prove the provider in the CLI: + +```bash +nanobot agent -m "Hello!" +``` + +Then compare your config against [`providers.md`](./providers.md). + +If you need a known-good snippet instead of diagnosis, use [`provider-cookbook.md`](./provider-cookbook.md). + +| Symptom | Likely cause | +|---|---| +| 401, unauthorized, invalid API key | Key is missing, expired, pasted with whitespace, or under the wrong provider key. | +| Model not found | The model ID belongs to a different provider or gateway. | +| Provider cannot be inferred | Pin `modelPresets..provider` in the active preset instead of using `"auto"`. For legacy direct configs, pin `agents.defaults.provider`. | +| Local model connection refused | Ollama, vLLM, LM Studio, or another local server is not running, or `apiBase` points to the wrong port. | +| Bedrock validation error | Check AWS region, credentials, model access, model ID, and whether the model supports Converse. | +| OAuth provider fails | Run `nanobot provider login openai-codex` or `nanobot provider login github-copilot`, then select the provider explicitly. | + +## Langfuse Problems + +Langfuse tracing is optional and controlled by environment variables. + +| Symptom | Check | +|---|---| +| `LANGFUSE_SECRET_KEY is set but langfuse is not installed` | Install `langfuse` in the same Python environment that runs nanobot, then restart the process. | +| No traces appear | Set `LANGFUSE_SECRET_KEY`, `LANGFUSE_PUBLIC_KEY`, and `LANGFUSE_BASE_URL` before starting nanobot. | +| Wrong Langfuse project or region | Check that the key pair and `LANGFUSE_BASE_URL` come from the same Langfuse project/region. | +| Only some providers trace | Langfuse tracing applies to OpenAI-compatible provider calls; native providers may not use that client path. | + +See [`configuration.md#langfuse-observability`](./configuration.md#langfuse-observability) for setup commands. + +## Gateway Problems + +`nanobot gateway` is required for WebUI, chat apps, heartbeat, Dream, and long-running channel connections. + +Default ports: + +| Surface | Default | +|---|---| +| Gateway health endpoint | `http://127.0.0.1:18790/health` | +| WebUI/WebSocket channel | `http://127.0.0.1:8765` | +| OpenAI-compatible API (`nanobot serve`) | `http://127.0.0.1:8900` | + +Common gateway checks: + +```bash +nanobot gateway --verbose +``` + +| Symptom | Check | +|---|---| +| Port already in use | Change `gateway.port`, `channels.websocket.port`, or the `--port` CLI flag for the relevant command. | +| WebUI opened on `18790` but shows nothing useful | Open `8765`; `18790` is the health endpoint. | +| Config changes ignored | Restart the gateway. | +| Heartbeat never runs | Keep the gateway running, add tasks under `/HEARTBEAT.md` -> `## Active Tasks`, and make sure `gateway.heartbeat.enabled` is true. | +| Cron jobs disappeared after switching workspaces | Cron jobs are workspace-scoped at `/cron/jobs.json`; check you are using the intended workspace. | + +## WebUI Problems + +The packaged WebUI is served by the WebSocket channel. + +Minimal config: + +```json +{ + "channels": { + "websocket": { + "enabled": true + } + } +} +``` + +Then run: + +```bash +nanobot gateway +``` + +Open: + +```text +http://127.0.0.1:8765 +``` + +If accessing from another device, bind the WebSocket channel to `0.0.0.0` and set `token` or `tokenIssueSecret`. The WebSocket channel refuses public binds without a token or token issue secret. + +See [`../webui/README.md`](../webui/README.md) for LAN and development setup. + +## Chat App Problems + +Before debugging a chat app: + +```bash +nanobot agent -m "Hello!" +nanobot channels status +nanobot gateway +``` + +Then check: + +| Symptom | Check | +|---|---| +| Bot never replies | Gateway is not running, the channel is not enabled, or the bot/app token is wrong. | +| Unknown sender ignored | Configure `allowFrom`, pairing, or the channel-specific allow list. | +| Telegram fails | Confirm the BotFather token and `allowFrom` user ID. | +| Discord replies missing | Enable Message Content intent and invite the bot with the required permissions. | +| WhatsApp or WeChat login expired | Re-run `nanobot channels login whatsapp` or `nanobot channels login weixin`. | +| Chat app works but WebUI does not | The provider and gateway are likely fine; debug the WebSocket channel separately. | + +See [`chat-apps.md`](./chat-apps.md) for channel-specific setup. + +## Tool and Workspace Problems + +| Symptom | Check | +|---|---| +| File access denied | Check `tools.restrictToWorkspace` and whether the target path is inside the active workspace. | +| Shell commands fail in Docker | Sandbox settings may need Linux capabilities; see [`deployment.md`](./deployment.md). | +| Web fetch blocked | SSRF protection blocks unsafe targets; use `tools.ssrfWhitelist` only for trusted private networks. | +| MCP tools missing | Check `tools.mcpServers`, server startup command, environment variables, and tool allow list. | +| Generated artifacts are missing | Check the active workspace and channel media directory. | + +## Memory and Session Problems + +| Symptom | Check | +|---|---| +| Conversation context seems wrong | Confirm the active workspace and session. WebUI chats and chat app threads may use different sessions. | +| Memory does not update immediately | Dream consolidation is periodic; recent turns still live in session history. | +| Old sessions appear after moving config | Session files are stored under `/sessions/`; verify the workspace path. | +| You want one shared session across devices | Set `agents.defaults.unifiedSession` intentionally; otherwise keep separate sessions. | + +## Collect Useful Evidence + +When opening an issue or asking for help, include: + +- install method and `nanobot --version`; +- operating system and Python version; +- the command you ran; +- relevant `nanobot status` output; +- sanitized config snippets, especially provider, model, channel, and tool settings; +- gateway logs from `nanobot gateway --verbose`; +- whether `nanobot agent -m "Hello!"` works. + +Never paste real API keys, bot tokens, OAuth tokens, or private chat IDs into public issues. + +If you find a docs mistake, outdated command, or confusing step, please open an issue: . diff --git a/nanobot/templates/AGENTS.md b/nanobot/templates/AGENTS.md index a6c046de4..08418141e 100644 --- a/nanobot/templates/AGENTS.md +++ b/nanobot/templates/AGENTS.md @@ -6,18 +6,18 @@ Use this file for project-specific preferences, recurring workflow conventions, ## Scheduled Reminders -Before scheduling reminders, check available skills and follow skill guidance first. -Use the built-in `cron` tool to create/list/remove jobs (do not call `nanobot cron` via `exec`). -Get USER_ID and CHANNEL from the current session (e.g., `8281248569` and `telegram` from `telegram:8281248569`). +- Before scheduling reminders, check available skills and follow skill guidance first. +- Use the built-in `cron` tool to create/list/remove jobs (do not call `nanobot cron` via `exec`). +- Get USER_ID and CHANNEL from the current session (e.g., `8281248569` and `telegram` from `telegram:8281248569`). **Do NOT just write reminders to MEMORY.md** — that won't trigger actual notifications. ## Heartbeat Tasks -`HEARTBEAT.md` is checked periodically when registered as a cron job. Use the built-in `cron` tool to schedule it (e.g. `cron add --name heartbeat --schedule "every 30m" --message "Check HEARTBEAT.md"`). +`HEARTBEAT.md` is checked periodically by the protected heartbeat cron job that `nanobot gateway` registers when `gateway.heartbeat.enabled` is true. Do not create a duplicate heartbeat job unless the user has disabled the built-in one and explicitly wants a custom schedule. - Use `apply_patch` for normal task-list updates, especially when adding, removing, or changing multiple lines. - Use `edit_file` only for small exact replacements copied from the current `HEARTBEAT.md`. - Use `write_file` for first creation or intentional full-file rewrites. -When the user asks for a recurring/periodic task, update `HEARTBEAT.md` and register it via `cron` instead of creating a one-time reminder. +When the user asks for a recurring/periodic heartbeat task, update `HEARTBEAT.md` instead of creating a one-time reminder. Use the built-in `cron` tool for separate reminders or custom schedules that should not be part of the heartbeat task list. diff --git a/nanobot/templates/HEARTBEAT.md b/nanobot/templates/HEARTBEAT.md index e29f64d41..e8da6fab3 100644 --- a/nanobot/templates/HEARTBEAT.md +++ b/nanobot/templates/HEARTBEAT.md @@ -1,11 +1,9 @@ # Heartbeat Tasks ## Active Tasks diff --git a/nanobot/templates/agent/tool_contract.md b/nanobot/templates/agent/tool_contract.md index ba65cfc79..a95a2353f 100644 --- a/nanobot/templates/agent/tool_contract.md +++ b/nanobot/templates/agent/tool_contract.md @@ -1,7 +1,6 @@ # Tool Usage Notes -Tool signatures are provided automatically via function calling. This section -documents the general tool contract and non-obvious usage patterns. +Tool signatures are provided automatically via function calling. This section documents the general tool contract and non-obvious usage patterns. ## General Tool Contract @@ -63,5 +62,5 @@ documents the general tool contract and non-obvious usage patterns. ## Scheduling and Background Work - Use `cron` for scheduled reminders or recurring jobs; do not run `nanobot cron` through `exec`. -- For heartbeat tasks, register `HEARTBEAT.md` as a cron job according to the agent instructions. +- For heartbeat tasks, update `HEARTBEAT.md`; the default gateway heartbeat cron job handles periodic checks when enabled. - Do not write reminders only to memory files when the user expects an actual notification. diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 000000000..2420edbd5 --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,163 @@ +param( + [switch]$Dev, + [switch]$DryRun, + [Parameter(ValueFromRemainingArguments = $true)] + [string[]]$RemainingArgs +) + +$ErrorActionPreference = "Stop" + +$Package = "nanobot-ai" +$MainSource = "https://github.com/HKUDS/nanobot/archive/refs/heads/main.zip" +$InstallTarget = $Package +$InstallSource = "PyPI" + +function Write-Info { + param([string]$Message) + Write-Host $Message +} + +function Fail { + param([string]$Message) + throw "Error: $Message" +} + +function Show-InstallFailureHint { + [Console]::Error.WriteLine("Error: pip could not install nanobot from $InstallSource.") + [Console]::Error.WriteLine("If pip mentioned externally-managed-environment, install in a virtual environment or use uv/pipx.") + [Console]::Error.WriteLine("You can also run manually:") + [Console]::Error.WriteLine(" $Python -m pip install --upgrade $InstallTarget") + [Console]::Error.WriteLine("Then start setup with:") + [Console]::Error.WriteLine(" $Python -m nanobot onboard --wizard") + throw "pip could not install nanobot from $InstallSource" +} + +function Show-Usage { + Write-Host "Usage: install.ps1 [-Dev|--dev] [-DryRun|--dry-run]" + Write-Host "" + Write-Host "By default this installs or upgrades nanobot-ai from PyPI." + Write-Host "Use --dev to install from the current main branch on GitHub." + Write-Host "Use --dry-run to print what would happen without installing or starting the wizard." +} + +function Test-Python { + param([string]$Command) + try { + & $Command -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else 1)" *> $null + return $LASTEXITCODE -eq 0 + } catch { + return $false + } +} + +function Find-Python { + if ($env:PYTHON) { + if (Get-Command $env:PYTHON -ErrorAction SilentlyContinue) { + if (Test-Python $env:PYTHON) { + return $env:PYTHON + } + Fail "PYTHON=$env:PYTHON is not Python 3.11 or newer." + } + Fail "PYTHON=$env:PYTHON was not found." + } + + foreach ($Candidate in @("python", "py")) { + if (Get-Command $Candidate -ErrorAction SilentlyContinue) { + if (Test-Python $Candidate) { + return $Candidate + } + } + } + + Fail "Python 3.11 or newer was not found. Install Python first, then rerun this command." +} + +foreach ($Arg in $RemainingArgs) { + switch ($Arg) { + "--dev" { + $Dev = $true + } + "--dry-run" { + $DryRun = $true + } + "-h" { + Show-Usage + return + } + "--help" { + Show-Usage + return + } + default { + Fail "Unknown option: $Arg" + } + } +} + +if ($Dev) { + $InstallTarget = $MainSource + $InstallSource = "GitHub main" +} + +$Python = Find-Python +$Version = & $Python --version +Write-Info "Using Python: $Version" + +try { + & $Python -m pip --version *> $null +} catch {} + +if ($LASTEXITCODE -ne 0) { + if ($DryRun) { + Write-Info "Dry run: pip was not found. Install would try: $Python -m ensurepip --upgrade" + } else { + Write-Info "pip was not found for this Python. Trying ensurepip..." + & $Python -m ensurepip --upgrade *> $null + if ($LASTEXITCODE -ne 0) { + Fail "pip is not available. Install pip for $Python, then rerun this command." + } + } +} + +if ($DryRun) { + Write-Info "Dry run: would install or upgrade nanobot from $InstallSource." + Write-Info "Dry run: would run: $Python -m pip install --upgrade $InstallTarget" + Write-Info "Dry run: if that fails because system site-packages are not writable, would retry: $Python -m pip install --user --upgrade $InstallTarget" + if ($env:NANOBOT_SKIP_WIZARD -eq "1") { + Write-Info "Dry run: would skip setup wizard because NANOBOT_SKIP_WIZARD=1." + } else { + Write-Info "Dry run: would run: $Python -m nanobot onboard --wizard" + } + Write-Info "Dry run: no changes made." + return +} + +Write-Info "Installing or upgrading nanobot from $InstallSource..." +& $Python -m pip install --upgrade $InstallTarget +if ($LASTEXITCODE -ne 0) { + Write-Info "Install failed. Retrying as a user install..." + & $Python -m pip install --user --upgrade $InstallTarget + if ($LASTEXITCODE -ne 0) { + Show-InstallFailureHint + } +} + +Write-Info "Installed nanobot:" +& $Python -m nanobot --version +if ($LASTEXITCODE -ne 0) { + Fail "nanobot was installed, but the command could not be started." +} + +if ($env:NANOBOT_SKIP_WIZARD -eq "1") { + Write-Info "Skipping setup wizard because NANOBOT_SKIP_WIZARD=1." + Write-Info "Run this later: $Python -m nanobot onboard --wizard" + return +} + +Write-Info "Starting setup wizard..." +& $Python -m nanobot onboard --wizard +if ($LASTEXITCODE -ne 0) { + Fail "Setup wizard did not complete." +} + +Write-Info "Done. Try: $Python -m nanobot agent -m `"Hello!`"" diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 000000000..49e4bff82 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,129 @@ +#!/bin/sh +set -eu + +package="nanobot-ai" +main_source="https://github.com/HKUDS/nanobot/archive/refs/heads/main.zip" +install_target="$package" +install_source="PyPI" +dry_run="0" + +info() { + printf '%s\n' "$*" +} + +fail() { + printf 'Error: %s\n' "$*" >&2 + exit 1 +} + +install_failure_hint() { + printf '%s\n' "Error: pip could not install nanobot from $install_source." >&2 + printf '%s\n' "If pip mentioned externally-managed-environment, install in a virtual environment or use uv/pipx." >&2 + printf '%s\n' "You can also run manually:" >&2 + printf ' %s\n' "$python_bin -m pip install --upgrade $install_target" >&2 + printf '%s\n' "Then start setup with:" >&2 + printf ' %s\n' "$python_bin -m nanobot onboard --wizard" >&2 + exit 1 +} + +usage() { + cat <<'EOF' +Usage: install.sh [--dev] [--dry-run] + +By default this installs or upgrades nanobot-ai from PyPI. +Use --dev to install from the current main branch on GitHub. +Use --dry-run to print what would happen without installing or starting the wizard. +EOF +} + +find_python() { + for candidate in python3 python; do + if command -v "$candidate" >/dev/null 2>&1; then + if "$candidate" - <<'PY' >/dev/null 2>&1 +import sys +raise SystemExit(0 if sys.version_info >= (3, 11) else 1) +PY + then + printf '%s\n' "$candidate" + return 0 + fi + fi + done + return 1 +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --dev) + install_target="$main_source" + install_source="GitHub main" + ;; + --dry-run) + dry_run="1" + ;; + -h|--help) + usage + exit 0 + ;; + *) + fail "Unknown option: $1" + ;; + esac + shift +done + +python_bin="${PYTHON:-}" + +if [ -n "$python_bin" ]; then + command -v "$python_bin" >/dev/null 2>&1 || fail "PYTHON=$python_bin was not found" + "$python_bin" - <<'PY' >/dev/null 2>&1 || fail "nanobot requires Python 3.11 or newer" +import sys +raise SystemExit(0 if sys.version_info >= (3, 11) else 1) +PY +else + python_bin="$(find_python)" || fail "Python 3.11 or newer was not found. Install Python first, then rerun this command." +fi + +info "Using Python: $("$python_bin" --version 2>&1)" + +if ! "$python_bin" -m pip --version >/dev/null 2>&1; then + if [ "$dry_run" = "1" ]; then + info "Dry run: pip was not found. Install would try: $python_bin -m ensurepip --upgrade" + else + info "pip was not found for this Python. Trying ensurepip..." + "$python_bin" -m ensurepip --upgrade >/dev/null 2>&1 || fail "pip is not available. Install pip for $python_bin, then rerun this command." + fi +fi + +if [ "$dry_run" = "1" ]; then + info "Dry run: would install or upgrade nanobot from $install_source." + info "Dry run: would run: $python_bin -m pip install --upgrade $install_target" + info "Dry run: if that fails because system site-packages are not writable, would retry: $python_bin -m pip install --user --upgrade $install_target" + if [ "${NANOBOT_SKIP_WIZARD:-}" = "1" ]; then + info "Dry run: would skip setup wizard because NANOBOT_SKIP_WIZARD=1." + else + info "Dry run: would run: $python_bin -m nanobot onboard --wizard" + fi + info "Dry run: no changes made." + exit 0 +fi + +info "Installing or upgrading nanobot from $install_source..." +if ! "$python_bin" -m pip install --upgrade "$install_target"; then + info "Install failed. Retrying as a user install..." + "$python_bin" -m pip install --user --upgrade "$install_target" || install_failure_hint +fi + +info "Installed nanobot:" +"$python_bin" -m nanobot --version + +if [ "${NANOBOT_SKIP_WIZARD:-}" = "1" ]; then + info "Skipping setup wizard because NANOBOT_SKIP_WIZARD=1." + info "Run this later: $python_bin -m nanobot onboard --wizard" + exit 0 +fi + +info "Starting setup wizard..." +"$python_bin" -m nanobot onboard --wizard + +info "Done. Try: $python_bin -m nanobot agent -m \"Hello!\"" diff --git a/webui/README.md b/webui/README.md index 8538bc1ed..2730a2721 100644 --- a/webui/README.md +++ b/webui/README.md @@ -1,18 +1,53 @@ -# nanobot webui +# nanobot WebUI -The browser front-end for the nanobot gateway. It is built with Vite + React 18 + -TypeScript + Tailwind 3 + shadcn/ui, talks to the gateway over the WebSocket -multiplex protocol, and reads session metadata from the embedded REST surface -on the same port. +The WebUI is the browser workbench served by `nanobot gateway`. If you installed `nanobot-ai` from PyPI, the WebUI bundle is already included; this `webui/` source tree is only needed when you are changing the frontend. -For the project overview, install guide, and general docs map, see the root -[`README.md`](../README.md). +For the project overview, install guide, and general docs map, see the root [`README.md`](../README.md) and [`docs/README.md`](../docs/README.md). + +## Pick a Path + +| Goal | Start with | Opens at | +|---|---|---| +| Use the bundled browser UI | [Just want to use the WebUI?](#just-want-to-use-the-webui) | `http://127.0.0.1:8765` | +| Use the WebUI from another device | [Access from another device (LAN)](#access-from-another-device-lan) | `http://:8765` | +| Change WebUI source code | [Develop the WebUI (Vite HMR)](#develop-the-webui-vite-hmr) | `http://127.0.0.1:5173` | +| Debug setup failures | [`docs/troubleshooting.md#webui-problems`](../docs/troubleshooting.md#webui-problems) | Diagnosis order and common fixes | ## Just want to use the WebUI? -If you installed nanobot via `pip install nanobot-ai`, the WebUI is **already bundled** in the wheel. Enable the WebSocket channel in `~/.nanobot/config.json` and run `nanobot gateway` — see the root [`README.md`](../README.md#-webui) for the 3-step setup. You do **not** need anything in this directory. +If you installed nanobot via `python -m pip install nanobot-ai`, the WebUI is **already bundled** in the wheel. You do **not** need Node.js, Bun, Vite, or anything in this directory unless you are changing the WebUI source code. -This `webui/` tree is for people **hacking on the WebUI itself** (UI changes, new components, styling, etc.). +First prove the provider path: + +```bash +nanobot agent -m "Hello!" +``` + +If the shell cannot find `nanobot`, use the module form from the same Python environment: + +```bash +python -m nanobot agent -m "Hello!" +``` + +Then merge this WebSocket snippet into your existing `~/.nanobot/config.json` instead of replacing the whole file: + +```json +{ "channels": { "websocket": { "enabled": true } } } +``` + +If you are new to JSON snippets, see [`docs/start-without-technical-background.md#how-to-merge-json-snippets`](../docs/start-without-technical-background.md#how-to-merge-json-snippets). + +Start the gateway: + +```bash +nanobot gateway +``` + +Leave this terminal running while you use the WebUI. Closing it stops the browser UI and WebSocket connection. + +Open [`http://127.0.0.1:8765`](http://127.0.0.1:8765). The gateway's `18790` port is only the health endpoint, not the browser UI. For setup failures, use [`docs/troubleshooting.md`](../docs/troubleshooting.md#webui-problems). + +This `webui/` tree is for people **changing the WebUI source code**. It is built with Vite + React 18 + TypeScript + Tailwind 3 + shadcn/ui, talks to the gateway over the WebSocket multiplex protocol, and reads session metadata from the embedded REST surface on the same port. ## Layout @@ -28,14 +63,14 @@ nanobot/web/dist/ build output served by the gateway From the repository root: ```bash -pip install -e . +python -m pip install -e . ``` > Editable installs intentionally **skip** the WebUI bundle step — Vite HMR is faster than rebuilding `dist/` on every change. ### 2. Enable the WebSocket channel -In `~/.nanobot/config.json`: +In `~/.nanobot/config.json`, merge: ```json { "channels": { "websocket": { "enabled": true } } } @@ -112,5 +147,4 @@ bun run test ## Acknowledgements -- [`agent-chat-ui`](https://github.com/langchain-ai/agent-chat-ui) for UI and - interaction inspiration across the chat surface. +- [`agent-chat-ui`](https://github.com/langchain-ai/agent-chat-ui) for UI and interaction inspiration across the chat surface.