mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-11 09:55:59 +00:00
Reflect IM channel runtime health
This commit is contained in:
@@ -88,6 +88,12 @@ class FeishuChannel(Channel):
|
|||||||
def supports_streaming(self) -> bool:
|
def supports_streaming(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_running(self) -> bool:
|
||||||
|
if not self._running:
|
||||||
|
return False
|
||||||
|
return self._thread is not None and self._thread.is_alive()
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
if self._running:
|
if self._running:
|
||||||
return
|
return
|
||||||
@@ -193,6 +199,7 @@ class FeishuChannel(Channel):
|
|||||||
except Exception:
|
except Exception:
|
||||||
if self._running:
|
if self._running:
|
||||||
logger.exception("Feishu WebSocket error")
|
logger.exception("Feishu WebSocket error")
|
||||||
|
self._running = False
|
||||||
|
|
||||||
async def stop(self) -> None:
|
async def stop(self) -> None:
|
||||||
self._running = False
|
self._running = False
|
||||||
|
|||||||
@@ -214,6 +214,36 @@ def _runtime_unavailable_reason(provider: str) -> str:
|
|||||||
return f"Enter the required {display_name} credentials to connect this channel."
|
return f"Enter the required {display_name} credentials to connect this channel."
|
||||||
|
|
||||||
|
|
||||||
|
def _runtime_not_running_reason(provider: str) -> str:
|
||||||
|
meta = _PROVIDER_META.get(provider)
|
||||||
|
display_name = meta["display_name"] if meta else provider
|
||||||
|
return f"{display_name} channel is configured but is not running. Check the credentials and save this channel again."
|
||||||
|
|
||||||
|
|
||||||
|
def _runtime_channel_running(provider: str) -> bool | None:
|
||||||
|
try:
|
||||||
|
from app.channels.service import get_channel_service
|
||||||
|
except Exception:
|
||||||
|
logger.debug("Unable to inspect channel service status", exc_info=True)
|
||||||
|
return None
|
||||||
|
|
||||||
|
service = get_channel_service()
|
||||||
|
if service is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
status = service.get_status()
|
||||||
|
except Exception:
|
||||||
|
logger.debug("Unable to read channel service status", exc_info=True)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not status.get("service_running"):
|
||||||
|
return False
|
||||||
|
channel_status = status.get("channels", {}).get(provider)
|
||||||
|
if not isinstance(channel_status, dict):
|
||||||
|
return None
|
||||||
|
return bool(channel_status.get("running"))
|
||||||
|
|
||||||
|
|
||||||
def _provider_unavailable_reason(
|
def _provider_unavailable_reason(
|
||||||
config: ChannelConnectionsConfig,
|
config: ChannelConnectionsConfig,
|
||||||
channels_config: dict[str, Any],
|
channels_config: dict[str, Any],
|
||||||
@@ -226,6 +256,8 @@ def _provider_unavailable_reason(
|
|||||||
return _runtime_unavailable_reason(provider)
|
return _runtime_unavailable_reason(provider)
|
||||||
if not _runtime_channel_configured(provider, channels_config):
|
if not _runtime_channel_configured(provider, channels_config):
|
||||||
return _runtime_unavailable_reason(provider)
|
return _runtime_unavailable_reason(provider)
|
||||||
|
if _runtime_channel_running(provider) is False:
|
||||||
|
return _runtime_not_running_reason(provider)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import SimpleNamespace
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from _router_auth_helpers import make_authed_test_app
|
from _router_auth_helpers import make_authed_test_app
|
||||||
@@ -201,6 +202,37 @@ def test_get_providers_reports_unconfigured_when_runtime_channel_is_missing(tmp_
|
|||||||
anyio.run(repo.close)
|
anyio.run(repo.close)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_providers_reports_configured_channel_not_running(tmp_path, monkeypatch):
|
||||||
|
import anyio
|
||||||
|
|
||||||
|
repo = anyio.run(_make_repo, tmp_path)
|
||||||
|
app = _make_app(_enabled_connections_config(), repo, _channels_config())
|
||||||
|
service = SimpleNamespace(
|
||||||
|
get_status=lambda: {
|
||||||
|
"service_running": True,
|
||||||
|
"channels": {
|
||||||
|
"feishu": {
|
||||||
|
"enabled": True,
|
||||||
|
"running": False,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
monkeypatch.setattr("app.channels.service.get_channel_service", lambda: service)
|
||||||
|
|
||||||
|
with TestClient(app) as client:
|
||||||
|
response = client.get("/api/channels/providers")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
by_provider = {item["provider"]: item for item in response.json()["providers"]}
|
||||||
|
assert by_provider["feishu"]["configured"] is True
|
||||||
|
assert by_provider["feishu"]["connectable"] is False
|
||||||
|
assert by_provider["feishu"]["connection_status"] == "not_connected"
|
||||||
|
assert "configured but is not running" in by_provider["feishu"]["unavailable_reason"]
|
||||||
|
|
||||||
|
anyio.run(repo.close)
|
||||||
|
|
||||||
|
|
||||||
def test_get_providers_uses_newest_connection_status_per_provider(tmp_path):
|
def test_get_providers_uses_newest_connection_status_per_provider(tmp_path):
|
||||||
import anyio
|
import anyio
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,16 @@ def test_feishu_on_message_plain_text():
|
|||||||
assert mock_make_inbound.call_args[1]["text"] == "Hello world"
|
assert mock_make_inbound.call_args[1]["text"] == "Hello world"
|
||||||
|
|
||||||
|
|
||||||
|
def test_feishu_is_not_running_when_ws_thread_exits():
|
||||||
|
bus = MessageBus()
|
||||||
|
channel = FeishuChannel(bus, {"app_id": "test", "app_secret": "test"})
|
||||||
|
channel._running = True
|
||||||
|
channel._thread = MagicMock()
|
||||||
|
channel._thread.is_alive.return_value = False
|
||||||
|
|
||||||
|
assert channel.is_running is False
|
||||||
|
|
||||||
|
|
||||||
def test_feishu_on_message_rich_text():
|
def test_feishu_on_message_rich_text():
|
||||||
bus = MessageBus()
|
bus = MessageBus()
|
||||||
config = {"app_id": "test", "app_secret": "test"}
|
config = {"app_id": "test", "app_secret": "test"}
|
||||||
|
|||||||
Reference in New Issue
Block a user