mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-06 17:38:35 +00:00
Keep quick start channels opt-in
This commit is contained in:
parent
fc7971b3b6
commit
0097488eed
@ -190,13 +190,15 @@ For the first setup, choose `[Q] Quick Start (recommended)`. It asks for the mod
|
|||||||
If you are following the OpenRouter example:
|
If you are following the OpenRouter example:
|
||||||
|
|
||||||
1. Choose `[Q] Quick Start (recommended)`.
|
1. Choose `[Q] Quick Start (recommended)`.
|
||||||
2. Choose `WebUI only (recommended)` unless you already have a Telegram, Feishu/Lark, Slack, or Discord bot token ready.
|
2. Choose `No chat channel yet (recommended)` unless you already have a Telegram, Feishu/Lark, Slack, or Discord bot token ready.
|
||||||
3. Select OpenRouter.
|
3. Select OpenRouter.
|
||||||
4. Paste your OpenRouter API key.
|
4. Paste your OpenRouter API key.
|
||||||
5. Enter a model ID, for example `anthropic/claude-sonnet-4.5`.
|
5. Enter a model ID, for example `anthropic/claude-sonnet-4.5`.
|
||||||
6. Review the Quick Start summary.
|
6. Review the Quick Start summary.
|
||||||
7. Choose `[S] Save and Exit`.
|
7. Choose `[S] Save and Exit`.
|
||||||
|
|
||||||
|
Enable WebUI later from `[C] Chat Channel` -> `WebSocket` after the first local reply works.
|
||||||
|
|
||||||
If OpenRouter says your account cannot use that model, use another OpenRouter model ID that your account can access.
|
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:
|
If you are using another provider, use the same wizard choices but substitute that provider's values:
|
||||||
|
|||||||
@ -54,7 +54,7 @@ _BACK_PRESSED = object() # Sentinel value for back navigation
|
|||||||
_MODEL_PRESET_CACHE: set[str] = set()
|
_MODEL_PRESET_CACHE: set[str] = set()
|
||||||
|
|
||||||
_QUICK_START_TARGETS = {
|
_QUICK_START_TARGETS = {
|
||||||
"WebUI only (recommended)": "websocket",
|
"No chat channel yet (recommended)": None,
|
||||||
"Telegram": "telegram",
|
"Telegram": "telegram",
|
||||||
"Feishu / Lark": "feishu",
|
"Feishu / Lark": "feishu",
|
||||||
"Slack": "slack",
|
"Slack": "slack",
|
||||||
@ -414,11 +414,9 @@ def _show_main_menu_header() -> None:
|
|||||||
body = Table.grid(expand=True)
|
body = Table.grid(expand=True)
|
||||||
body.add_column(ratio=1)
|
body.add_column(ratio=1)
|
||||||
body.add_row(f"{__logo__} [bold {_UI_TEXT}]nanobot[/] [{_UI_MUTED}]v{__version__}[/]")
|
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(
|
body.add_row(
|
||||||
f"[{_UI_ACCENT}]Quick Start configures a model and enables WebUI by default.[/]"
|
f"[{_UI_MUTED}]WebUI, chat channels, and advanced settings stay available when you need them.[/]"
|
||||||
)
|
|
||||||
body.add_row(
|
|
||||||
f"[{_UI_MUTED}]Chat channels and advanced settings stay available when you need them.[/]"
|
|
||||||
)
|
)
|
||||||
console.print(
|
console.print(
|
||||||
Panel(
|
Panel(
|
||||||
@ -1445,13 +1443,21 @@ def _configure_quick_start_channel(config: Config, channel_name: str | None) ->
|
|||||||
|
|
||||||
current = getattr(config.channels, channel_name, None) or {}
|
current = getattr(config.channels, channel_name, None) or {}
|
||||||
model = config_cls.model_validate(current)
|
model = config_cls.model_validate(current)
|
||||||
if channel_name == "websocket":
|
required_fields = _QUICK_START_CHANNEL_FIELDS.get(channel_name, ())
|
||||||
console.print("[dim]WebUI uses the built-in local WebSocket channel. No token is needed now.[/dim]")
|
for field_name, prompt in required_fields:
|
||||||
for field_name, prompt in _QUICK_START_CHANNEL_FIELDS.get(channel_name, ()):
|
|
||||||
value = _input_with_existing(prompt, getattr(model, field_name, ""), "str")
|
value = _input_with_existing(prompt, getattr(model, field_name, ""), "str")
|
||||||
if value is not None:
|
if value is not None:
|
||||||
setattr(model, field_name, value)
|
setattr(model, field_name, value)
|
||||||
|
|
||||||
|
missing = [
|
||||||
|
prompt
|
||||||
|
for field_name, prompt in required_fields
|
||||||
|
if not str(getattr(model, field_name, "") or "").strip()
|
||||||
|
]
|
||||||
|
if missing:
|
||||||
|
console.print(f"[yellow]! {missing[0]} is required; channel was not enabled[/yellow]")
|
||||||
|
return False
|
||||||
|
|
||||||
if hasattr(model, "enabled"):
|
if hasattr(model, "enabled"):
|
||||||
setattr(model, "enabled", True)
|
setattr(model, "enabled", True)
|
||||||
setattr(config.channels, channel_name, model.model_dump(by_alias=True, exclude_none=True))
|
setattr(config.channels, channel_name, model.model_dump(by_alias=True, exclude_none=True))
|
||||||
@ -1465,8 +1471,9 @@ def _show_quick_start_summary(config: Config, channel_name: str | None) -> None:
|
|||||||
rows = [
|
rows = [
|
||||||
("Provider", preset.provider if preset else "[not set]"),
|
("Provider", preset.provider if preset else "[not set]"),
|
||||||
("Model", preset.model if preset else "[not set]"),
|
("Model", preset.model if preset else "[not set]"),
|
||||||
("Entry point", "WebUI" if channel_name in {None, "websocket"} else channel_name),
|
("Entry point", "Not enabled yet" if channel_name is None else channel_name),
|
||||||
("Next", "Save, then run `nanobot gateway`"),
|
("Next", "Save, then run `nanobot agent -m \"Hello!\"`"),
|
||||||
|
("WebUI", "Use [C] Chat Channel -> WebSocket when you are ready"),
|
||||||
]
|
]
|
||||||
_print_summary_panel(rows, "Quick Start")
|
_print_summary_panel(rows, "Quick Start")
|
||||||
|
|
||||||
@ -1482,7 +1489,7 @@ def _configure_quick_start(config: Config) -> None:
|
|||||||
answer = _select_with_back(
|
answer = _select_with_back(
|
||||||
"How do you want to use nanobot first?",
|
"How do you want to use nanobot first?",
|
||||||
list(_QUICK_START_TARGETS) + ["<- Back"],
|
list(_QUICK_START_TARGETS) + ["<- Back"],
|
||||||
default="WebUI only (recommended)",
|
default="No chat channel yet (recommended)",
|
||||||
)
|
)
|
||||||
if answer is _BACK_PRESSED or answer is None or answer == "<- Back":
|
if answer is _BACK_PRESSED or answer is None or answer == "<- Back":
|
||||||
return
|
return
|
||||||
|
|||||||
@ -926,10 +926,10 @@ class TestMainMenuUpdate:
|
|||||||
assert telegram["enabled"] is True
|
assert telegram["enabled"] is True
|
||||||
assert telegram["token"] == "123:abc"
|
assert telegram["token"] == "123:abc"
|
||||||
|
|
||||||
def test_quick_start_webui_enables_websocket(self, monkeypatch):
|
def test_quick_start_recommended_path_does_not_enable_channel(self, monkeypatch):
|
||||||
"""The recommended Quick Start target should create a working WebUI channel."""
|
"""The recommended Quick Start target should leave chat surfaces as explicit opt-ins."""
|
||||||
config = Config()
|
config = Config()
|
||||||
selections = iter(["WebUI only (recommended)", "OpenRouter"])
|
selections = iter(["No chat channel yet (recommended)", "OpenRouter"])
|
||||||
|
|
||||||
def fake_select_with_back(*_args, **_kwargs):
|
def fake_select_with_back(*_args, **_kwargs):
|
||||||
return next(selections)
|
return next(selections)
|
||||||
@ -948,9 +948,18 @@ class TestMainMenuUpdate:
|
|||||||
|
|
||||||
onboard_wizard._configure_quick_start(config)
|
onboard_wizard._configure_quick_start(config)
|
||||||
|
|
||||||
websocket = getattr(config.channels, "websocket")
|
assert getattr(config.channels, "websocket", None) is None
|
||||||
assert websocket["enabled"] is True
|
assert config.agents.defaults.model_preset == "primary"
|
||||||
assert websocket["allowFrom"] == ["*"]
|
|
||||||
|
def test_quick_start_channel_requires_token_before_enable(self, monkeypatch):
|
||||||
|
"""Quick Start should not enable token-based channels with blank credentials."""
|
||||||
|
config = Config()
|
||||||
|
|
||||||
|
monkeypatch.setattr(onboard_wizard, "_show_quick_start_progress", lambda *_args: None)
|
||||||
|
monkeypatch.setattr(onboard_wizard, "_input_with_existing", lambda *a, **kw: "")
|
||||||
|
|
||||||
|
assert onboard_wizard._configure_quick_start_channel(config, "telegram") is False
|
||||||
|
assert getattr(config.channels, "telegram", None) is None
|
||||||
|
|
||||||
def test_main_menu_dispatch_includes_channel_common(self):
|
def test_main_menu_dispatch_includes_channel_common(self):
|
||||||
"""Main menu dispatch should route [H] to Channel Common."""
|
"""Main menu dispatch should route [H] to Channel Common."""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user