autoglm-gui 1.4.1__py3-none-any.whl → 1.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AutoGLM_GUI/__init__.py +11 -0
- AutoGLM_GUI/__main__.py +26 -4
- AutoGLM_GUI/actions/__init__.py +6 -0
- phone_agent/actions/handler_ios.py → AutoGLM_GUI/actions/handler.py +30 -112
- AutoGLM_GUI/actions/types.py +15 -0
- {phone_agent → AutoGLM_GUI}/adb/__init__.py +25 -23
- {phone_agent → AutoGLM_GUI}/adb/connection.py +5 -40
- {phone_agent → AutoGLM_GUI}/adb/device.py +12 -94
- {phone_agent → AutoGLM_GUI}/adb/input.py +6 -47
- AutoGLM_GUI/adb/screenshot.py +11 -0
- {phone_agent/config → AutoGLM_GUI/adb}/timing.py +1 -1
- AutoGLM_GUI/adb_plus/keyboard_installer.py +4 -2
- AutoGLM_GUI/adb_plus/screenshot.py +22 -1
- AutoGLM_GUI/adb_plus/serial.py +38 -20
- AutoGLM_GUI/adb_plus/touch.py +4 -9
- AutoGLM_GUI/agents/__init__.py +43 -12
- AutoGLM_GUI/agents/events.py +19 -0
- AutoGLM_GUI/agents/factory.py +31 -38
- AutoGLM_GUI/agents/glm/__init__.py +7 -0
- AutoGLM_GUI/agents/glm/agent.py +297 -0
- AutoGLM_GUI/agents/glm/message_builder.py +81 -0
- AutoGLM_GUI/agents/glm/parser.py +110 -0
- {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_en.py +7 -9
- {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_zh.py +18 -25
- AutoGLM_GUI/agents/mai/__init__.py +28 -0
- AutoGLM_GUI/agents/mai/agent.py +408 -0
- AutoGLM_GUI/agents/mai/parser.py +254 -0
- AutoGLM_GUI/agents/mai/prompts.py +103 -0
- AutoGLM_GUI/agents/mai/traj_memory.py +91 -0
- AutoGLM_GUI/agents/protocols.py +12 -8
- AutoGLM_GUI/agents/stream_runner.py +193 -0
- AutoGLM_GUI/api/__init__.py +40 -21
- AutoGLM_GUI/api/agents.py +181 -239
- AutoGLM_GUI/api/control.py +9 -6
- AutoGLM_GUI/api/devices.py +102 -12
- AutoGLM_GUI/api/history.py +104 -0
- AutoGLM_GUI/api/layered_agent.py +67 -15
- AutoGLM_GUI/api/media.py +64 -1
- AutoGLM_GUI/api/scheduled_tasks.py +98 -0
- AutoGLM_GUI/config.py +81 -0
- AutoGLM_GUI/config_manager.py +68 -51
- AutoGLM_GUI/device_manager.py +248 -29
- AutoGLM_GUI/device_protocol.py +1 -1
- AutoGLM_GUI/devices/adb_device.py +5 -10
- AutoGLM_GUI/devices/mock_device.py +4 -2
- AutoGLM_GUI/devices/remote_device.py +8 -3
- AutoGLM_GUI/history_manager.py +164 -0
- AutoGLM_GUI/model/__init__.py +5 -0
- AutoGLM_GUI/model/message_builder.py +69 -0
- AutoGLM_GUI/model/types.py +24 -0
- AutoGLM_GUI/models/__init__.py +10 -0
- AutoGLM_GUI/models/history.py +140 -0
- AutoGLM_GUI/models/scheduled_task.py +71 -0
- AutoGLM_GUI/parsers/__init__.py +22 -0
- AutoGLM_GUI/parsers/base.py +50 -0
- AutoGLM_GUI/parsers/phone_parser.py +58 -0
- AutoGLM_GUI/phone_agent_manager.py +62 -396
- AutoGLM_GUI/platform_utils.py +26 -0
- AutoGLM_GUI/prompt_config.py +15 -0
- AutoGLM_GUI/prompts/__init__.py +32 -0
- AutoGLM_GUI/scheduler_manager.py +350 -0
- AutoGLM_GUI/schemas.py +246 -72
- AutoGLM_GUI/scrcpy_stream.py +142 -24
- AutoGLM_GUI/socketio_server.py +100 -27
- AutoGLM_GUI/static/assets/{about-_XNhzQZX.js → about-CfwX1Cmc.js} +1 -1
- AutoGLM_GUI/static/assets/alert-dialog-CtGlN2IJ.js +1 -0
- AutoGLM_GUI/static/assets/chat-BYa-foUI.js +129 -0
- AutoGLM_GUI/static/assets/circle-alert-t08bEMPO.js +1 -0
- AutoGLM_GUI/static/assets/dialog-FNwZJFwk.js +45 -0
- AutoGLM_GUI/static/assets/eye-D0UPWCWC.js +1 -0
- AutoGLM_GUI/static/assets/history-CRo95B7i.js +1 -0
- AutoGLM_GUI/static/assets/{index-Cy8TmmHV.js → index-BaLMSqd3.js} +1 -1
- AutoGLM_GUI/static/assets/index-CTHbFvKl.js +11 -0
- AutoGLM_GUI/static/assets/index-CV7jGxGm.css +1 -0
- AutoGLM_GUI/static/assets/label-DJFevVmr.js +1 -0
- AutoGLM_GUI/static/assets/logs-RW09DyYY.js +1 -0
- AutoGLM_GUI/static/assets/popover--JTJrE5v.js +1 -0
- AutoGLM_GUI/static/assets/scheduled-tasks-DTRKsQXF.js +1 -0
- AutoGLM_GUI/static/assets/square-pen-CPK_K680.js +1 -0
- AutoGLM_GUI/static/assets/textarea-PRmVnWq5.js +1 -0
- AutoGLM_GUI/static/assets/workflows-CdcsAoaT.js +1 -0
- AutoGLM_GUI/static/index.html +2 -2
- AutoGLM_GUI/types.py +17 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/METADATA +179 -130
- autoglm_gui-1.5.1.dist-info/RECORD +118 -0
- AutoGLM_GUI/agents/mai_adapter.py +0 -627
- AutoGLM_GUI/api/dual_model.py +0 -317
- AutoGLM_GUI/device_adapter.py +0 -263
- AutoGLM_GUI/dual_model/__init__.py +0 -53
- AutoGLM_GUI/dual_model/decision_model.py +0 -664
- AutoGLM_GUI/dual_model/dual_agent.py +0 -917
- AutoGLM_GUI/dual_model/protocols.py +0 -354
- AutoGLM_GUI/dual_model/vision_model.py +0 -442
- AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +0 -291
- AutoGLM_GUI/phone_agent_patches.py +0 -147
- AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +0 -126
- AutoGLM_GUI/static/assets/dialog-B3uW4T8V.js +0 -45
- AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +0 -1
- AutoGLM_GUI/static/assets/index-UYYauTly.js +0 -12
- AutoGLM_GUI/static/assets/workflows-Du_de-dt.js +0 -1
- autoglm_gui-1.4.1.dist-info/RECORD +0 -117
- mai_agent/base.py +0 -137
- mai_agent/mai_grounding_agent.py +0 -263
- mai_agent/mai_naivigation_agent.py +0 -526
- mai_agent/prompt.py +0 -148
- mai_agent/unified_memory.py +0 -67
- mai_agent/utils.py +0 -73
- phone_agent/__init__.py +0 -12
- phone_agent/actions/__init__.py +0 -5
- phone_agent/actions/handler.py +0 -400
- phone_agent/adb/screenshot.py +0 -108
- phone_agent/agent.py +0 -253
- phone_agent/agent_ios.py +0 -277
- phone_agent/config/__init__.py +0 -53
- phone_agent/config/apps_harmonyos.py +0 -256
- phone_agent/config/apps_ios.py +0 -339
- phone_agent/config/prompts.py +0 -80
- phone_agent/device_factory.py +0 -166
- phone_agent/hdc/__init__.py +0 -53
- phone_agent/hdc/connection.py +0 -384
- phone_agent/hdc/device.py +0 -269
- phone_agent/hdc/input.py +0 -145
- phone_agent/hdc/screenshot.py +0 -127
- phone_agent/model/__init__.py +0 -5
- phone_agent/model/client.py +0 -290
- phone_agent/xctest/__init__.py +0 -47
- phone_agent/xctest/connection.py +0 -379
- phone_agent/xctest/device.py +0 -472
- phone_agent/xctest/input.py +0 -311
- phone_agent/xctest/screenshot.py +0 -226
- {phone_agent/config → AutoGLM_GUI/adb}/apps.py +0 -0
- {phone_agent/config → AutoGLM_GUI}/i18n.py +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/WHEEL +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Prompt templates for agents."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
# Import MAI prompt from new location
|
|
6
|
+
from AutoGLM_GUI.agents.mai.prompts import MAI_MOBILE_SYSTEM_PROMPT
|
|
7
|
+
|
|
8
|
+
# Import from parent-level prompts.py file
|
|
9
|
+
# When prompts/ directory exists, Python prioritizes it over prompts.py
|
|
10
|
+
# We need to import from sibling prompts.py file
|
|
11
|
+
parent_dir = Path(__file__).parent.parent
|
|
12
|
+
prompts_file = parent_dir / "prompts.py"
|
|
13
|
+
|
|
14
|
+
if prompts_file.exists():
|
|
15
|
+
import importlib.util
|
|
16
|
+
|
|
17
|
+
spec = importlib.util.spec_from_file_location("_prompts_legacy", prompts_file)
|
|
18
|
+
if spec and spec.loader:
|
|
19
|
+
_prompts_legacy = importlib.util.module_from_spec(spec)
|
|
20
|
+
spec.loader.exec_module(_prompts_legacy)
|
|
21
|
+
MCP_SYSTEM_PROMPT_ZH = getattr(_prompts_legacy, "MCP_SYSTEM_PROMPT_ZH", "")
|
|
22
|
+
MCP_SYSTEM_PROMPT_EN = getattr(_prompts_legacy, "MCP_SYSTEM_PROMPT_EN", "")
|
|
23
|
+
else:
|
|
24
|
+
# Fallback if file doesn't exist
|
|
25
|
+
MCP_SYSTEM_PROMPT_ZH = ""
|
|
26
|
+
MCP_SYSTEM_PROMPT_EN = ""
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"MAI_MOBILE_SYSTEM_PROMPT",
|
|
30
|
+
"MCP_SYSTEM_PROMPT_ZH",
|
|
31
|
+
"MCP_SYSTEM_PROMPT_EN",
|
|
32
|
+
]
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"""Scheduled task manager with APScheduler."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from apscheduler.schedulers.background import BackgroundScheduler
|
|
9
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
10
|
+
|
|
11
|
+
from AutoGLM_GUI.logger import logger
|
|
12
|
+
from AutoGLM_GUI.models.scheduled_task import ScheduledTask
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SchedulerManager:
|
|
16
|
+
_instance: Optional["SchedulerManager"] = None
|
|
17
|
+
|
|
18
|
+
def __new__(cls):
|
|
19
|
+
if cls._instance is None:
|
|
20
|
+
cls._instance = super().__new__(cls)
|
|
21
|
+
return cls._instance
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
if hasattr(self, "_initialized"):
|
|
25
|
+
return
|
|
26
|
+
self._initialized = True
|
|
27
|
+
self._tasks_path = Path.home() / ".config" / "autoglm" / "scheduled_tasks.json"
|
|
28
|
+
self._scheduler = BackgroundScheduler()
|
|
29
|
+
self._tasks: dict[str, ScheduledTask] = {}
|
|
30
|
+
self._file_mtime: Optional[float] = None
|
|
31
|
+
|
|
32
|
+
def start(self) -> None:
|
|
33
|
+
self._load_tasks()
|
|
34
|
+
for task in self._tasks.values():
|
|
35
|
+
if task.enabled:
|
|
36
|
+
self._add_job(task)
|
|
37
|
+
self._scheduler.start()
|
|
38
|
+
logger.info(f"SchedulerManager started with {len(self._tasks)} task(s)")
|
|
39
|
+
|
|
40
|
+
def shutdown(self) -> None:
|
|
41
|
+
self._scheduler.shutdown(wait=False)
|
|
42
|
+
logger.info("SchedulerManager shutdown")
|
|
43
|
+
|
|
44
|
+
def create_task(
|
|
45
|
+
self,
|
|
46
|
+
name: str,
|
|
47
|
+
workflow_uuid: str,
|
|
48
|
+
device_serialno: str,
|
|
49
|
+
cron_expression: str,
|
|
50
|
+
enabled: bool = True,
|
|
51
|
+
) -> ScheduledTask:
|
|
52
|
+
task = ScheduledTask(
|
|
53
|
+
name=name,
|
|
54
|
+
workflow_uuid=workflow_uuid,
|
|
55
|
+
device_serialno=device_serialno,
|
|
56
|
+
cron_expression=cron_expression,
|
|
57
|
+
enabled=enabled,
|
|
58
|
+
)
|
|
59
|
+
self._tasks[task.id] = task
|
|
60
|
+
self._save_tasks()
|
|
61
|
+
|
|
62
|
+
if enabled:
|
|
63
|
+
self._add_job(task)
|
|
64
|
+
|
|
65
|
+
logger.info(f"Created scheduled task: {name} (id={task.id})")
|
|
66
|
+
return task
|
|
67
|
+
|
|
68
|
+
def update_task(self, task_id: str, **kwargs) -> Optional[ScheduledTask]:
|
|
69
|
+
task = self._tasks.get(task_id)
|
|
70
|
+
if not task:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
old_enabled = task.enabled
|
|
74
|
+
old_cron = task.cron_expression
|
|
75
|
+
|
|
76
|
+
for key, value in kwargs.items():
|
|
77
|
+
if value is not None and hasattr(task, key):
|
|
78
|
+
setattr(task, key, value)
|
|
79
|
+
|
|
80
|
+
task.updated_at = datetime.now()
|
|
81
|
+
self._save_tasks()
|
|
82
|
+
|
|
83
|
+
if old_enabled and not task.enabled:
|
|
84
|
+
self._remove_job(task_id)
|
|
85
|
+
elif not old_enabled and task.enabled:
|
|
86
|
+
self._add_job(task)
|
|
87
|
+
elif task.enabled and old_cron != task.cron_expression:
|
|
88
|
+
self._remove_job(task_id)
|
|
89
|
+
self._add_job(task)
|
|
90
|
+
|
|
91
|
+
logger.info(f"Updated scheduled task: {task.name} (id={task_id})")
|
|
92
|
+
return task
|
|
93
|
+
|
|
94
|
+
def delete_task(self, task_id: str) -> bool:
|
|
95
|
+
task = self._tasks.pop(task_id, None)
|
|
96
|
+
if not task:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
self._remove_job(task_id)
|
|
100
|
+
self._save_tasks()
|
|
101
|
+
logger.info(f"Deleted scheduled task: {task.name} (id={task_id})")
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
def list_tasks(self) -> list[ScheduledTask]:
|
|
105
|
+
return list(self._tasks.values())
|
|
106
|
+
|
|
107
|
+
def get_task(self, task_id: str) -> Optional[ScheduledTask]:
|
|
108
|
+
return self._tasks.get(task_id)
|
|
109
|
+
|
|
110
|
+
def set_enabled(self, task_id: str, enabled: bool) -> bool:
|
|
111
|
+
task = self._tasks.get(task_id)
|
|
112
|
+
if not task:
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
if task.enabled == enabled:
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
task.enabled = enabled
|
|
119
|
+
task.updated_at = datetime.now()
|
|
120
|
+
self._save_tasks()
|
|
121
|
+
|
|
122
|
+
if enabled:
|
|
123
|
+
self._add_job(task)
|
|
124
|
+
else:
|
|
125
|
+
self._remove_job(task_id)
|
|
126
|
+
|
|
127
|
+
logger.info(f"{'Enabled' if enabled else 'Disabled'} task: {task.name}")
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
def get_next_run_time(self, task_id: str) -> Optional[datetime]:
|
|
131
|
+
job = self._scheduler.get_job(task_id)
|
|
132
|
+
if job and job.next_run_time:
|
|
133
|
+
return job.next_run_time.replace(tzinfo=None)
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
def _add_job(self, task: ScheduledTask) -> None:
|
|
137
|
+
try:
|
|
138
|
+
parts = task.cron_expression.split()
|
|
139
|
+
if len(parts) != 5:
|
|
140
|
+
logger.error(f"Invalid cron expression: {task.cron_expression}")
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
trigger = CronTrigger(
|
|
144
|
+
minute=parts[0],
|
|
145
|
+
hour=parts[1],
|
|
146
|
+
day=parts[2],
|
|
147
|
+
month=parts[3],
|
|
148
|
+
day_of_week=parts[4],
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
self._scheduler.add_job(
|
|
152
|
+
self._execute_task,
|
|
153
|
+
trigger=trigger,
|
|
154
|
+
id=task.id,
|
|
155
|
+
args=[task.id],
|
|
156
|
+
replace_existing=True,
|
|
157
|
+
)
|
|
158
|
+
logger.debug(f"Added job for task: {task.name}")
|
|
159
|
+
except Exception as e:
|
|
160
|
+
logger.error(f"Failed to add job for task {task.name}: {e}")
|
|
161
|
+
|
|
162
|
+
def _remove_job(self, task_id: str) -> None:
|
|
163
|
+
try:
|
|
164
|
+
if self._scheduler.get_job(task_id):
|
|
165
|
+
self._scheduler.remove_job(task_id)
|
|
166
|
+
logger.debug(f"Removed job: {task_id}")
|
|
167
|
+
except Exception as e:
|
|
168
|
+
logger.warning(f"Failed to remove job {task_id}: {e}")
|
|
169
|
+
|
|
170
|
+
def _execute_task(self, task_id: str) -> None:
|
|
171
|
+
task = self._tasks.get(task_id)
|
|
172
|
+
if not task:
|
|
173
|
+
logger.warning(f"Task {task_id} not found for execution")
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
logger.info(f"Executing scheduled task: {task.name}")
|
|
177
|
+
|
|
178
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
179
|
+
from AutoGLM_GUI.history_manager import history_manager
|
|
180
|
+
from AutoGLM_GUI.models.history import ConversationRecord, MessageRecord
|
|
181
|
+
from AutoGLM_GUI.phone_agent_manager import PhoneAgentManager
|
|
182
|
+
from AutoGLM_GUI.workflow_manager import workflow_manager
|
|
183
|
+
|
|
184
|
+
workflow = workflow_manager.get_workflow(task.workflow_uuid)
|
|
185
|
+
if not workflow:
|
|
186
|
+
self._record_failure(task, "Workflow not found")
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
device_manager = DeviceManager.get_instance()
|
|
190
|
+
device = None
|
|
191
|
+
for d in device_manager.get_devices():
|
|
192
|
+
if d.serial == task.device_serialno and d.state.value == "online":
|
|
193
|
+
device = d
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
if not device:
|
|
197
|
+
self._record_failure(task, "Device offline")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
manager = PhoneAgentManager.get_instance()
|
|
201
|
+
acquired = manager.acquire_device(
|
|
202
|
+
device.primary_device_id,
|
|
203
|
+
timeout=0,
|
|
204
|
+
raise_on_timeout=False,
|
|
205
|
+
auto_initialize=True,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if not acquired:
|
|
209
|
+
self._record_failure(task, "Device busy")
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
start_time = datetime.now()
|
|
213
|
+
|
|
214
|
+
# 收集完整对话消息
|
|
215
|
+
messages: list[MessageRecord] = []
|
|
216
|
+
messages.append(
|
|
217
|
+
MessageRecord(
|
|
218
|
+
role="user",
|
|
219
|
+
content=workflow["text"],
|
|
220
|
+
timestamp=start_time,
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
try:
|
|
225
|
+
agent = manager.get_agent(device.primary_device_id)
|
|
226
|
+
agent.reset()
|
|
227
|
+
|
|
228
|
+
# 使用 step 循环执行,收集每步信息
|
|
229
|
+
is_first = True
|
|
230
|
+
result_message = ""
|
|
231
|
+
task_success = False
|
|
232
|
+
|
|
233
|
+
while agent.step_count < agent.agent_config.max_steps:
|
|
234
|
+
step_result = agent.step(workflow["text"] if is_first else None)
|
|
235
|
+
is_first = False
|
|
236
|
+
|
|
237
|
+
# 收集每个 step 的消息
|
|
238
|
+
messages.append(
|
|
239
|
+
MessageRecord(
|
|
240
|
+
role="assistant",
|
|
241
|
+
content="",
|
|
242
|
+
timestamp=datetime.now(),
|
|
243
|
+
thinking=step_result.thinking,
|
|
244
|
+
action=step_result.action,
|
|
245
|
+
step=agent.step_count,
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if step_result.finished:
|
|
250
|
+
result_message = step_result.message or "Task completed"
|
|
251
|
+
task_success = step_result.success
|
|
252
|
+
break
|
|
253
|
+
else:
|
|
254
|
+
result_message = "Max steps reached"
|
|
255
|
+
task_success = False
|
|
256
|
+
|
|
257
|
+
steps = agent.step_count
|
|
258
|
+
end_time = datetime.now()
|
|
259
|
+
|
|
260
|
+
record = ConversationRecord(
|
|
261
|
+
task_text=workflow["text"],
|
|
262
|
+
final_message=result_message,
|
|
263
|
+
success=task_success,
|
|
264
|
+
steps=steps,
|
|
265
|
+
start_time=start_time,
|
|
266
|
+
end_time=end_time,
|
|
267
|
+
duration_ms=int((end_time - start_time).total_seconds() * 1000),
|
|
268
|
+
source="scheduled",
|
|
269
|
+
source_detail=task.name,
|
|
270
|
+
error_message=None if task_success else result_message,
|
|
271
|
+
messages=messages,
|
|
272
|
+
)
|
|
273
|
+
history_manager.add_record(task.device_serialno, record)
|
|
274
|
+
|
|
275
|
+
if task_success:
|
|
276
|
+
self._record_success(task, result_message)
|
|
277
|
+
else:
|
|
278
|
+
self._record_failure(task, result_message)
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
end_time = datetime.now()
|
|
282
|
+
error_msg = str(e)
|
|
283
|
+
logger.error(f"Scheduled task failed: {task.name} - {error_msg}")
|
|
284
|
+
|
|
285
|
+
record = ConversationRecord(
|
|
286
|
+
task_text=workflow["text"],
|
|
287
|
+
final_message=error_msg,
|
|
288
|
+
success=False,
|
|
289
|
+
steps=0,
|
|
290
|
+
start_time=start_time,
|
|
291
|
+
end_time=end_time,
|
|
292
|
+
duration_ms=int((end_time - start_time).total_seconds() * 1000),
|
|
293
|
+
source="scheduled",
|
|
294
|
+
source_detail=task.name,
|
|
295
|
+
error_message=error_msg,
|
|
296
|
+
messages=messages,
|
|
297
|
+
)
|
|
298
|
+
history_manager.add_record(task.device_serialno, record)
|
|
299
|
+
|
|
300
|
+
self._record_failure(task, error_msg)
|
|
301
|
+
|
|
302
|
+
finally:
|
|
303
|
+
manager.release_device(device.primary_device_id)
|
|
304
|
+
|
|
305
|
+
def _record_success(self, task: ScheduledTask, message: str) -> None:
|
|
306
|
+
task.last_run_time = datetime.now()
|
|
307
|
+
task.last_run_success = True
|
|
308
|
+
task.last_run_message = message[:500] if message else ""
|
|
309
|
+
self._save_tasks()
|
|
310
|
+
logger.info(f"Scheduled task completed: {task.name}")
|
|
311
|
+
|
|
312
|
+
def _record_failure(self, task: ScheduledTask, error: str) -> None:
|
|
313
|
+
task.last_run_time = datetime.now()
|
|
314
|
+
task.last_run_success = False
|
|
315
|
+
task.last_run_message = error[:500] if error else ""
|
|
316
|
+
self._save_tasks()
|
|
317
|
+
logger.warning(f"Scheduled task failed: {task.name} - {error}")
|
|
318
|
+
|
|
319
|
+
def _load_tasks(self) -> None:
|
|
320
|
+
if not self._tasks_path.exists():
|
|
321
|
+
return
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
with open(self._tasks_path, encoding="utf-8") as f:
|
|
325
|
+
data = json.load(f)
|
|
326
|
+
tasks_data = data.get("tasks", [])
|
|
327
|
+
self._tasks = {t["id"]: ScheduledTask.from_dict(t) for t in tasks_data}
|
|
328
|
+
self._file_mtime = self._tasks_path.stat().st_mtime
|
|
329
|
+
logger.debug(f"Loaded {len(self._tasks)} scheduled tasks")
|
|
330
|
+
except Exception as e:
|
|
331
|
+
logger.warning(f"Failed to load scheduled tasks: {e}")
|
|
332
|
+
|
|
333
|
+
def _save_tasks(self) -> None:
|
|
334
|
+
self._tasks_path.parent.mkdir(parents=True, exist_ok=True)
|
|
335
|
+
temp_path = self._tasks_path.with_suffix(".tmp")
|
|
336
|
+
|
|
337
|
+
try:
|
|
338
|
+
data = {"tasks": [t.to_dict() for t in self._tasks.values()]}
|
|
339
|
+
with open(temp_path, "w", encoding="utf-8") as f:
|
|
340
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
341
|
+
temp_path.replace(self._tasks_path)
|
|
342
|
+
self._file_mtime = self._tasks_path.stat().st_mtime
|
|
343
|
+
logger.debug(f"Saved {len(self._tasks)} scheduled tasks")
|
|
344
|
+
except Exception as e:
|
|
345
|
+
logger.error(f"Failed to save scheduled tasks: {e}")
|
|
346
|
+
if temp_path.exists():
|
|
347
|
+
temp_path.unlink()
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
scheduler_manager = SchedulerManager()
|