新增飞书机器人适配器全套功能,包括: - 基础适配器入口与工具导出 - 消息格式化、卡片渲染、回复调度逻辑 - 会话ID生成、模型覆盖策略 - 消息发送缓存、顺序队列管理 - 飞书签名验证、加解密webhook请求 - 审批权限校验、机器人菜单事件处理 - 文档评论、钉消息、语音转码处理 - 静态/动态目录管理、子代理生命周期管理 - 各类工具集:聊天、云盘、文档、知识库API封装
115 lines
3.8 KiB
Python
115 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class SubAgentLifecycle:
|
|
agent_id: str
|
|
parent_chat_id: str
|
|
status: str = "pending"
|
|
created_at: float = 0.0
|
|
completed_at: float | None = None
|
|
result: dict[str, Any] = field(default_factory=dict)
|
|
error: str = ""
|
|
|
|
|
|
class FeishuSubAgentManager:
|
|
def __init__(self, max_concurrent: int = 10):
|
|
self._sub_agents: dict[str, SubAgentLifecycle] = {}
|
|
self._max_concurrent = max_concurrent
|
|
|
|
def start_sub_agent(
|
|
self,
|
|
agent_id: str,
|
|
parent_chat_id: str,
|
|
*,
|
|
metadata: dict[str, Any] | None = None,
|
|
) -> SubAgentLifecycle:
|
|
if len(self._sub_agents) >= self._max_concurrent:
|
|
oldest = next(iter(self._sub_agents))
|
|
self._sub_agents.pop(oldest)
|
|
logger.info("[FeishuSubAgent] Evicted oldest sub-agent: %s", oldest)
|
|
|
|
import time
|
|
|
|
lifecycle = SubAgentLifecycle(
|
|
agent_id=agent_id,
|
|
parent_chat_id=parent_chat_id,
|
|
status="active",
|
|
created_at=time.monotonic(),
|
|
result=metadata or {},
|
|
)
|
|
self._sub_agents[agent_id] = lifecycle
|
|
logger.info("[FeishuSubAgent] Started sub-agent %s for chat %s", agent_id, parent_chat_id)
|
|
return lifecycle
|
|
|
|
def complete_sub_agent(self, agent_id: str, *, result: dict[str, Any] | None = None) -> SubAgentLifecycle | None:
|
|
lifecycle = self._sub_agents.get(agent_id)
|
|
if lifecycle is None:
|
|
logger.warning("[FeishuSubAgent] Sub-agent %s not found for completion", agent_id)
|
|
return None
|
|
|
|
import time
|
|
|
|
lifecycle.status = "completed"
|
|
lifecycle.completed_at = time.monotonic()
|
|
if result:
|
|
lifecycle.result.update(result)
|
|
logger.info("[FeishuSubAgent] Completed sub-agent %s", agent_id)
|
|
return lifecycle
|
|
|
|
def fail_sub_agent(self, agent_id: str, error: str) -> SubAgentLifecycle | None:
|
|
lifecycle = self._sub_agents.get(agent_id)
|
|
if lifecycle is None:
|
|
return None
|
|
lifecycle.status = "failed"
|
|
lifecycle.error = error
|
|
logger.warning("[FeishuSubAgent] Sub-agent %s failed: %s", agent_id, error)
|
|
return lifecycle
|
|
|
|
def end_sub_agent(self, agent_id: str, *, result: dict[str, Any] | None = None) -> SubAgentLifecycle | None:
|
|
lifecycle = self._sub_agents.pop(agent_id, None)
|
|
if lifecycle is None:
|
|
return None
|
|
|
|
import time
|
|
|
|
lifecycle.completed_at = time.monotonic()
|
|
lifecycle.status = "ended"
|
|
if result:
|
|
lifecycle.result.update(result)
|
|
logger.info("[FeishuSubAgent] Ended sub-agent %s", agent_id)
|
|
return lifecycle
|
|
|
|
def get_sub_agent(self, agent_id: str) -> SubAgentLifecycle | None:
|
|
return self._sub_agents.get(agent_id)
|
|
|
|
def list_active(self) -> list[SubAgentLifecycle]:
|
|
return [s for s in self._sub_agents.values() if s.status == "active"]
|
|
|
|
def list_by_parent(self, parent_chat_id: str) -> list[SubAgentLifecycle]:
|
|
return [s for s in self._sub_agents.values() if s.parent_chat_id == parent_chat_id]
|
|
|
|
def clean_completed(self, max_age_s: float = 3600) -> int:
|
|
import time
|
|
|
|
now = time.monotonic()
|
|
to_remove = [
|
|
aid
|
|
for aid, s in self._sub_agents.items()
|
|
if s.status in ("completed", "failed", "ended") and s.completed_at and (now - s.completed_at) > max_age_s
|
|
]
|
|
for aid in to_remove:
|
|
self._sub_agents.pop(aid, None)
|
|
if to_remove:
|
|
logger.info("[FeishuSubAgent] Cleaned %d completed sub-agents", len(to_remove))
|
|
return len(to_remove)
|
|
|
|
def clear(self) -> None:
|
|
self._sub_agents.clear()
|