"""任务管理工具模块。 提供任务相关操作的封装,包括任务列表查询、创建、获取详情、更新状态等功能。 通过内部 HTTP API 与后端任务服务通信,使用 JWT 服务令牌进行认证。 """ import httpx import logging import os import jwt import time from config import settings logger = logging.getLogger(__name__) # 当前模块的日志记录器 _INTERNAL_BASE = os.getenv("INTERNAL_API_BASE", "http://127.0.0.1:8000/api") # 内部 API 基础地址 _client: httpx.Client | None = None # 全局复用的 HTTP 客户端实例 def _get_client() -> httpx.Client: """获取或创建全局复用的 HTTP 客户端实例。 Returns: httpx.Client: 配置了超时时间的 HTTP 客户端。 """ global _client if _client is None: _client = httpx.Client(timeout=30) return _client def _get_service_token() -> str | None: """生成用于内部服务间调用的 JWT 令牌。 Returns: str | None: 编码后的 JWT 令牌,生成失败返回 None。 """ try: payload = { "sub": "system_tool", # 令牌主体标识为系统工具 "exp": int(time.time()) + 3600, # 1 小时后过期 "type": "service", # 令牌类型为服务令牌 } token = jwt.encode(payload, settings.JWT_SECRET, algorithm="HS256") return token except Exception: return None def _headers(token: str | None = None) -> dict: """构建 HTTP 请求头,包含认证令牌。 Args: token: 可选的自定义令牌,不提供则自动生成服务令牌。 Returns: dict: 包含 Authorization 头的字典。 """ t = token or _get_service_token() return {"Authorization": f"Bearer {t}"} if t else {} # 工具函数描述 Schema,用于 AgentScope 工具注册 SCHEMAS = { "list_tasks": { "name": "list_tasks", "description": "查询任务列表,可选按状态筛选", "parameters": { "type": "object", "properties": { "status": {"type": "string", "description": "任务状态筛选", "enum": ["todo", "in_progress", "done"]} } } }, "create_task": { "name": "create_task", "description": "创建新任务", "parameters": { "type": "object", "properties": { "title": {"type": "string", "description": "任务标题"}, "description": {"type": "string", "description": "任务描述"}, "assignee_id": {"type": "string", "description": "负责人ID"}, "priority": {"type": "string", "description": "优先级", "enum": ["low", "medium", "high", "urgent"]}, "deadline": {"type": "string", "description": "截止日期"} }, "required": ["title"] } }, "get_task": { "name": "get_task", "description": "查询指定任务详情", "parameters": { "type": "object", "properties": {"task_id": {"type": "string", "description": "任务ID"}}, "required": ["task_id"] } }, "update_task": { "name": "update_task", "description": "更新任务状态或描述", "parameters": { "type": "object", "properties": { "task_id": {"type": "string", "description": "任务ID"}, "status": {"type": "string", "description": "新状态", "enum": ["todo", "in_progress", "done"]}, "description": {"type": "string", "description": "新描述"} }, "required": ["task_id"] } }, "push_task_to_wecom": { "name": "push_task_to_wecom", "description": "将任务推送到企业微信", "parameters": { "type": "object", "properties": {"task_id": {"type": "string", "description": "任务ID"}}, "required": ["task_id"] } }, } def list_tasks(status: str | None = None) -> str: """查询任务列表,支持按状态筛选。 Args: status: 可选的任务状态筛选条件(todo/in_progress/done)。 Returns: str: 格式化的任务列表文本或错误信息。 """ try: resp = _get_client().get(f"{_INTERNAL_BASE}/tasks", headers=_headers()) tasks = resp.json() if isinstance(resp.json(), list) else resp.json().get("data", []) if status: tasks = [t for t in tasks if t.get("status") == status] if not tasks: return "当前没有任务。" lines = [] for t in tasks: lines.append( f"- [{t.get('status', '?')}] {t.get('id', '')[:8]} | {t.get('title', '无标题')} " f"| 负责人: {t.get('assignee_name', t.get('assignee_id', '无人'))} " f"| 截止: {t.get('deadline', '无')} " f"| 优先级: {t.get('priority', '?')}" ) return "\n".join(lines) except Exception as e: return f"查询任务列表失败: {e}" def create_task(title: str, description: str = "", assignee_id: str = "", priority: str = "medium", deadline: str | None = None) -> str: """创建新任务。 Args: title: 任务标题(必填)。 description: 任务描述。 assignee_id: 负责人用户 ID。 priority: 任务优先级,默认 medium。 deadline: 截止日期。 Returns: str: 创建结果描述或错误信息。 """ try: body = {"title": title, "description": description, "assignee_id": assignee_id, "priority": priority, "deadline": deadline} resp = _get_client().post(f"{_INTERNAL_BASE}/tasks", json=body, headers=_headers()) task = resp.json() return f"任务创建成功: {task.get('title', title)} (ID: {task.get('id', '?')[:8]})" except Exception as e: return f"创建任务失败: {e}" def get_task(task_id: str) -> str: """查询指定任务的详细信息。 Args: task_id: 任务唯一标识 ID。 Returns: str: 格式化的任务详情文本或错误信息。 """ try: resp = _get_client().get(f"{_INTERNAL_BASE}/tasks/{task_id}", headers=_headers()) t = resp.json() return f"任务: {t.get('title', '?')}\n描述: {t.get('description', '无')}\n负责人: {t.get('assignee_name', t.get('assignee_id', '无人'))}\n状态: {t.get('status', '?')} | 优先级: {t.get('priority', '?')} | 截止: {t.get('deadline', '无')}" except Exception as e: return f"查询任务失败: {e}" def update_task(task_id: str, status: str | None = None, description: str | None = None) -> str: """更新任务状态或描述。 Args: task_id: 任务唯一标识 ID。 status: 新的任务状态(todo/in_progress/done)。 description: 新的任务描述。 Returns: str: 更新结果描述或错误信息。 """ try: body = {} if status: body["status"] = status if description: body["description"] = description resp = _get_client().put(f"{_INTERNAL_BASE}/tasks/{task_id}", json=body, headers=_headers()) return f"任务 {task_id[:8]} 已更新" except Exception as e: return f"更新任务失败: {e}" def push_task_to_wecom(task_id: str) -> str: """将任务通知推送到企业微信。 Args: task_id: 任务唯一标识 ID。 Returns: str: 推送结果描述或错误信息。 """ try: resp = _get_client().post(f"{_INTERNAL_BASE}/tasks/{task_id}/push", headers=_headers()) return f"任务 {task_id[:8]} 已推送至企业微信" except Exception as e: return f"推送任务失败: {e}" __all__ = ["list_tasks", "create_task", "get_task", "update_task", "push_task_to_wecom", "SCHEMAS"]