diff --git a/nanobot/channels/discord.py b/nanobot/channels/discord.py index 464462756..aaa8584ff 100644 --- a/nanobot/channels/discord.py +++ b/nanobot/channels/discord.py @@ -207,6 +207,16 @@ if DISCORD_AVAILABLE: ) -> None: await self._forward_slash_command(interaction, _command_text) + @self.tree.command(name="model", description="Show or switch runtime model preset") + @app_commands.describe(preset="Optional model preset name, such as default") + async def model_command( + interaction: discord.Interaction, + preset: str | None = None, + ) -> None: + preset = (preset or "").strip() + command_text = f"/model {preset}" if preset else "/model" + await self._forward_slash_command(interaction, command_text) + @self.tree.command(name="help", description="Show available commands") async def help_command(interaction: discord.Interaction) -> None: sender_id = str(interaction.user.id) diff --git a/tests/channels/test_discord_channel.py b/tests/channels/test_discord_channel.py index d5882ca99..9bd1ab30e 100644 --- a/tests/channels/test_discord_channel.py +++ b/tests/channels/test_discord_channel.py @@ -865,7 +865,7 @@ async def test_slash_new_is_blocked_for_disallowed_user() -> None: assert handled == [] -@pytest.mark.parametrize("slash_name", ["stop", "restart", "status", "history"]) +@pytest.mark.parametrize("slash_name", ["stop", "restart", "status", "history", "model"]) @pytest.mark.asyncio async def test_slash_commands_forward_via_handle_message(slash_name: str) -> None: channel = DiscordChannel(DiscordConfig(enabled=True, allow_from=["*"]), MessageBus()) @@ -891,6 +891,31 @@ async def test_slash_commands_forward_via_handle_message(slash_name: str) -> Non assert handled[0]["metadata"]["is_slash_command"] is True +@pytest.mark.asyncio +async def test_slash_model_forwards_optional_preset() -> None: + channel = DiscordChannel(DiscordConfig(enabled=True, allow_from=["*"]), MessageBus()) + handled: list[dict] = [] + + async def capture_handle(**kwargs) -> None: + handled.append(kwargs) + + channel._handle_message = capture_handle # type: ignore[method-assign] + client = DiscordBotClient(channel, intents=discord.Intents.none()) + interaction = _make_interaction() + interaction.command.qualified_name = "model" + + model_cmd = client.tree.get_command("model") + assert model_cmd is not None + await model_cmd.callback(interaction, preset="fast") + + assert interaction.response.messages == [ + {"content": "Processing /model fast...", "ephemeral": True} + ] + assert len(handled) == 1 + assert handled[0]["content"] == "/model fast" + assert handled[0]["metadata"]["is_slash_command"] is True + + @pytest.mark.asyncio async def test_slash_help_returns_ephemeral_help_text() -> None: channel = DiscordChannel(DiscordConfig(enabled=True, allow_from=["*"]), MessageBus())