Route WebUI quick start through channel config

This commit is contained in:
chengyongru
2026-06-22 13:04:05 +08:00
committed by Xubin Ren
parent 0097488eed
commit f9f5a19910
3 changed files with 40 additions and 11 deletions
+2 -2
View File
@@ -190,14 +190,14 @@ For the first setup, choose `[Q] Quick Start (recommended)`. It asks for the mod
If you are following the OpenRouter example:
1. Choose `[Q] Quick Start (recommended)`.
2. Choose `No chat channel yet (recommended)` unless you already have a Telegram, Feishu/Lark, Slack, or Discord bot token ready.
2. Choose `No chat channel yet` unless you already want to configure WebUI, Telegram, Feishu/Lark, Slack, or Discord now.
3. Select OpenRouter.
4. Paste your OpenRouter API key.
5. Enter a model ID, for example `anthropic/claude-sonnet-4.5`.
6. Review the Quick Start summary.
7. Choose `[S] Save and Exit`.
Enable WebUI later from `[C] Chat Channel` -> `WebSocket` after the first local reply works.
If you choose `WebUI / local browser`, the wizard will open the WebSocket settings before it enables WebUI.
If OpenRouter says your account cannot use that model, use another OpenRouter model ID that your account can access.
+25 -5
View File
@@ -54,7 +54,8 @@ _BACK_PRESSED = object() # Sentinel value for back navigation
_MODEL_PRESET_CACHE: set[str] = set()
_QUICK_START_TARGETS = {
"No chat channel yet (recommended)": None,
"WebUI / local browser (recommended)": "websocket",
"No chat channel yet": None,
"Telegram": "telegram",
"Feishu / Lark": "feishu",
"Slack": "slack",
@@ -414,7 +415,7 @@ def _show_main_menu_header() -> None:
body = Table.grid(expand=True)
body.add_column(ratio=1)
body.add_row(f"{__logo__} [bold {_UI_TEXT}]nanobot[/] [{_UI_MUTED}]v{__version__}[/]")
body.add_row(f"[{_UI_ACCENT}]Quick Start configures one AI model first.[/]")
body.add_row(f"[{_UI_ACCENT}]Quick Start configures one AI model and one entry point.[/]")
body.add_row(
f"[{_UI_MUTED}]WebUI, chat channels, and advanced settings stay available when you need them.[/]"
)
@@ -1443,6 +1444,19 @@ def _configure_quick_start_channel(config: Config, channel_name: str | None) ->
current = getattr(config.channels, channel_name, None) or {}
model = config_cls.model_validate(current)
if channel_name == "websocket":
enable = _input_bool("Enable WebSocket channel for local WebUI", True)
if not enable:
console.print("[yellow]! WebSocket channel was not enabled[/yellow]")
return False
if hasattr(model, "enabled"):
setattr(model, "enabled", True)
updated = _configure_pydantic_model(model, "WebSocket")
if updated is None:
return False
setattr(config.channels, channel_name, updated.model_dump(by_alias=True, exclude_none=True))
return True
required_fields = _QUICK_START_CHANNEL_FIELDS.get(channel_name, ())
for field_name, prompt in required_fields:
value = _input_with_existing(prompt, getattr(model, field_name, ""), "str")
@@ -1472,9 +1486,15 @@ def _show_quick_start_summary(config: Config, channel_name: str | None) -> None:
("Provider", preset.provider if preset else "[not set]"),
("Model", preset.model if preset else "[not set]"),
("Entry point", "Not enabled yet" if channel_name is None else channel_name),
("Next", "Save, then run `nanobot agent -m \"Hello!\"`"),
("WebUI", "Use [C] Chat Channel -> WebSocket when you are ready"),
(
"Next",
"Save, then run `nanobot gateway`"
if channel_name
else "Save, then run `nanobot agent -m \"Hello!\"`",
),
]
if channel_name == "websocket":
rows.append(("WebUI", "Open http://127.0.0.1:8765 after the gateway starts"))
_print_summary_panel(rows, "Quick Start")
@@ -1489,7 +1509,7 @@ def _configure_quick_start(config: Config) -> None:
answer = _select_with_back(
"How do you want to use nanobot first?",
list(_QUICK_START_TARGETS) + ["<- Back"],
default="No chat channel yet (recommended)",
default="WebUI / local browser (recommended)",
)
if answer is _BACK_PRESSED or answer is None or answer == "<- Back":
return
+13 -4
View File
@@ -926,18 +926,25 @@ class TestMainMenuUpdate:
assert telegram["enabled"] is True
assert telegram["token"] == "123:abc"
def test_quick_start_recommended_path_does_not_enable_channel(self, monkeypatch):
"""The recommended Quick Start target should leave chat surfaces as explicit opt-ins."""
def test_quick_start_webui_opens_websocket_config(self, monkeypatch):
"""The recommended WebUI path should explicitly route through WebSocket config."""
config = Config()
selections = iter(["No chat channel yet (recommended)", "OpenRouter"])
selections = iter(["WebUI / local browser (recommended)", "OpenRouter"])
def fake_select_with_back(*_args, **_kwargs):
return next(selections)
def fake_configure(model, display_name):
assert display_name == "WebSocket"
assert model.enabled is True
return model
monkeypatch.setattr(onboard_wizard.console, "clear", lambda: None)
monkeypatch.setattr(onboard_wizard, "_show_section_header", lambda *a, **kw: None)
monkeypatch.setattr(onboard_wizard, "_select_with_back", fake_select_with_back)
monkeypatch.setattr(onboard_wizard, "_input_with_existing", lambda *a, **kw: "sk-or-test")
monkeypatch.setattr(onboard_wizard, "_input_bool", lambda *a, **kw: True)
monkeypatch.setattr(onboard_wizard, "_configure_pydantic_model", fake_configure)
monkeypatch.setattr(
onboard_wizard,
"_input_model_with_autocomplete",
@@ -948,7 +955,9 @@ class TestMainMenuUpdate:
onboard_wizard._configure_quick_start(config)
assert getattr(config.channels, "websocket", None) is None
websocket = getattr(config.channels, "websocket")
assert websocket["enabled"] is True
assert websocket["websocketRequiresToken"] is True
assert config.agents.defaults.model_preset == "primary"
def test_quick_start_channel_requires_token_before_enable(self, monkeypatch):