From 77c2bfcc1e1c7ed6e699894c3de47e472e47fc03 Mon Sep 17 00:00:00 2001 From: zhayujie Date: Sun, 1 Feb 2026 19:40:27 +0800 Subject: [PATCH] fix: scheduler in feishu --- agent/tools/__init__.py | 46 +++++++++++++++++++++------- agent/tools/scheduler/integration.py | 23 ++++++++++++-- channel/feishu/feishu_channel.py | 9 ++++-- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/agent/tools/__init__.py b/agent/tools/__init__.py index fb9d828..b449c25 100644 --- a/agent/tools/__init__.py +++ b/agent/tools/__init__.py @@ -13,46 +13,70 @@ from agent.tools.ls.ls import Ls from agent.tools.memory.memory_search import MemorySearchTool from agent.tools.memory.memory_get import MemoryGetTool -# Import env config tool -from agent.tools.env_config.env_config import EnvConfig - # Import tools with optional dependencies def _import_optional_tools(): """Import tools that have optional dependencies""" + from common.log import logger tools = {} + # EnvConfig Tool (requires python-dotenv) + try: + from agent.tools.env_config.env_config import EnvConfig + tools['EnvConfig'] = EnvConfig + except ImportError as e: + logger.error( + f"[Tools] EnvConfig tool not loaded - missing dependency: {e}\n" + f" To enable environment variable management, run:\n" + f" pip install python-dotenv>=1.0.0" + ) + except Exception as e: + logger.error(f"[Tools] EnvConfig tool failed to load: {e}") + # Scheduler Tool (requires croniter) try: from agent.tools.scheduler.scheduler_tool import SchedulerTool tools['SchedulerTool'] = SchedulerTool - except ImportError: - pass + except ImportError as e: + logger.error( + f"[Tools] Scheduler tool not loaded - missing dependency: {e}\n" + f" To enable scheduled tasks, run:\n" + f" pip install croniter>=2.0.0" + ) + except Exception as e: + logger.error(f"[Tools] Scheduler tool failed to load: {e}") # Google Search (requires requests) try: from agent.tools.google_search.google_search import GoogleSearch tools['GoogleSearch'] = GoogleSearch - except ImportError: - pass + except ImportError as e: + logger.warning(f"[Tools] GoogleSearch tool not loaded - missing dependency: {e}") + except Exception as e: + logger.error(f"[Tools] GoogleSearch tool failed to load: {e}") # File Save (may have dependencies) try: from agent.tools.file_save.file_save import FileSave tools['FileSave'] = FileSave - except ImportError: - pass + except ImportError as e: + logger.warning(f"[Tools] FileSave tool not loaded - missing dependency: {e}") + except Exception as e: + logger.error(f"[Tools] FileSave tool failed to load: {e}") # Terminal (basic, should work) try: from agent.tools.terminal.terminal import Terminal tools['Terminal'] = Terminal - except ImportError: - pass + except ImportError as e: + logger.warning(f"[Tools] Terminal tool not loaded - missing dependency: {e}") + except Exception as e: + logger.error(f"[Tools] Terminal tool failed to load: {e}") return tools # Load optional tools _optional_tools = _import_optional_tools() +EnvConfig = _optional_tools.get('EnvConfig') SchedulerTool = _optional_tools.get('SchedulerTool') GoogleSearch = _optional_tools.get('GoogleSearch') FileSave = _optional_tools.get('FileSave') diff --git a/agent/tools/scheduler/integration.py b/agent/tools/scheduler/integration.py index 0f8ac35..1f345bf 100644 --- a/agent/tools/scheduler/integration.py +++ b/agent/tools/scheduler/integration.py @@ -101,12 +101,21 @@ def _execute_send_message(task: dict, agent_bridge): context["isgroup"] = is_group context["session_id"] = receiver - # For web channel, generate a virtual request_id + # Channel-specific context setup if channel_type == "web": + # Web channel needs request_id import uuid request_id = f"scheduler_{task['id']}_{uuid.uuid4().hex[:8]}" context["request_id"] = request_id logger.debug(f"[Scheduler] Generated request_id for web channel: {request_id}") + elif channel_type == "feishu": + # Feishu channel: for scheduled tasks, send as new message (no msg_id to reply to) + # Use chat_id for groups, open_id for private chats + context["receive_id_type"] = "chat_id" if is_group else "open_id" + # Keep isgroup as is, but set msg to None (no original message to reply to) + # Feishu channel will detect this and send as new message instead of reply + context["msg"] = None + logger.debug(f"[Scheduler] Feishu: receive_id_type={context['receive_id_type']}, is_group={is_group}, receiver={receiver}") # Create reply reply = Reply(ReplyType.TEXT, content) @@ -128,9 +137,13 @@ def _execute_send_message(task: dict, agent_bridge): logger.error(f"[Scheduler] Failed to create channel: {channel_type}") except Exception as e: logger.error(f"[Scheduler] Failed to send message: {e}") + import traceback + logger.error(f"[Scheduler] Traceback: {traceback.format_exc()}") except Exception as e: logger.error(f"[Scheduler] Error in _execute_send_message: {e}") + import traceback + logger.error(f"[Scheduler] Traceback: {traceback.format_exc()}") def _execute_tool_call(task: dict, agent_bridge): @@ -187,12 +200,18 @@ def _execute_tool_call(task: dict, agent_bridge): context["isgroup"] = is_group context["session_id"] = receiver - # For web channel, generate a virtual request_id + # Channel-specific context setup if channel_type == "web": + # Web channel needs request_id import uuid request_id = f"scheduler_{task['id']}_{uuid.uuid4().hex[:8]}" context["request_id"] = request_id logger.debug(f"[Scheduler] Generated request_id for web channel: {request_id}") + elif channel_type == "feishu": + # Feishu channel: for scheduled tasks, send as new message (no msg_id to reply to) + context["receive_id_type"] = "chat_id" if is_group else "open_id" + context["msg"] = None + logger.debug(f"[Scheduler] Feishu: receive_id_type={context['receive_id_type']}, is_group={is_group}, receiver={receiver}") reply = Reply(ReplyType.TEXT, content) diff --git a/channel/feishu/feishu_channel.py b/channel/feishu/feishu_channel.py index 16248de..e971302 100644 --- a/channel/feishu/feishu_channel.py +++ b/channel/feishu/feishu_channel.py @@ -208,8 +208,12 @@ class FeiShuChanel(ChatChannel): return msg_type = "image" content_key = "image_key" - if is_group: - # 群聊中直接回复 + + # Check if we can reply to an existing message (need msg_id) + can_reply = is_group and msg and hasattr(msg, 'msg_id') and msg.msg_id + + if can_reply: + # 群聊中回复已有消息 url = f"https://open.feishu.cn/open-apis/im/v1/messages/{msg.msg_id}/reply" data = { "msg_type": msg_type, @@ -217,6 +221,7 @@ class FeiShuChanel(ChatChannel): } res = requests.post(url=url, headers=headers, json=data, timeout=(5, 10)) else: + # 发送新消息(私聊或群聊中无msg_id的情况,如定时任务) url = "https://open.feishu.cn/open-apis/im/v1/messages" params = {"receive_id_type": context.get("receive_id_type") or "open_id"} data = {