You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

230 lines
7.8 KiB

"""任务管理工具模块。
提供任务相关操作的封装,包括任务列表查询、创建、获取详情、更新状态等功能。
通过内部 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"]