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.
Files changed (135) hide show
  1. AutoGLM_GUI/__init__.py +11 -0
  2. AutoGLM_GUI/__main__.py +26 -4
  3. AutoGLM_GUI/actions/__init__.py +6 -0
  4. phone_agent/actions/handler_ios.py → AutoGLM_GUI/actions/handler.py +30 -112
  5. AutoGLM_GUI/actions/types.py +15 -0
  6. {phone_agent → AutoGLM_GUI}/adb/__init__.py +25 -23
  7. {phone_agent → AutoGLM_GUI}/adb/connection.py +5 -40
  8. {phone_agent → AutoGLM_GUI}/adb/device.py +12 -94
  9. {phone_agent → AutoGLM_GUI}/adb/input.py +6 -47
  10. AutoGLM_GUI/adb/screenshot.py +11 -0
  11. {phone_agent/config → AutoGLM_GUI/adb}/timing.py +1 -1
  12. AutoGLM_GUI/adb_plus/keyboard_installer.py +4 -2
  13. AutoGLM_GUI/adb_plus/screenshot.py +22 -1
  14. AutoGLM_GUI/adb_plus/serial.py +38 -20
  15. AutoGLM_GUI/adb_plus/touch.py +4 -9
  16. AutoGLM_GUI/agents/__init__.py +43 -12
  17. AutoGLM_GUI/agents/events.py +19 -0
  18. AutoGLM_GUI/agents/factory.py +31 -38
  19. AutoGLM_GUI/agents/glm/__init__.py +7 -0
  20. AutoGLM_GUI/agents/glm/agent.py +297 -0
  21. AutoGLM_GUI/agents/glm/message_builder.py +81 -0
  22. AutoGLM_GUI/agents/glm/parser.py +110 -0
  23. {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_en.py +7 -9
  24. {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_zh.py +18 -25
  25. AutoGLM_GUI/agents/mai/__init__.py +28 -0
  26. AutoGLM_GUI/agents/mai/agent.py +408 -0
  27. AutoGLM_GUI/agents/mai/parser.py +254 -0
  28. AutoGLM_GUI/agents/mai/prompts.py +103 -0
  29. AutoGLM_GUI/agents/mai/traj_memory.py +91 -0
  30. AutoGLM_GUI/agents/protocols.py +12 -8
  31. AutoGLM_GUI/agents/stream_runner.py +193 -0
  32. AutoGLM_GUI/api/__init__.py +40 -21
  33. AutoGLM_GUI/api/agents.py +181 -239
  34. AutoGLM_GUI/api/control.py +9 -6
  35. AutoGLM_GUI/api/devices.py +102 -12
  36. AutoGLM_GUI/api/history.py +104 -0
  37. AutoGLM_GUI/api/layered_agent.py +67 -15
  38. AutoGLM_GUI/api/media.py +64 -1
  39. AutoGLM_GUI/api/scheduled_tasks.py +98 -0
  40. AutoGLM_GUI/config.py +81 -0
  41. AutoGLM_GUI/config_manager.py +68 -51
  42. AutoGLM_GUI/device_manager.py +248 -29
  43. AutoGLM_GUI/device_protocol.py +1 -1
  44. AutoGLM_GUI/devices/adb_device.py +5 -10
  45. AutoGLM_GUI/devices/mock_device.py +4 -2
  46. AutoGLM_GUI/devices/remote_device.py +8 -3
  47. AutoGLM_GUI/history_manager.py +164 -0
  48. AutoGLM_GUI/model/__init__.py +5 -0
  49. AutoGLM_GUI/model/message_builder.py +69 -0
  50. AutoGLM_GUI/model/types.py +24 -0
  51. AutoGLM_GUI/models/__init__.py +10 -0
  52. AutoGLM_GUI/models/history.py +140 -0
  53. AutoGLM_GUI/models/scheduled_task.py +71 -0
  54. AutoGLM_GUI/parsers/__init__.py +22 -0
  55. AutoGLM_GUI/parsers/base.py +50 -0
  56. AutoGLM_GUI/parsers/phone_parser.py +58 -0
  57. AutoGLM_GUI/phone_agent_manager.py +62 -396
  58. AutoGLM_GUI/platform_utils.py +26 -0
  59. AutoGLM_GUI/prompt_config.py +15 -0
  60. AutoGLM_GUI/prompts/__init__.py +32 -0
  61. AutoGLM_GUI/scheduler_manager.py +350 -0
  62. AutoGLM_GUI/schemas.py +246 -72
  63. AutoGLM_GUI/scrcpy_stream.py +142 -24
  64. AutoGLM_GUI/socketio_server.py +100 -27
  65. AutoGLM_GUI/static/assets/{about-_XNhzQZX.js → about-CfwX1Cmc.js} +1 -1
  66. AutoGLM_GUI/static/assets/alert-dialog-CtGlN2IJ.js +1 -0
  67. AutoGLM_GUI/static/assets/chat-BYa-foUI.js +129 -0
  68. AutoGLM_GUI/static/assets/circle-alert-t08bEMPO.js +1 -0
  69. AutoGLM_GUI/static/assets/dialog-FNwZJFwk.js +45 -0
  70. AutoGLM_GUI/static/assets/eye-D0UPWCWC.js +1 -0
  71. AutoGLM_GUI/static/assets/history-CRo95B7i.js +1 -0
  72. AutoGLM_GUI/static/assets/{index-Cy8TmmHV.js → index-BaLMSqd3.js} +1 -1
  73. AutoGLM_GUI/static/assets/index-CTHbFvKl.js +11 -0
  74. AutoGLM_GUI/static/assets/index-CV7jGxGm.css +1 -0
  75. AutoGLM_GUI/static/assets/label-DJFevVmr.js +1 -0
  76. AutoGLM_GUI/static/assets/logs-RW09DyYY.js +1 -0
  77. AutoGLM_GUI/static/assets/popover--JTJrE5v.js +1 -0
  78. AutoGLM_GUI/static/assets/scheduled-tasks-DTRKsQXF.js +1 -0
  79. AutoGLM_GUI/static/assets/square-pen-CPK_K680.js +1 -0
  80. AutoGLM_GUI/static/assets/textarea-PRmVnWq5.js +1 -0
  81. AutoGLM_GUI/static/assets/workflows-CdcsAoaT.js +1 -0
  82. AutoGLM_GUI/static/index.html +2 -2
  83. AutoGLM_GUI/types.py +17 -0
  84. {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/METADATA +179 -130
  85. autoglm_gui-1.5.1.dist-info/RECORD +118 -0
  86. AutoGLM_GUI/agents/mai_adapter.py +0 -627
  87. AutoGLM_GUI/api/dual_model.py +0 -317
  88. AutoGLM_GUI/device_adapter.py +0 -263
  89. AutoGLM_GUI/dual_model/__init__.py +0 -53
  90. AutoGLM_GUI/dual_model/decision_model.py +0 -664
  91. AutoGLM_GUI/dual_model/dual_agent.py +0 -917
  92. AutoGLM_GUI/dual_model/protocols.py +0 -354
  93. AutoGLM_GUI/dual_model/vision_model.py +0 -442
  94. AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +0 -291
  95. AutoGLM_GUI/phone_agent_patches.py +0 -147
  96. AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +0 -126
  97. AutoGLM_GUI/static/assets/dialog-B3uW4T8V.js +0 -45
  98. AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +0 -1
  99. AutoGLM_GUI/static/assets/index-UYYauTly.js +0 -12
  100. AutoGLM_GUI/static/assets/workflows-Du_de-dt.js +0 -1
  101. autoglm_gui-1.4.1.dist-info/RECORD +0 -117
  102. mai_agent/base.py +0 -137
  103. mai_agent/mai_grounding_agent.py +0 -263
  104. mai_agent/mai_naivigation_agent.py +0 -526
  105. mai_agent/prompt.py +0 -148
  106. mai_agent/unified_memory.py +0 -67
  107. mai_agent/utils.py +0 -73
  108. phone_agent/__init__.py +0 -12
  109. phone_agent/actions/__init__.py +0 -5
  110. phone_agent/actions/handler.py +0 -400
  111. phone_agent/adb/screenshot.py +0 -108
  112. phone_agent/agent.py +0 -253
  113. phone_agent/agent_ios.py +0 -277
  114. phone_agent/config/__init__.py +0 -53
  115. phone_agent/config/apps_harmonyos.py +0 -256
  116. phone_agent/config/apps_ios.py +0 -339
  117. phone_agent/config/prompts.py +0 -80
  118. phone_agent/device_factory.py +0 -166
  119. phone_agent/hdc/__init__.py +0 -53
  120. phone_agent/hdc/connection.py +0 -384
  121. phone_agent/hdc/device.py +0 -269
  122. phone_agent/hdc/input.py +0 -145
  123. phone_agent/hdc/screenshot.py +0 -127
  124. phone_agent/model/__init__.py +0 -5
  125. phone_agent/model/client.py +0 -290
  126. phone_agent/xctest/__init__.py +0 -47
  127. phone_agent/xctest/connection.py +0 -379
  128. phone_agent/xctest/device.py +0 -472
  129. phone_agent/xctest/input.py +0 -311
  130. phone_agent/xctest/screenshot.py +0 -226
  131. {phone_agent/config → AutoGLM_GUI/adb}/apps.py +0 -0
  132. {phone_agent/config → AutoGLM_GUI}/i18n.py +0 -0
  133. {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/WHEEL +0 -0
  134. {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/entry_points.txt +0 -0
  135. {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()