mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-06-15 07:14:08 +00:00
* 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
237 lines
6.4 KiB
Markdown
237 lines
6.4 KiB
Markdown
# Python SDK
|
|
|
|
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
|
|
import asyncio
|
|
|
|
from nanobot import Nanobot
|
|
|
|
|
|
async def main() -> None:
|
|
async with Nanobot.from_config() as bot:
|
|
result = await bot.run("What time is it in Tokyo?")
|
|
print(result.content)
|
|
|
|
|
|
asyncio.run(main())
|
|
```
|
|
|
|
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
|
|
|
|
### Use a specific config or workspace
|
|
|
|
```python
|
|
from nanobot import Nanobot
|
|
|
|
bot = Nanobot.from_config(
|
|
config_path="~/.nanobot/config.json",
|
|
workspace="/my/project",
|
|
)
|
|
```
|
|
|
|
### Isolate conversations with `session_key`
|
|
|
|
Different session keys keep independent conversation history:
|
|
|
|
```python
|
|
await bot.run("hi", session_key="user-alice")
|
|
await bot.run("hi", session_key="task-42")
|
|
```
|
|
|
|
### Attach hooks for observability
|
|
|
|
Hooks let you inspect tool calls, streaming, and iteration state without modifying nanobot internals:
|
|
|
|
```python
|
|
from nanobot.agent import AgentHook, AgentHookContext
|
|
|
|
|
|
class AuditHook(AgentHook):
|
|
async def before_execute_tools(self, context: AgentHookContext) -> None:
|
|
for tc in context.tool_calls:
|
|
print(f"[tool] {tc.name}")
|
|
|
|
|
|
result = await bot.run("Review this change", hooks=[AuditHook()])
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### `Nanobot.from_config(config_path=None, *, workspace=None)`
|
|
|
|
Create a `Nanobot` instance from a config file.
|
|
|
|
| Param | Type | Default | Description |
|
|
|-------|------|---------|-------------|
|
|
| `config_path` | `str \| Path \| None` | `None` | Path to `config.json`. Defaults to `~/.nanobot/config.json`. |
|
|
| `workspace` | `str \| Path \| None` | `None` | Override the workspace directory from config. |
|
|
|
|
Raises `FileNotFoundError` if an explicit config path does not exist.
|
|
|
|
### `await bot.run(message, *, session_key="sdk:default", hooks=None)`
|
|
|
|
Run the agent once and return a `RunResult`.
|
|
|
|
| Param | Type | Default | Description |
|
|
|-------|------|---------|-------------|
|
|
| `message` | `str` | *(required)* | The user message to process. |
|
|
| `session_key` | `str` | `"sdk:default"` | Session identifier for conversation isolation. Different keys get independent history. |
|
|
| `hooks` | `list[AgentHook] \| None` | `None` | Lifecycle hooks for this run only. |
|
|
|
|
### `await bot.aclose()`
|
|
|
|
Release resources held by the SDK instance, including MCP connections. The async context manager calls this automatically:
|
|
|
|
```python
|
|
async with Nanobot.from_config() as bot:
|
|
result = await bot.run("Summarize this repo")
|
|
```
|
|
|
|
### `RunResult`
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `content` | `str` | The agent's final text response. |
|
|
| `tools_used` | `list[str]` | Reserved for richer SDK introspection; may be empty in current versions. |
|
|
| `messages` | `list[dict]` | Reserved for richer SDK introspection; may be empty in current versions. |
|
|
|
|
## Hooks
|
|
|
|
Hooks let you observe or customize the agent loop. Subclass `AgentHook` and override the methods you need.
|
|
|
|
### Hook lifecycle
|
|
|
|
| Method | When |
|
|
|--------|------|
|
|
| `wants_streaming()` | Return `True` if you want token-by-token `on_stream()` callbacks |
|
|
| `before_iteration(context)` | Before each LLM call |
|
|
| `on_stream(context, delta)` | On each streamed token when streaming is enabled |
|
|
| `on_stream_end(context, *, resuming)` | When streaming finishes |
|
|
| `before_execute_tools(context)` | Before tool execution |
|
|
| `after_iteration(context)` | After each iteration |
|
|
| `finalize_content(context, content)` | Transform final output text |
|
|
|
|
Useful fields on `AgentHookContext` include:
|
|
|
|
- `iteration`
|
|
- `messages`
|
|
- `response`
|
|
- `usage`
|
|
- `tool_calls`
|
|
- `tool_results`
|
|
- `tool_events`
|
|
- `final_content`
|
|
- `stop_reason`
|
|
- `error`
|
|
|
|
### Example: audit tool calls
|
|
|
|
```python
|
|
from nanobot.agent import AgentHook, AgentHookContext
|
|
|
|
|
|
class AuditHook(AgentHook):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.calls: list[str] = []
|
|
|
|
async def before_execute_tools(self, context: AgentHookContext) -> None:
|
|
for tc in context.tool_calls:
|
|
self.calls.append(tc.name)
|
|
print(f"[audit] {tc.name}({tc.arguments})")
|
|
```
|
|
|
|
```python
|
|
hook = AuditHook()
|
|
result = await bot.run("List files in /tmp", hooks=[hook])
|
|
print(result.content)
|
|
print(f"Tools observed: {hook.calls}")
|
|
```
|
|
|
|
### Example: receive streaming tokens
|
|
|
|
```python
|
|
from nanobot.agent import AgentHook, AgentHookContext
|
|
|
|
|
|
class StreamingHook(AgentHook):
|
|
def wants_streaming(self) -> bool:
|
|
return True
|
|
|
|
async def on_stream(self, context: AgentHookContext, delta: str) -> None:
|
|
print(delta, end="", flush=True)
|
|
|
|
async def on_stream_end(self, context: AgentHookContext, *, resuming: bool) -> None:
|
|
print()
|
|
```
|
|
|
|
### Compose multiple hooks
|
|
|
|
Pass multiple hooks when you want to combine behaviors:
|
|
|
|
```python
|
|
result = await bot.run("hi", hooks=[AuditHook(), MetricsHook()])
|
|
```
|
|
|
|
Async hook methods are fan-out with error isolation. `finalize_content` is a pipeline: each hook receives the previous hook's output.
|
|
|
|
### Example: post-process final content
|
|
|
|
```python
|
|
from nanobot.agent import AgentHook
|
|
|
|
|
|
class Censor(AgentHook):
|
|
def finalize_content(self, context, content):
|
|
return content.replace("secret", "***") if content else content
|
|
```
|
|
|
|
## Full Example
|
|
|
|
```python
|
|
import asyncio
|
|
import time
|
|
|
|
from nanobot import Nanobot
|
|
from nanobot.agent import AgentHook, AgentHookContext
|
|
|
|
|
|
class TimingHook(AgentHook):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self._started_at = 0.0
|
|
|
|
async def before_iteration(self, context: AgentHookContext) -> None:
|
|
self._started_at = time.perf_counter()
|
|
|
|
async def after_iteration(self, context: AgentHookContext) -> None:
|
|
elapsed_ms = (time.perf_counter() - self._started_at) * 1000
|
|
print(f"[timing] iteration {context.iteration} took {elapsed_ms:.1f}ms")
|
|
|
|
|
|
async def main() -> None:
|
|
bot = Nanobot.from_config(workspace="/my/project")
|
|
result = await bot.run(
|
|
"Explain the main function",
|
|
session_key="sdk:demo",
|
|
hooks=[TimingHook()],
|
|
)
|
|
print(result.content)
|
|
|
|
|
|
asyncio.run(main())
|
|
```
|