Replaces the never-executed "LiteRT-LM on UNO Q 4GB" spike plan with a measured record of the local-fallback path we actually run. Measured on board 65301572 (Qwen2.5-0.5B-Instruct, -c 8192, 4 threads), using llama-server's own timings rather than wall clock: prefill ~17-20 tok/s (linear), decode ~6-11 tok/s (degrades with KV) warm prefix-cached tool call: 3.8s, 6/6 correct structured calls Two findings that changed the deployment: 1. The board had drifted onto Qwen2.5-Coder-1.5B - larger and tuned for the wrong task. Reverting to the repo's 0.5B made tool calls ~6x faster (24s -> 3.8s) and freed ~700MB. The repo was right. 2. The harness, not the model, was the bottleneck. The default agent profile sent a 4718-token prompt (~4.6 min prefill) and the client cancelled before the model could answer. A lean runtime profile cuts that to 706 tokens, lifts prefix-cache match 0.435 -> 0.966, and completes a full agentic turn with a real tool call in 11s warm. The ZeroClaw text parser was never at fault. Prompt cost model for budgeting profiles: ~706 base (1 tool), ~244/additional tool, +315 for uno_q_flash (schema + flash imperative), ~53/skill in compact mode. Also standardises context on -c 8192 across all three provisioning paths (a 16k window costs ~16 min to fill at this speed and doubles KV for nothing), and fixes stale references to the deleted src/lib/harness.ts. Adds bench-prefill.sh and bench-tools.py as reproducible baselines. Co-Authored-By: Claude Opus 4.8 <[email protected]>
146 lines
6.2 KiB
TOML
146 lines
6.2 KiB
TOML
schema_version = 3
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Providers
|
|
# ---------------------------------------------------------------------------
|
|
# "cloud" primary. During bring-up this points at the local claude_shim
|
|
# (adb-reverse tunnel on :8090). For a real fleet, swap in a shared cloud
|
|
# endpoint + key, e.g.:
|
|
# uri = "https://api.anthropic.com/v1" (or an OpenRouter/LiteLLM gateway)
|
|
# api_key = "..." (prefer an env-injected key)
|
|
[providers.models.custom.cloud]
|
|
uri = "__CLOUD_URI__"
|
|
model = "__CLOUD_MODEL__"
|
|
native_tools = false
|
|
|
|
# Same cloud endpoint, but with an on-board Qwen fallback ("cloud first,
|
|
# local if it fails"). Used by the `default` agent.
|
|
[providers.models.custom.claude]
|
|
uri = "__CLOUD_URI__"
|
|
model = "__CLOUD_MODEL__"
|
|
native_tools = false
|
|
fallback = ["llamacpp.local"]
|
|
|
|
# A deliberately-dead cloud endpoint (nothing listens on :9099) that fails over
|
|
# to the on-board Qwen. Used by the `chaos` agent to DEMONSTRATE resilience: a
|
|
# prompt routed here always finds the cloud unreachable and answers locally —
|
|
# the "simulate cloud outage" demo (ADD L4 Harness: reasoning + tiering).
|
|
[providers.models.custom.dead]
|
|
uri = "http://127.0.0.1:9099/v1"
|
|
model = "__CLOUD_MODEL__"
|
|
native_tools = false
|
|
fallback = ["llamacpp.local"]
|
|
|
|
[providers.models.llamacpp]
|
|
|
|
# On-board Qwen via llama-server (see zeroclaw-llama.service).
|
|
[providers.models.llamacpp.local]
|
|
uri = "http://127.0.0.1:8083/v1"
|
|
model = "qwen"
|
|
native_tools = false
|
|
|
|
[providers.models.custom]
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Hardware — the Uno Q's onboard MCU over the GPIO bridge.
|
|
# ---------------------------------------------------------------------------
|
|
[[peripherals.boards]]
|
|
board = "arduino-uno-q"
|
|
transport = "bridge"
|
|
|
|
[peripherals]
|
|
enabled = true
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Gateway — the HTTP/WS/SSE surface APESS talks to, AND the embedded web
|
|
# dashboard + chat UI a team opens directly at http://<board-lan-ip>:8080.
|
|
# ---------------------------------------------------------------------------
|
|
[gateway]
|
|
port = 8080
|
|
# LAN-open by default: participants reach the board's WiFi IP directly (the
|
|
# "Open your node →" link in APESS). 0.0.0.0 + allow_public_bind make the
|
|
# dashboard/web-chat reachable across the workshop subnet.
|
|
host = "0.0.0.0"
|
|
allow_public_bind = true
|
|
# Open during SETUP so a team's browser can chat + edit config (paste a Telegram
|
|
# token, etc.) with no token on the isolated workshop LAN. The reload-watcher
|
|
# (loopback) applies any dashboard config edit. Run `zeroclaw-lockdown.sh` to
|
|
# flip this on and mint a pair code once a team has finished setting up.
|
|
require_pairing = false
|
|
# paired_tokens are added by the pairing flow (lockdown / provision-uno-q.sh);
|
|
# never commit a real token.
|
|
|
|
[skills]
|
|
prompt_injection_mode = "compact"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Risk profile — only the on-board hardware tools, auto-approved so the agent
|
|
# can flash without a human in the loop.
|
|
# ---------------------------------------------------------------------------
|
|
# read_skill + file_read are allowed AND auto-approved so agents can load the
|
|
# bundled skills (e.g. arduino-uno-q) and read their references/*.md on demand
|
|
# without a human approver (the webhook path is non-interactive).
|
|
[risk_profiles.default]
|
|
level = "supervised"
|
|
allowed_tools = ["uno_q_flash", "sysfs_led", "camera", "network", "i2cdetect", "read_skill", "file_read", "content_search"]
|
|
auto_approve = ["uno_q_flash", "sysfs_led", "camera", "network", "i2cdetect", "read_skill", "file_read", "content_search"]
|
|
|
|
[runtime_profiles.unoq]
|
|
agentic = true
|
|
max_tool_iterations = 6
|
|
strict_tool_parsing = false
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Agents — one per provider strategy. APESS routes here via ?agent=; callers
|
|
# pass the alias explicitly (see sendPrompt in src/lib/api.ts).
|
|
# ---------------------------------------------------------------------------
|
|
[agents.default] # cloud + on-board Qwen fallback — the workshop default
|
|
enabled = true
|
|
model_provider = "custom.claude"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|
|
# Modalities this agent answers on, beyond the APESS proxy + web chat: the team's
|
|
# own Telegram bot. A team enables it from the dashboard (Config → channels →
|
|
# telegram) by pasting their @BotFather token; the reload-watcher applies it.
|
|
channels = ["telegram.default"]
|
|
|
|
[agents.cloud] # cloud only, no fallback
|
|
enabled = true
|
|
model_provider = "custom.cloud"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|
|
|
|
[agents.local] # on-board Qwen only (fully offline)
|
|
enabled = true
|
|
model_provider = "llamacpp.local"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Channels — extra ways a team talks to its node (all bind to `default`, above).
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Telegram: each board is its own agent, so each needs its own bot. A team
|
|
# creates one live via @BotFather, then pastes the token in the dashboard
|
|
# (Config → channels → telegram.default), sets enabled = true, and adds their
|
|
# Telegram username to allowed_users. The reload-watcher restarts the listener.
|
|
# Seeded disabled so the block shows up pre-bound in the config editor.
|
|
[channels.telegram.default]
|
|
enabled = false
|
|
bot_token = "" # from @BotFather (paste via the dashboard)
|
|
# allowed_users = ["your_tg_username"] # lock the bot to your team (no leading @)
|
|
mention_only = false
|
|
|
|
# Browser-mic streaming voice — talk to the agent inside the web chat, it speaks
|
|
# back. Requires a daemon built with the voice feature (see README §Voice):
|
|
# cargo xtask web build && cargo build --release \
|
|
# --features "hardware,peripheral-rpi,embedded-web,zeroclaw-gateway/gateway-voice-duplex"
|
|
# Enable once the board runs a voice-capable binary:
|
|
# [channels.voice_duplex.default]
|
|
# enabled = true
|
|
[agents.chaos] # simulated cloud outage → falls back to on-board Qwen
|
|
enabled = true
|
|
model_provider = "custom.dead"
|
|
risk_profile = "default"
|
|
runtime_profile = "unoq"
|