fix(channels): point the Discord missing-dependency hint at the discord extra (#6054)

* test(scripts): cover discord extra auto-detection

The discord mapping in scripts/detect_uv_extras.py worked but was neither
listed in the "currently maps" docstring nor covered by a test, unlike the
buzz mapping that grew up beside it.

* fix(channels): point the Discord missing-dependency hint at the discord extra

discord.py is the only IM transport that is not a core dependency — it ships in
the optional `discord` extra, which detect_uv_extras.py, backend/Dockerfile and
deploy.sh already expand. Its ImportError hint told users to run `uv add
discord.py` instead, which rewrites pyproject.toml/uv.lock in a tree every
restart syncs with `--locked`. Mirror the buzz hint wording and note the extra
in config.example.yaml next to the buzz block.

---------

Co-authored-by: huyengiang101086-spec <270684046+huyengiang101086-spec@users.noreply.github.com>
This commit is contained in:
huyengiang101086-spec
2026-10-01 10:51:37 +08:00
committed by GitHub
co-authored by huyengiang101086-spec
parent 5b0ef628e2
commit baae72d876
5 changed files with 50 additions and 1 deletions
+1 -1
View File
@@ -108,7 +108,7 @@ class DiscordChannel(Channel):
try:
import discord
except ImportError:
logger.error("discord.py is not installed. Install it with: uv add discord.py")
logger.error("discord.py is not installed. The Discord channel needs the 'discord' extra: run `cd backend && uv sync --extra discord`.")
return
if not self._bot_token:
+29
View File
@@ -285,6 +285,35 @@ def test_detect_from_config_ignores_commented_buzz_block(tmp_path):
assert detect.detect_from_config(cfg) == []
def test_detect_from_config_discord_via_channels_enabled(tmp_path):
cfg = tmp_path / "config.yaml"
cfg.write_text(
"channels:\n discord:\n enabled: true\n bot_token: $DISCORD_BOT_TOKEN\n",
)
assert detect.detect_from_config(cfg) == ["discord"]
def test_detect_from_config_discord_disabled_returns_no_extras(tmp_path):
cfg = tmp_path / "config.yaml"
cfg.write_text("channels:\n discord:\n enabled: false\n")
assert detect.detect_from_config(cfg) == []
def test_detect_from_config_ignores_commented_discord_block(tmp_path):
"""Mirrors the commented example block shipped in config.example.yaml."""
cfg = tmp_path / "config.yaml"
cfg.write_text(
"# channels:\n# discord:\n# enabled: false\n# bot_token: $DISCORD_BOT_TOKEN\ndatabase:\n backend: sqlite\n",
)
assert detect.detect_from_config(cfg) == []
def test_detect_from_config_combines_discord_with_postgres(tmp_path):
cfg = tmp_path / "config.yaml"
cfg.write_text("database:\n backend: postgres\nchannels:\n discord:\n enabled: true\n")
assert detect.detect_from_config(cfg) == ["discord", "postgres"]
def test_detect_from_config_memory_stream_bridge_returns_no_extras(tmp_path):
cfg = tmp_path / "config.yaml"
cfg.write_text("stream_bridge:\n type: memory\n queue_maxsize: 256\n")
+17
View File
@@ -6,6 +6,7 @@ import asyncio
import builtins
import gc
import json
import sys
import threading
import weakref
from types import SimpleNamespace
@@ -34,6 +35,22 @@ def test_discord_channel_init() -> None:
assert channel.name == "discord"
@pytest.mark.asyncio
async def test_start_without_discord_module_points_at_the_extra(caplog) -> None:
"""discord.py ships in the optional ``discord`` extra, so the missing-dependency
hint must send the operator to ``uv sync --extra discord`` — the same command
``scripts/detect_uv_extras.py`` and the Docker builds already use — rather than
``uv add``, which would rewrite pyproject.toml and diverge from ``uv sync --locked``."""
channel = DiscordChannel(bus=MessageBus(), config={"bot_token": "token"})
with caplog.at_level("ERROR", logger="app.channels.discord"), patch.dict(sys.modules, {"discord": None}):
await channel.start()
assert any("uv sync --extra discord" in record.message for record in caplog.records)
assert not any("uv add" in record.message for record in caplog.records)
assert channel._running is False
# ---------------------------------------------------------------------------
# thread-mapping persistence across restart (#2897)
# ---------------------------------------------------------------------------
+2
View File
@@ -2985,6 +2985,8 @@ run_ownership:
# allowed_users: [] # empty = allow all
# card_template_id: "" # Optional: AI Card template ID for streaming updates
#
# # Discord joins as a bot; requires the `discord` dependency extra
# # (uv sync --extra discord) for discord.py.
# discord:
# enabled: false
# bot_token: $DISCORD_BOT_TOKEN
+1
View File
@@ -13,6 +13,7 @@ Order of resolution:
- stream_bridge.type == redis -> redis
- tools[].name == browser_navigate -> browser
- sandbox.ownership.type == redis -> redis
- channels.discord.enabled == true -> discord
- channels.buzz.enabled == true -> buzz
- models[].use == langchain_ollama:* -> ollama
3. Runtime environment toggles that enable optional backends: