From c2dd8937ed4dc4bb3888d3b61ac9f139421c4fa3 Mon Sep 17 00:00:00 2001 From: DanielWalnut <45447813+hetaoBackend@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:37:41 +0800 Subject: [PATCH] Fix IM channel backend URLs in Docker (#1497) * Fix IM channel backend URLs in Docker * Address Copilot review comments --- README.md | 2 ++ backend/CLAUDE.md | 1 + backend/app/channels/service.py | 20 +++++++++++++++++--- backend/tests/test_channels.py | 27 +++++++++++++++++++++++++++ config.example.yaml | 7 +++++++ docker/docker-compose-dev.yaml | 2 ++ docker/docker-compose.yaml | 2 ++ 7 files changed, 58 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fa8712d..03740e4 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,8 @@ FEISHU_APP_SECRET=your_app_secret 3. Under **Events**, subscribe to `im.message.receive_v1` and select **Long Connection** mode. 4. Copy the App ID and App Secret. Set `FEISHU_APP_ID` and `FEISHU_APP_SECRET` in `.env` and enable the channel in `config.yaml`. +When DeerFlow runs in Docker Compose, IM channels execute inside the `gateway` container. In that case, do not point `channels.langgraph_url` or `channels.gateway_url` at `localhost`; use container service names such as `http://langgraph:2024` and `http://gateway:8001`, or set `DEER_FLOW_CHANNELS_LANGGRAPH_URL` and `DEER_FLOW_CHANNELS_GATEWAY_URL`. + **Commands** Once a channel is connected, you can interact with DeerFlow directly from the chat: diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md index c10a693..12fd149 100644 --- a/backend/CLAUDE.md +++ b/backend/CLAUDE.md @@ -318,6 +318,7 @@ Bridges external messaging platforms (Feishu, Slack, Telegram) to the DeerFlow a **Configuration** (`config.yaml` -> `channels`): - `langgraph_url` - LangGraph Server URL (default: `http://localhost:2024`) - `gateway_url` - Gateway API URL for auxiliary commands (default: `http://localhost:8001`) +- In Docker Compose, IM channels run inside the `gateway` container, so `localhost` points back to that container. Use `http://langgraph:2024` / `http://gateway:8001`, or set `DEER_FLOW_CHANNELS_LANGGRAPH_URL` / `DEER_FLOW_CHANNELS_GATEWAY_URL`. - Per-channel configs: `feishu` (app_id, app_secret), `slack` (bot_token, app_token), `telegram` (bot_token) ### Memory System (`packages/harness/deerflow/agents/memory/`) diff --git a/backend/app/channels/service.py b/backend/app/channels/service.py index 1a07c21..5c4b0d2 100644 --- a/backend/app/channels/service.py +++ b/backend/app/channels/service.py @@ -3,9 +3,10 @@ from __future__ import annotations import logging +import os from typing import Any -from app.channels.manager import ChannelManager +from app.channels.manager import DEFAULT_GATEWAY_URL, DEFAULT_LANGGRAPH_URL, ChannelManager from app.channels.message_bus import MessageBus from app.channels.store import ChannelStore @@ -18,6 +19,19 @@ _CHANNEL_REGISTRY: dict[str, str] = { "telegram": "app.channels.telegram:TelegramChannel", } +_CHANNELS_LANGGRAPH_URL_ENV = "DEER_FLOW_CHANNELS_LANGGRAPH_URL" +_CHANNELS_GATEWAY_URL_ENV = "DEER_FLOW_CHANNELS_GATEWAY_URL" + + +def _resolve_service_url(config: dict[str, Any], config_key: str, env_key: str, default: str) -> str: + value = config.pop(config_key, None) + if isinstance(value, str) and value.strip(): + return value + env_value = os.getenv(env_key, "").strip() + if env_value: + return env_value + return default + class ChannelService: """Manages the lifecycle of all configured IM channels. @@ -30,8 +44,8 @@ class ChannelService: self.bus = MessageBus() self.store = ChannelStore() config = dict(channels_config or {}) - langgraph_url = config.pop("langgraph_url", None) or "http://localhost:2024" - gateway_url = config.pop("gateway_url", None) or "http://localhost:8001" + langgraph_url = _resolve_service_url(config, "langgraph_url", _CHANNELS_LANGGRAPH_URL_ENV, DEFAULT_LANGGRAPH_URL) + gateway_url = _resolve_service_url(config, "gateway_url", _CHANNELS_GATEWAY_URL_ENV, DEFAULT_GATEWAY_URL) default_session = config.pop("session", None) channel_sessions = {name: channel_config.get("session") for name, channel_config in config.items() if isinstance(channel_config, dict)} self.manager = ChannelManager( diff --git a/backend/tests/test_channels.py b/backend/tests/test_channels.py index 04131fa..faa7d36 100644 --- a/backend/tests/test_channels.py +++ b/backend/tests/test_channels.py @@ -1681,6 +1681,33 @@ class TestChannelService: assert service.manager._channel_sessions["telegram"]["assistant_id"] == "mobile_agent" assert service.manager._channel_sessions["telegram"]["users"]["vip"]["assistant_id"] == "vip_agent" + def test_service_urls_fall_back_to_env(self, monkeypatch): + from app.channels.service import ChannelService + + monkeypatch.setenv("DEER_FLOW_CHANNELS_LANGGRAPH_URL", "http://langgraph:2024") + monkeypatch.setenv("DEER_FLOW_CHANNELS_GATEWAY_URL", "http://gateway:8001") + + service = ChannelService(channels_config={}) + + assert service.manager._langgraph_url == "http://langgraph:2024" + assert service.manager._gateway_url == "http://gateway:8001" + + def test_config_service_urls_override_env(self, monkeypatch): + from app.channels.service import ChannelService + + monkeypatch.setenv("DEER_FLOW_CHANNELS_LANGGRAPH_URL", "http://langgraph:2024") + monkeypatch.setenv("DEER_FLOW_CHANNELS_GATEWAY_URL", "http://gateway:8001") + + service = ChannelService( + channels_config={ + "langgraph_url": "http://custom-langgraph:2024", + "gateway_url": "http://custom-gateway:8001", + } + ) + + assert service.manager._langgraph_url == "http://custom-langgraph:2024" + assert service.manager._gateway_url == "http://custom-gateway:8001" + # --------------------------------------------------------------------------- # Slack send retry tests diff --git a/config.example.yaml b/config.example.yaml index 6a0a89c..b188ecd 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -557,6 +557,13 @@ checkpointer: # langgraph_url: http://localhost:2024 # # Gateway API URL for auxiliary queries like /models, /memory (default: http://localhost:8001) # gateway_url: http://localhost:8001 +# # +# # Docker Compose note: +# # If channels run inside the gateway container, use container DNS names instead +# # of localhost, for example: +# # langgraph_url: http://langgraph:2024 +# # gateway_url: http://gateway:8001 +# # You can also set DEER_FLOW_CHANNELS_LANGGRAPH_URL / DEER_FLOW_CHANNELS_GATEWAY_URL. # # # Optional: default mobile/session settings for all IM channels # session: diff --git a/docker/docker-compose-dev.yaml b/docker/docker-compose-dev.yaml index b63fdd0..81a46bd 100644 --- a/docker/docker-compose-dev.yaml +++ b/docker/docker-compose-dev.yaml @@ -146,6 +146,8 @@ services: working_dir: /app environment: - CI=true + - DEER_FLOW_CHANNELS_LANGGRAPH_URL=${DEER_FLOW_CHANNELS_LANGGRAPH_URL:-http://langgraph:2024} + - DEER_FLOW_CHANNELS_GATEWAY_URL=${DEER_FLOW_CHANNELS_GATEWAY_URL:-http://gateway:8001} - DEER_FLOW_HOST_BASE_DIR=${DEER_FLOW_ROOT}/backend/.deer-flow - DEER_FLOW_HOST_SKILLS_PATH=${DEER_FLOW_ROOT}/skills - DEER_FLOW_SANDBOX_HOST=host.docker.internal diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 4fe0329..d929991 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -90,6 +90,8 @@ services: environment: - CI=true - DEER_FLOW_HOME=/app/backend/.deer-flow + - DEER_FLOW_CHANNELS_LANGGRAPH_URL=${DEER_FLOW_CHANNELS_LANGGRAPH_URL:-http://langgraph:2024} + - DEER_FLOW_CHANNELS_GATEWAY_URL=${DEER_FLOW_CHANNELS_GATEWAY_URL:-http://gateway:8001} # DooD path/network translation - DEER_FLOW_HOST_BASE_DIR=${DEER_FLOW_HOME} - DEER_FLOW_HOST_SKILLS_PATH=${DEER_FLOW_REPO_ROOT}/skills