autoglm-gui 1.4.0__py3-none-any.whl → 1.5.0__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 (120) hide show
  1. AutoGLM_GUI/__init__.py +11 -0
  2. AutoGLM_GUI/__main__.py +26 -8
  3. AutoGLM_GUI/actions/__init__.py +6 -0
  4. AutoGLM_GUI/actions/handler.py +196 -0
  5. AutoGLM_GUI/actions/types.py +15 -0
  6. AutoGLM_GUI/adb/__init__.py +53 -0
  7. AutoGLM_GUI/adb/apps.py +227 -0
  8. AutoGLM_GUI/adb/connection.py +323 -0
  9. AutoGLM_GUI/adb/device.py +171 -0
  10. AutoGLM_GUI/adb/input.py +67 -0
  11. AutoGLM_GUI/adb/screenshot.py +11 -0
  12. AutoGLM_GUI/adb/timing.py +167 -0
  13. AutoGLM_GUI/adb_plus/keyboard_installer.py +4 -2
  14. AutoGLM_GUI/adb_plus/qr_pair.py +8 -8
  15. AutoGLM_GUI/adb_plus/screenshot.py +22 -1
  16. AutoGLM_GUI/adb_plus/serial.py +38 -20
  17. AutoGLM_GUI/adb_plus/touch.py +4 -9
  18. AutoGLM_GUI/agents/__init__.py +51 -0
  19. AutoGLM_GUI/agents/events.py +19 -0
  20. AutoGLM_GUI/agents/factory.py +153 -0
  21. AutoGLM_GUI/agents/glm/__init__.py +7 -0
  22. AutoGLM_GUI/agents/glm/agent.py +292 -0
  23. AutoGLM_GUI/agents/glm/message_builder.py +81 -0
  24. AutoGLM_GUI/agents/glm/parser.py +110 -0
  25. AutoGLM_GUI/agents/glm/prompts_en.py +77 -0
  26. AutoGLM_GUI/agents/glm/prompts_zh.py +75 -0
  27. AutoGLM_GUI/agents/mai/__init__.py +28 -0
  28. AutoGLM_GUI/agents/mai/agent.py +405 -0
  29. AutoGLM_GUI/agents/mai/parser.py +254 -0
  30. AutoGLM_GUI/agents/mai/prompts.py +103 -0
  31. AutoGLM_GUI/agents/mai/traj_memory.py +91 -0
  32. AutoGLM_GUI/agents/protocols.py +27 -0
  33. AutoGLM_GUI/agents/stream_runner.py +188 -0
  34. AutoGLM_GUI/api/__init__.py +71 -11
  35. AutoGLM_GUI/api/agents.py +190 -229
  36. AutoGLM_GUI/api/control.py +9 -6
  37. AutoGLM_GUI/api/devices.py +112 -28
  38. AutoGLM_GUI/api/health.py +13 -0
  39. AutoGLM_GUI/api/history.py +78 -0
  40. AutoGLM_GUI/api/layered_agent.py +306 -181
  41. AutoGLM_GUI/api/mcp.py +11 -10
  42. AutoGLM_GUI/api/media.py +64 -1
  43. AutoGLM_GUI/api/scheduled_tasks.py +98 -0
  44. AutoGLM_GUI/api/version.py +23 -10
  45. AutoGLM_GUI/api/workflows.py +2 -1
  46. AutoGLM_GUI/config.py +72 -14
  47. AutoGLM_GUI/config_manager.py +98 -27
  48. AutoGLM_GUI/device_adapter.py +263 -0
  49. AutoGLM_GUI/device_manager.py +248 -29
  50. AutoGLM_GUI/device_protocol.py +266 -0
  51. AutoGLM_GUI/devices/__init__.py +49 -0
  52. AutoGLM_GUI/devices/adb_device.py +200 -0
  53. AutoGLM_GUI/devices/mock_device.py +185 -0
  54. AutoGLM_GUI/devices/remote_device.py +177 -0
  55. AutoGLM_GUI/exceptions.py +3 -3
  56. AutoGLM_GUI/history_manager.py +164 -0
  57. AutoGLM_GUI/i18n.py +81 -0
  58. AutoGLM_GUI/metrics.py +13 -20
  59. AutoGLM_GUI/model/__init__.py +5 -0
  60. AutoGLM_GUI/model/message_builder.py +69 -0
  61. AutoGLM_GUI/model/types.py +24 -0
  62. AutoGLM_GUI/models/__init__.py +10 -0
  63. AutoGLM_GUI/models/history.py +96 -0
  64. AutoGLM_GUI/models/scheduled_task.py +71 -0
  65. AutoGLM_GUI/parsers/__init__.py +22 -0
  66. AutoGLM_GUI/parsers/base.py +50 -0
  67. AutoGLM_GUI/parsers/phone_parser.py +58 -0
  68. AutoGLM_GUI/phone_agent_manager.py +118 -367
  69. AutoGLM_GUI/platform_utils.py +31 -2
  70. AutoGLM_GUI/prompt_config.py +15 -0
  71. AutoGLM_GUI/prompts/__init__.py +32 -0
  72. AutoGLM_GUI/scheduler_manager.py +304 -0
  73. AutoGLM_GUI/schemas.py +272 -63
  74. AutoGLM_GUI/scrcpy_stream.py +159 -37
  75. AutoGLM_GUI/server.py +3 -1
  76. AutoGLM_GUI/socketio_server.py +114 -29
  77. AutoGLM_GUI/state.py +10 -30
  78. AutoGLM_GUI/static/assets/{about-DeclntHg.js → about-BQm96DAl.js} +1 -1
  79. AutoGLM_GUI/static/assets/alert-dialog-B42XxGPR.js +1 -0
  80. AutoGLM_GUI/static/assets/chat-C0L2gQYG.js +129 -0
  81. AutoGLM_GUI/static/assets/circle-alert-D4rSJh37.js +1 -0
  82. AutoGLM_GUI/static/assets/dialog-DZ78cEcj.js +45 -0
  83. AutoGLM_GUI/static/assets/history-DFBv7TGc.js +1 -0
  84. AutoGLM_GUI/static/assets/index-Bzyv2yQ2.css +1 -0
  85. AutoGLM_GUI/static/assets/{index-zQ4KKDHt.js → index-CmZSnDqc.js} +1 -1
  86. AutoGLM_GUI/static/assets/index-CssG-3TH.js +11 -0
  87. AutoGLM_GUI/static/assets/label-BCUzE_nm.js +1 -0
  88. AutoGLM_GUI/static/assets/logs-eoFxn5of.js +1 -0
  89. AutoGLM_GUI/static/assets/popover-DLsuV5Sx.js +1 -0
  90. AutoGLM_GUI/static/assets/scheduled-tasks-MyqGJvy_.js +1 -0
  91. AutoGLM_GUI/static/assets/square-pen-zGWYrdfj.js +1 -0
  92. AutoGLM_GUI/static/assets/textarea-BX6y7uM5.js +1 -0
  93. AutoGLM_GUI/static/assets/workflows-CYFs6ssC.js +1 -0
  94. AutoGLM_GUI/static/index.html +2 -2
  95. AutoGLM_GUI/types.py +142 -0
  96. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.5.0.dist-info}/METADATA +178 -92
  97. autoglm_gui-1.5.0.dist-info/RECORD +157 -0
  98. mai_agent/base.py +137 -0
  99. mai_agent/mai_grounding_agent.py +263 -0
  100. mai_agent/mai_naivigation_agent.py +526 -0
  101. mai_agent/prompt.py +148 -0
  102. mai_agent/unified_memory.py +67 -0
  103. mai_agent/utils.py +73 -0
  104. AutoGLM_GUI/api/dual_model.py +0 -311
  105. AutoGLM_GUI/dual_model/__init__.py +0 -53
  106. AutoGLM_GUI/dual_model/decision_model.py +0 -664
  107. AutoGLM_GUI/dual_model/dual_agent.py +0 -917
  108. AutoGLM_GUI/dual_model/protocols.py +0 -354
  109. AutoGLM_GUI/dual_model/vision_model.py +0 -442
  110. AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +0 -291
  111. AutoGLM_GUI/phone_agent_patches.py +0 -146
  112. AutoGLM_GUI/static/assets/chat-Iut2yhSw.js +0 -125
  113. AutoGLM_GUI/static/assets/dialog-BfdcBs1x.js +0 -45
  114. AutoGLM_GUI/static/assets/index-5hCCwHA7.css +0 -1
  115. AutoGLM_GUI/static/assets/index-DHF1NZh0.js +0 -12
  116. AutoGLM_GUI/static/assets/workflows-xiplap-r.js +0 -1
  117. autoglm_gui-1.4.0.dist-info/RECORD +0 -100
  118. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.5.0.dist-info}/WHEEL +0 -0
  119. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.5.0.dist-info}/entry_points.txt +0 -0
  120. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,96 @@
