Coverage for aipyapp/aipy/agent_taskmgr.py: 0%
118 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:02 +0200
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:02 +0200
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
4import uuid
5import time
6import asyncio
7from typing import Dict, Any, Optional
8from concurrent.futures import ThreadPoolExecutor
9from datetime import datetime
11from loguru import logger
13from .taskmgr import TaskManager
14from .task import Task
16class AgentTask:
17 """Agent任务封装"""
19 def __init__(self, task_id: str, instruction: str, task: Task, display: Any):
20 self.task_id = task_id
21 self.instruction = instruction
22 self.task = task
23 self.display = display
24 self.status = 'pending'
25 self.created_at = datetime.now()
26 self.started_at = None
27 self.completed_at = None
28 self.error = None
30 def to_dict(self) -> Dict[str, Any]:
31 """转换为字典格式"""
32 return {
33 'task_id': self.task_id,
34 'instruction': self.instruction,
35 'status': self.status,
36 'created_at': self.created_at.isoformat(),
37 'started_at': self.started_at.isoformat() if self.started_at else None,
38 'completed_at': self.completed_at.isoformat() if self.completed_at else None,
39 'error': self.error,
40 'captured_data': self.display.get_captured_data() if self.display else None
41 }
43class AgentTaskManager(TaskManager):
44 """Agent模式任务管理器"""
46 def __init__(self, settings, /, display_manager=None):
47 # 强制使用agent显示模式和headless设置
48 super().__init__(settings, display_manager=display_manager)
50 # Agent特有属性
51 self.agent_tasks: Dict[str, AgentTask] = {}
52 self.executor = ThreadPoolExecutor(max_workers=4) # 支持并发
53 self.log = logger.bind(src='agent_taskmgr')
55 async def submit_task(self, instruction: str, metadata: Dict[str, Any] = None) -> str:
56 """提交新任务"""
57 task_id = str(uuid.uuid4())
59 try:
60 # 创建任务
61 task = super().new_task()
63 # 获取Task内部的display对象(这个已经注册到事件系统)
64 display = task.display
66 # 确保display是DisplayAgent类型
67 if display and hasattr(display, 'captured_data'):
68 # 添加元数据
69 if metadata:
70 display.captured_data['metadata'].update(metadata)
71 # 清空之前可能的数据
72 display.clear_captured_data()
73 if metadata:
74 display.captured_data['metadata'].update(metadata)
76 # 创建Agent任务封装
77 agent_task = AgentTask(task_id, instruction, task, display)
78 agent_task.status = 'pending'
80 self.agent_tasks[task_id] = agent_task
81 self.log.info(f"Task submitted: {task_id}")
83 return task_id
85 except Exception as e:
86 self.log.error(f"Failed to submit task: {e}")
87 raise
89 async def execute_task(self, task_id: str) -> Dict[str, Any]:
90 """执行任务"""
91 if task_id not in self.agent_tasks:
92 raise ValueError(f"Task {task_id} not found")
94 agent_task = self.agent_tasks[task_id]
95 if agent_task.status != 'pending':
96 raise ValueError(f"Task {task_id} is not in pending status")
98 agent_task.status = 'running'
99 agent_task.started_at = datetime.now()
101 try:
102 # 在线程池中执行任务
103 loop = asyncio.get_event_loop()
104 await loop.run_in_executor(
105 self.executor,
106 self._run_task_sync,
107 agent_task
108 )
110 agent_task.status = 'completed'
111 agent_task.completed_at = datetime.now()
113 except Exception as e:
114 agent_task.status = 'error'
115 agent_task.error = str(e)
116 agent_task.completed_at = datetime.now()
117 self.log.error(f"Task {task_id} failed: {e}")
119 return agent_task.to_dict()
121 def _run_task_sync(self, agent_task: AgentTask):
122 """同步执行任务(在线程池中运行)"""
123 try:
124 # 执行任务
125 agent_task.task.run(agent_task.instruction)
127 # 确保任务完成
128 agent_task.task.done()
130 except Exception as e:
131 # 捕获异常并记录到display中
132 if hasattr(agent_task.display, 'on_exception'):
133 from ..interface import Event
134 event = Event('exception', msg=str(e), exception=e)
135 agent_task.display.on_exception(event)
136 raise
138 async def get_task_status(self, task_id: str) -> Dict[str, Any]:
139 """获取任务状态"""
140 if task_id not in self.agent_tasks:
141 raise ValueError(f"Task {task_id} not found")
143 return self.agent_tasks[task_id].to_dict()
145 async def get_task_result(self, task_id: str) -> Dict[str, Any]:
146 """获取任务结果"""
147 if task_id not in self.agent_tasks:
148 raise ValueError(f"Task {task_id} not found")
150 agent_task = self.agent_tasks[task_id]
151 result = agent_task.to_dict()
153 # 添加详细的执行结果
154 if agent_task.status == 'completed':
155 captured_data = agent_task.display.get_captured_data()
156 result['output'] = {
157 'messages': captured_data['messages'],
158 'results': captured_data['results'],
159 'errors': captured_data['errors'],
160 'metadata': captured_data['metadata']
161 }
163 return result
165 async def list_tasks(self) -> Dict[str, Any]:
166 """列出所有任务"""
167 tasks = {}
168 for task_id, agent_task in self.agent_tasks.items():
169 tasks[task_id] = {
170 'task_id': task_id,
171 'instruction': agent_task.instruction,
172 'status': agent_task.status,
173 'created_at': agent_task.created_at.isoformat(),
174 'started_at': agent_task.started_at.isoformat() if agent_task.started_at else None,
175 'completed_at': agent_task.completed_at.isoformat() if agent_task.completed_at else None,
176 }
177 return tasks
179 async def cancel_task(self, task_id: str) -> bool:
180 """取消任务"""
181 if task_id not in self.agent_tasks:
182 return False
184 agent_task = self.agent_tasks[task_id]
185 if agent_task.status == 'running':
186 # 尝试停止任务
187 if hasattr(agent_task.task, 'stop'):
188 agent_task.task.stop()
189 agent_task.status = 'cancelled'
190 agent_task.completed_at = datetime.now()
191 return True
193 return False
195 def cleanup_completed_tasks(self, max_age_hours: int = 24):
196 """清理完成的任务"""
197 current_time = datetime.now()
198 to_remove = []
200 for task_id, agent_task in self.agent_tasks.items():
201 if agent_task.status in ['completed', 'error', 'cancelled']:
202 if agent_task.completed_at:
203 age = (current_time - agent_task.completed_at).total_seconds() / 3600
204 if age > max_age_hours:
205 to_remove.append(task_id)
207 for task_id in to_remove:
208 del self.agent_tasks[task_id]
209 self.log.info(f"Cleaned up task: {task_id}")
211 return len(to_remove)