1
+ """Conversation history data models."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime
5
+ from typing import Literal
6
+ from uuid import uuid4
7
+
8
+
9
+ @dataclass
10
+ class ConversationRecord:
11
+ """单条对话记录."""
12
+
13
+ id: str = field(default_factory=lambda: str(uuid4()))
14
+
15
+ # 任务信息
16
+ task_text: str = "" # 用户输入的任务
17
+ final_message: str = "" # 最终结果消息
18
+
19
+ # 执行信息
20
+ success: bool = False
21
+ steps: int = 0
22
+ start_time: datetime = field(default_factory=datetime.now)
23
+ end_time: datetime | None = None
24
+ duration_ms: int = 0 # 执行时长(毫秒)
25
+
26
+ # 来源标记
27
+ source: Literal["chat", "layered", "scheduled"] = "chat"
28
+ source_detail: str = "" # 定时任务名称 or session_id
29
+
30
+ # 错误信息
31
+ error_message: str | None = None
32
+
33
+ def to_dict(self) -> dict:
34
+ """转换为可序列化的字典."""
35
+ return {
36
+ "id": self.id,
37
+ "task_text": self.task_text,
38
+ "final_message": self.final_message,
39
+ "success": self.success,
40
+ "steps": self.steps,
41
+ "start_time": self.start_time.isoformat(),
42
+ "end_time": self.end_time.isoformat() if self.end_time else None,
43
+ "duration_ms": self.duration_ms,
44
+ "source": self.source,
45
+ "source_detail": self.source_detail,
46
+ "error_message": self.error_message,
47
+ }
48
+
49
+ @classmethod
50
+ def from_dict(cls, data: dict) -> "ConversationRecord":
51
+ """从字典创建实例."""
52
+ return cls(
53
+ id=data.get("id", str(uuid4())),
54
+ task_text=data.get("task_text", ""),
55
+ final_message=data.get("final_message", ""),
56
+ success=data.get("success", False),
57
+ steps=data.get("steps", 0),
58
+ start_time=datetime.fromisoformat(data["start_time"])
59
+ if data.get("start_time")
60
+ else datetime.now(),
61
+ end_time=datetime.fromisoformat(data["end_time"])
62
+ if data.get("end_time")
63
+ else None,
64
+ duration_ms=data.get("duration_ms", 0),
65
+ source=data.get("source", "chat"),
66
+ source_detail=data.get("source_detail", ""),
67
+ error_message=data.get("error_message"),
68
+ )
69
+
70
+
71
+ @dataclass
72
+ class DeviceHistory:
73
+ """设备对话历史(一个设备一个文件)."""
74
+
75
+ serialno: str
76
+ records: list[ConversationRecord] = field(default_factory=list)
77
+ last_updated: datetime = field(default_factory=datetime.now)
78
+
79
+ def to_dict(self) -> dict:
80
+ """转换为可序列化的字典."""
81
+ return {
82
+ "serialno": self.serialno,
83
+ "records": [r.to_dict() for r in self.records],
84
+ "last_updated": self.last_updated.isoformat(),
85
+ }
86
+
87
+ @classmethod
88
+ def from_dict(cls, data: dict) -> "DeviceHistory":
89
+ """从字典创建实例."""
90
+ return cls(
91
+ serialno=data.get("serialno", ""),
92
+ records=[ConversationRecord.from_dict(r) for r in data.get("records", [])],
93
+ last_updated=datetime.fromisoformat(data["last_updated"])
94
+ if data.get("last_updated")
95
+ else datetime.now(),
96
+ )
@@ -0,0 +1,71 @@
1
+ """Scheduled task data models."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime
5
+ from uuid import uuid4
6
+
7
+
8
+ @dataclass
9
+ class ScheduledTask:
10
+ """定时任务定义."""
11
+
12
+ id: str = field(default_factory=lambda: str(uuid4()))
13
+
14
+ # 基础信息
15
+ name: str = "" # 任务名称
16
+ workflow_uuid: str = "" # 关联的 Workflow UUID
17
+ device_serialno: str = "" # 绑定的设备 serialno
18
+
19
+ # 调度配置
20
+ cron_expression: str = "" # Cron 表达式 (如 "0 8 * * *")
21
+ enabled: bool = True # 是否启用
22
+
23
+ # 元数据
24
+ created_at: datetime = field(default_factory=datetime.now)
25
+ updated_at: datetime = field(default_factory=datetime.now)
26
+
27
+ # 最近执行信息(只记录最后一次)
28
+ last_run_time: datetime | None = None
29
+ last_run_success: bool | None = None
30
+ last_run_message: str | None = None
31
+
32
+ def to_dict(self) -> dict:
33
+ """转换为可序列化的字典."""
34
+ return {
35
+ "id": self.id,
36
+ "name": self.name,
37
+ "workflow_uuid": self.workflow_uuid,
38
+ "device_serialno": self.device_serialno,
39
+ "cron_expression": self.cron_expression,
40
+ "enabled": self.enabled,
41
+ "created_at": self.created_at.isoformat(),
42
+ "updated_at": self.updated_at.isoformat(),
43
+ "last_run_time": self.last_run_time.isoformat()
44
+ if self.last_run_time
45
+ else None,
46
+ "last_run_success": self.last_run_success,
47
+ "last_run_message": self.last_run_message,
48
+ }
49
+
50
+ @classmethod
51
+ def from_dict(cls, data: dict) -> "ScheduledTask":
52
+ """从字典创建实例."""
53
+ return cls(
54
+ id=data.get("id", str(uuid4())),
55
+ name=data.get("name", ""),
56
+ workflow_uuid=data.get("workflow_uuid", ""),
57
+ device_serialno=data.get("device_serialno", ""),
58
+ cron_expression=data.get("cron_expression", ""),
59
+ enabled=data.get("enabled", True),
60
+ created_at=datetime.fromisoformat(data["created_at"])
61
+ if data.get("created_at")
62
+ else datetime.now(),
63
+ updated_at=datetime.fromisoformat(data["updated_at"])
64
+ if data.get("updated_at")
65
+ else datetime.now(),
66
+ last_run_time=datetime.fromisoformat(data["last_run_time"])
67
+ if data.get("last_run_time")
68
+ else None,
69
+ last_run_success=data.get("last_run_success"),
70
+ last_run_message=data.get("last_run_message"),
71
+ )
@@ -0,0 +1,22 @@
1
+ """Action parsers for different agent types.
2
+
3
+ This module provides parser implementations for converting model outputs
4
+ into standardized action dictionaries that can be executed by ActionHandler.
5
+
6
+ Each agent type has its own parser implementation:
7
+ - GLMParser: For GLM-based agents (enhanced AST parsing)
8
+ - PhoneAgentParser: For standard PhoneAgent (basic AST parsing)
9
+ - MAIParser: For MAI agent (XML + JSON parsing)
10
+ """
11
+
12
+ from .base import ActionParser
13
+ from AutoGLM_GUI.agents.glm.parser import GLMParser
14
+ from AutoGLM_GUI.agents.mai.parser import MAIParser
15
+ from .phone_parser import PhoneAgentParser
16
+
17
+ __all__ = [
18
+ "ActionParser",
19
+ "GLMParser",
20
+ "MAIParser",
21
+ "PhoneAgentParser",
22
+ ]
@@ -0,0 +1,50 @@
1
+ """Base protocol for action parsers.
2
+
3
+ This module defines the interface that all action parsers must implement.
4
+ """
5
+
6
+ from typing import Any, Protocol
7
+
8
+
9
+ class ActionParser(Protocol):
10
+ """Protocol for parsing model outputs into action dictionaries.
11
+
12
+ All parser implementations must provide:
13
+ 1. parse() method to convert raw model output into standardized action dict
14
+ 2. coordinate_scale property to specify the coordinate normalization range
15
+
16
+ The standardized action dictionary format:
17
+ {
18
+ "_metadata": "do" | "finish",
19
+ "action": "Tap" | "Swipe" | "Type" | ..., # Only when _metadata="do"
20
+ "coordinate": [x, y], # Normalized to 0-1000 range
21
+ "text": "...", # For Type action
22
+ ... # Other action-specific parameters
23
+ }
24
+ """
25
+
26
+ def parse(self, raw_response: str) -> dict[str, Any]:
27
+ """Parse raw model output into standardized action dictionary.
28
+
29
+ Args:
30
+ raw_response: Raw text output from the model.
31
+
32
+ Returns:
33
+ Standardized action dictionary with:
34
+ - "_metadata": "do" or "finish"
35
+ - "action": Action type (Tap, Swipe, etc.) when _metadata="do"
36
+ - Additional parameters based on action type
37
+
38
+ Raises:
39
+ ValueError: If the response cannot be parsed.
40
+ """
41
+ ...
42
+
43
+ @property
44
+ def coordinate_scale(self) -> int:
45
+ """Get the coordinate normalization scale used by this parser.
46
+
47
+ Returns:
48
+ 999 for MAI parser, 1000 for GLM/PhoneAgent parsers.
49
+ """
50
+ ...
@@ -0,0 +1,58 @@
1
+ """PhoneAgent parser using standard AST parsing."""
2
+
3
+ import ast
4
+ from typing import Any
5
+
6
+
7
+ class PhoneAgentParser:
8
+ """Parse PhoneAgent function-call style outputs using ast.parse.
9
+
10
+ Handles the same format as GLM but with simpler AST-based parsing.
11
+ Includes special handling for Type action to avoid parsing issues.
12
+ Coordinate scale: 0-1000
13
+ """
14
+
15
+ @property
16
+ def coordinate_scale(self) -> int:
17
+ return 1000
18
+
19
+ def parse(self, raw_response: str) -> dict[str, Any]:
20
+ response = raw_response.strip()
21
+
22
+ if response.startswith('do(action="Type"') or response.startswith(
23
+ 'do(action="Type_Name"'
24
+ ):
25
+ text = response.split("text=", 1)[1][1:-2]
26
+ action = {"_metadata": "do", "action": "Type", "text": text}
27
+ return action
28
+ elif response.startswith("do"):
29
+ try:
30
+ response = response.replace("\n", "\\n")
31
+ response = response.replace("\r", "\\r")
32
+ response = response.replace("\t", "\\t")
33
+
34
+ tree = ast.parse(response, mode="eval")
35
+ if not isinstance(tree.body, ast.Call):
36
+ raise ValueError("Expected a function call")
37
+
38
+ call = tree.body
39
+ action = {"_metadata": "do"}
40
+ for keyword in call.keywords:
41
+ key = keyword.arg
42
+ if key is None:
43
+ raise ValueError("Keyword argument name missing")
44
+ value = ast.literal_eval(keyword.value)
45
+ action[key] = value
46
+
47
+ return action
48
+ except (SyntaxError, ValueError) as e:
49
+ raise ValueError(f"Failed to parse do() action: {e}") from e
50
+
51
+ elif response.startswith("finish"):
52
+ action = {
53
+ "_metadata": "finish",
54
+ "message": response.replace("finish(message=", "")[1:-2],
55
+ }
56
+ return action
57
+ else:
58
+ raise ValueError(f"Failed to parse action: {response}")