autoglm-gui 1.4.0__py3-none-any.whl → 1.4.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 (57) hide show
  1. AutoGLM_GUI/__main__.py +0 -4
  2. AutoGLM_GUI/adb_plus/qr_pair.py +8 -8
  3. AutoGLM_GUI/agents/__init__.py +20 -0
  4. AutoGLM_GUI/agents/factory.py +160 -0
  5. AutoGLM_GUI/agents/mai_adapter.py +627 -0
  6. AutoGLM_GUI/agents/protocols.py +23 -0
  7. AutoGLM_GUI/api/__init__.py +48 -7
  8. AutoGLM_GUI/api/agents.py +61 -17
  9. AutoGLM_GUI/api/devices.py +12 -18
  10. AutoGLM_GUI/api/dual_model.py +15 -9
  11. AutoGLM_GUI/api/health.py +13 -0
  12. AutoGLM_GUI/api/layered_agent.py +239 -166
  13. AutoGLM_GUI/api/mcp.py +11 -10
  14. AutoGLM_GUI/api/version.py +23 -10
  15. AutoGLM_GUI/api/workflows.py +2 -1
  16. AutoGLM_GUI/config_manager.py +55 -1
  17. AutoGLM_GUI/device_adapter.py +263 -0
  18. AutoGLM_GUI/device_protocol.py +266 -0
  19. AutoGLM_GUI/devices/__init__.py +49 -0
  20. AutoGLM_GUI/devices/adb_device.py +205 -0
  21. AutoGLM_GUI/devices/mock_device.py +183 -0
  22. AutoGLM_GUI/devices/remote_device.py +172 -0
  23. AutoGLM_GUI/dual_model/decision_model.py +4 -4
  24. AutoGLM_GUI/exceptions.py +3 -3
  25. AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +2 -2
  26. AutoGLM_GUI/metrics.py +13 -20
  27. AutoGLM_GUI/phone_agent_manager.py +219 -134
  28. AutoGLM_GUI/phone_agent_patches.py +2 -1
  29. AutoGLM_GUI/platform_utils.py +5 -2
  30. AutoGLM_GUI/schemas.py +47 -0
  31. AutoGLM_GUI/scrcpy_stream.py +17 -13
  32. AutoGLM_GUI/server.py +3 -1
  33. AutoGLM_GUI/socketio_server.py +16 -4
  34. AutoGLM_GUI/state.py +10 -30
  35. AutoGLM_GUI/static/assets/{about-DeclntHg.js → about-_XNhzQZX.js} +1 -1
  36. AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +126 -0
  37. AutoGLM_GUI/static/assets/{dialog-BfdcBs1x.js → dialog-B3uW4T8V.js} +3 -3
  38. AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +1 -0
  39. AutoGLM_GUI/static/assets/{index-zQ4KKDHt.js → index-Cy8TmmHV.js} +1 -1
  40. AutoGLM_GUI/static/assets/{index-DHF1NZh0.js → index-UYYauTly.js} +6 -6
  41. AutoGLM_GUI/static/assets/{workflows-xiplap-r.js → workflows-Du_de-dt.js} +1 -1
  42. AutoGLM_GUI/static/index.html +2 -2
  43. AutoGLM_GUI/types.py +125 -0
  44. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.4.1.dist-info}/METADATA +83 -4
  45. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.4.1.dist-info}/RECORD +54 -37
  46. mai_agent/base.py +137 -0
  47. mai_agent/mai_grounding_agent.py +263 -0
  48. mai_agent/mai_naivigation_agent.py +526 -0
  49. mai_agent/prompt.py +148 -0
  50. mai_agent/unified_memory.py +67 -0
  51. mai_agent/utils.py +73 -0
  52. AutoGLM_GUI/config.py +0 -23
  53. AutoGLM_GUI/static/assets/chat-Iut2yhSw.js +0 -125
  54. AutoGLM_GUI/static/assets/index-5hCCwHA7.css +0 -1
  55. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.4.1.dist-info}/WHEEL +0 -0
  56. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.4.1.dist-info}/entry_points.txt +0 -0
  57. {autoglm_gui-1.4.0.dist-info → autoglm_gui-1.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -1 +1 @@
1
- import{c as D,u as S,r as t,l as b,j as e,B as r,d as W,a as F,b as T}from"./index-DHF1NZh0.js";import{P as E,L as f,C as L,a as P,b as z,c as H,T as I,D as q,d as B,e as M,f as _,g as j,I as O,h as R,i as A}from"./dialog-BfdcBs1x.js";const G=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",key:"1m0v6g"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",key:"ohrbg2"}]],J=D("square-pen",G);function U(){const s=S(),[m,g]=t.useState([]),[p,x]=t.useState(!0),[k,o]=t.useState(!1),[c,h]=t.useState(null),[l,i]=t.useState({name:"",text:""}),[w,u]=t.useState(!1);t.useEffect(()=>{d()},[]);const d=async()=>{try{x(!0);const a=await b();g(a.workflows)}catch(a){console.error("Failed to load workflows:",a)}finally{x(!1)}},v=()=>{h(null),i({name:"",text:""}),o(!0)},N=a=>{h(a),i({name:a.name,text:a.text}),o(!0)},C=async()=>{try{u(!0),c?await F(c.uuid,l):await T(l),o(!1),d()}catch(a){console.error("Failed to save workflow:",a)}finally{u(!1)}},y=async a=>{if(window.confirm(s.workflows.deleteConfirm))try{await W(a),d()}catch(n){console.error("Failed to delete workflow:",n)}};return e.jsxs("div",{className:"container mx-auto p-6 max-w-7xl",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h1",{className:"text-3xl font-bold",children:s.workflows.title}),e.jsxs(r,{onClick:v,children:[e.jsx(E,{className:"w-4 h-4 mr-2"}),s.workflows.createNew]})]}),p?e.jsx("div",{className:"flex justify-center items-center h-64",children:e.jsx(f,{className:"w-8 h-8 animate-spin text-slate-400"})}):m.length===0?e.jsx("div",{className:"text-center py-12",children:e.jsx("p",{className:"text-slate-500 dark:text-slate-400",children:s.workflows.empty})}):e.jsx("div",{className:"grid gap-4 md:grid-cols-2 lg:grid-cols-3",children:m.map(a=>e.jsxs(L,{className:"hover:shadow-md transition-shadow",children:[e.jsx(P,{children:e.jsx(z,{className:"text-lg",children:a.name})}),e.jsxs(H,{children:[e.jsx("p",{className:"text-sm text-slate-600 dark:text-slate-400 mb-4 line-clamp-3",children:a.text}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(r,{variant:"outline",size:"sm",onClick:()=>N(a),children:[e.jsx(J,{className:"w-3 h-3 mr-1"}),s.common.edit]}),e.jsxs(r,{variant:"destructive",size:"sm",onClick:()=>y(a.uuid),children:[e.jsx(I,{className:"w-3 h-3 mr-1"}),s.common.delete]})]})]})]},a.uuid))}),e.jsx(q,{open:k,onOpenChange:o,children:e.jsxs(B,{className:"sm:max-w-[600px]",children:[e.jsx(M,{children:e.jsx(_,{children:c?s.workflows.edit:s.workflows.create})}),e.jsxs("div",{className:"space-y-4 py-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(j,{htmlFor:"name",children:s.workflows.name}),e.jsx(O,{id:"name",value:l.name,onChange:a=>i(n=>({...n,name:a.target.value})),placeholder:s.workflows.namePlaceholder})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(j,{htmlFor:"text",children:s.workflows.text}),e.jsx(R,{id:"text",value:l.text,onChange:a=>i(n=>({...n,text:a.target.value})),placeholder:s.workflows.textPlaceholder,rows:6,className:"resize-none !rounded-lg"})]})]}),e.jsxs(A,{children:[e.jsx(r,{variant:"outline",onClick:()=>o(!1),children:s.common.cancel}),e.jsx(r,{onClick:C,disabled:!l.name.trim()||!l.text.trim()||w,children:w?e.jsxs(e.Fragment,{children:[e.jsx(f,{className:"w-4 h-4 mr-2 animate-spin"}),s.common.loading]}):s.common.save})]})]})})]})}export{U as component};
1
+ import{c as D,u as S,r as t,l as b,j as e,B as r,d as W,a as F,b as T}from"./index-UYYauTly.js";import{P as E,L as f,C as L,a as P,b as z,c as H,T as I,D as q,d as B,e as M,f as _,g as j,I as O,h as R,i as A}from"./dialog-B3uW4T8V.js";const G=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",key:"1m0v6g"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",key:"ohrbg2"}]],J=D("square-pen",G);function U(){const s=S(),[m,g]=t.useState([]),[p,x]=t.useState(!0),[k,o]=t.useState(!1),[c,h]=t.useState(null),[l,i]=t.useState({name:"",text:""}),[w,u]=t.useState(!1);t.useEffect(()=>{d()},[]);const d=async()=>{try{x(!0);const a=await b();g(a.workflows)}catch(a){console.error("Failed to load workflows:",a)}finally{x(!1)}},v=()=>{h(null),i({name:"",text:""}),o(!0)},N=a=>{h(a),i({name:a.name,text:a.text}),o(!0)},C=async()=>{try{u(!0),c?await F(c.uuid,l):await T(l),o(!1),d()}catch(a){console.error("Failed to save workflow:",a)}finally{u(!1)}},y=async a=>{if(window.confirm(s.workflows.deleteConfirm))try{await W(a),d()}catch(n){console.error("Failed to delete workflow:",n)}};return e.jsxs("div",{className:"container mx-auto p-6 max-w-7xl",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h1",{className:"text-3xl font-bold",children:s.workflows.title}),e.jsxs(r,{onClick:v,children:[e.jsx(E,{className:"w-4 h-4 mr-2"}),s.workflows.createNew]})]}),p?e.jsx("div",{className:"flex justify-center items-center h-64",children:e.jsx(f,{className:"w-8 h-8 animate-spin text-slate-400"})}):m.length===0?e.jsx("div",{className:"text-center py-12",children:e.jsx("p",{className:"text-slate-500 dark:text-slate-400",children:s.workflows.empty})}):e.jsx("div",{className:"grid gap-4 md:grid-cols-2 lg:grid-cols-3",children:m.map(a=>e.jsxs(L,{className:"hover:shadow-md transition-shadow",children:[e.jsx(P,{children:e.jsx(z,{className:"text-lg",children:a.name})}),e.jsxs(H,{children:[e.jsx("p",{className:"text-sm text-slate-600 dark:text-slate-400 mb-4 line-clamp-3",children:a.text}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(r,{variant:"outline",size:"sm",onClick:()=>N(a),children:[e.jsx(J,{className:"w-3 h-3 mr-1"}),s.common.edit]}),e.jsxs(r,{variant:"destructive",size:"sm",onClick:()=>y(a.uuid),children:[e.jsx(I,{className:"w-3 h-3 mr-1"}),s.common.delete]})]})]})]},a.uuid))}),e.jsx(q,{open:k,onOpenChange:o,children:e.jsxs(B,{className:"sm:max-w-[600px]",children:[e.jsx(M,{children:e.jsx(_,{children:c?s.workflows.edit:s.workflows.create})}),e.jsxs("div",{className:"space-y-4 py-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(j,{htmlFor:"name",children:s.workflows.name}),e.jsx(O,{id:"name",value:l.name,onChange:a=>i(n=>({...n,name:a.target.value})),placeholder:s.workflows.namePlaceholder})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(j,{htmlFor:"text",children:s.workflows.text}),e.jsx(R,{id:"text",value:l.text,onChange:a=>i(n=>({...n,text:a.target.value})),placeholder:s.workflows.textPlaceholder,rows:6,className:"resize-none !rounded-lg"})]})]}),e.jsxs(A,{children:[e.jsx(r,{variant:"outline",onClick:()=>o(!1),children:s.common.cancel}),e.jsx(r,{onClick:C,disabled:!l.name.trim()||!l.text.trim()||w,children:w?e.jsxs(e.Fragment,{children:[e.jsx(f,{className:"w-4 h-4 mr-2 animate-spin"}),s.common.loading]}):s.common.save})]})]})})]})}export{U as component};
@@ -11,8 +11,8 @@
11
11
  <link rel="icon" type="image/png" sizes="192x192" href="/logo-192.png" />
12
12
  <link rel="icon" type="image/png" sizes="512x512" href="/logo-512.png" />
13
13
  <meta name="theme-color" content="#f5f1e8" />
14
- <script type="module" crossorigin src="/assets/index-DHF1NZh0.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-5hCCwHA7.css">
14
+ <script type="module" crossorigin src="/assets/index-UYYauTly.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-Cpv2gSF1.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="app"></div>
AutoGLM_GUI/types.py ADDED
@@ -0,0 +1,125 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal
4
+
5
+ from typing_extensions import TypedDict
6
+
7
+ ActionMetadata = Literal["do", "finish", "takeover"]
8
+ PhoneActionType = Literal[
9
+ "Tap", "Swipe", "Type", "Launch", "Wait", "Back", "Home", "Long Press", "Double Tap"
10
+ ]
11
+ MAIActionType = Literal[
12
+ "click",
13
+ "swipe",
14
+ "type",
15
+ "terminate",
16
+ "answer",
17
+ "wait",
18
+ "long_press",
19
+ "double_click",
20
+ "open",
21
+ "system_button",
22
+ "drag",
23
+ ]
24
+ SwipeDirection = Literal["up", "down", "left", "right"]
25
+ SystemButton = Literal["back", "home", "enter"]
26
+ TerminateStatus = Literal["success", "failure"]
27
+ MessageRole = Literal["system", "user", "assistant"]
28
+ ContentType = Literal["text", "image_url"]
29
+
30
+
31
+ class PhoneAgentAction(TypedDict, total=False):
32
+ _metadata: ActionMetadata
33
+ action: PhoneActionType
34
+ element: list[int]
35
+ text: str
36
+ app: str
37
+ start: list[int]
38
+ end: list[int]
39
+ duration: str
40
+ message: str
41
+
42
+
43
+ class MAIAction(TypedDict, total=False):
44
+ action: MAIActionType
45
+ coordinate: list[float]
46
+ direction: SwipeDirection
47
+ text: str
48
+ button: SystemButton
49
+ status: TerminateStatus
50
+ start_coordinate: list[float]
51
+ end_coordinate: list[float]
52
+
53
+
54
+ class SSEThinkingChunkData(TypedDict):
55
+ type: str
56
+ role: str
57
+ chunk: str
58
+
59
+
60
+ class SSEStepData(TypedDict, total=False):
61
+ type: str
62
+ role: str
63
+ step: int
64
+ thinking: str
65
+ action: PhoneAgentAction | None
66
+ success: bool
67
+ finished: bool
68
+
69
+
70
+ class SSEDoneData(TypedDict, total=False):
71
+ type: str
72
+ role: str
73
+ message: str
74
+ steps: int
75
+ success: bool
76
+
77
+
78
+ class SSEErrorData(TypedDict):
79
+ type: str
80
+ role: str
81
+ message: str
82
+
83
+
84
+ SSEEventData = SSEThinkingChunkData | SSEStepData | SSEDoneData | SSEErrorData
85
+
86
+
87
+ class MAIAgentSpecificConfig(TypedDict, total=False):
88
+ history_n: int
89
+ max_pixels: int
90
+ min_pixels: int
91
+ tools: list[dict[str, str]]
92
+ use_mai_prompt: bool
93
+
94
+
95
+ class GLMAgentSpecificConfig(TypedDict, total=False):
96
+ pass
97
+
98
+
99
+ AgentSpecificConfig = MAIAgentSpecificConfig | GLMAgentSpecificConfig
100
+
101
+
102
+ class TextContent(TypedDict):
103
+ type: ContentType
104
+ text: str
105
+
106
+
107
+ class ImageURLContent(TypedDict):
108
+ type: ContentType
109
+ image_url: dict[str, str]
110
+
111
+
112
+ MessageContent = str | list[TextContent | ImageURLContent]
113
+
114
+
115
+ class ChatMessage(TypedDict, total=False):
116
+ role: MessageRole
117
+ content: MessageContent
118
+
119
+
120
+ ConversationContext = list[ChatMessage]
121
+
122
+
123
+ class Observation(TypedDict, total=False):
124
+ screenshot: object
125
+ accessibility_tree: dict[str, object] | None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autoglm-gui
3
- Version: 1.4.0
3
+ Version: 1.4.1
4
4
  Summary: Web GUI for AutoGLM Phone Agent - AI-powered Android automation
5
5
  Project-URL: Homepage, https://github.com/suyiiyii/AutoGLM-GUI
6
6
  Project-URL: Repository, https://github.com/suyiiyii/AutoGLM-GUI
@@ -23,12 +23,15 @@ Requires-Python: >=3.10
23
23
  Requires-Dist: fastapi>=0.124.0
24
24
  Requires-Dist: fastmcp>=2.0.0
25
25
  Requires-Dist: httpx[socks]>=0.28.1
26
+ Requires-Dist: jinja2>=3.1.0
26
27
  Requires-Dist: loguru>=0.7.3
28
+ Requires-Dist: numpy>=1.24.0
27
29
  Requires-Dist: openai-agents>=0.6.4
28
30
  Requires-Dist: openai>=2.9.0
29
31
  Requires-Dist: pillow>=11.3.0
30
32
  Requires-Dist: prometheus-client>=0.21.0
31
33
  Requires-Dist: python-socketio>=5.11.0
34
+ Requires-Dist: pyyaml>=6.0.3
32
35
  Requires-Dist: uvicorn[standard]>=0.38.0
33
36
  Requires-Dist: zeroconf>=0.148.0
34
37
  Description-Content-Type: text/markdown
@@ -76,9 +79,9 @@ AutoGLM 手机助手的现代化 Web 图形界面 - 让 AI 自动化操作 Andro
76
79
 
77
80
  | 操作系统 | 下载链接 | 说明 |
78
81
  |---------|---------|------|
79
- | 🪟 **Windows** (x64) | [📦 下载便携版 EXE](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.0/AutoGLM.GUI.1.4.0.exe) | 适用于 Windows 10/11,免安装 |
80
- | 🍎 **macOS** (Apple Silicon) | [📦 下载 DMG](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.0/AutoGLM.GUI-1.4.0-arm64.dmg) | 适用于 M 芯片 Mac |
81
- | 🐧 **Linux** (x64) | [📦 下载 AppImage](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.0/AutoGLM.GUI-1.4.0.AppImage) \| [deb](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.0/autoglm-gui_1.4.0_amd64.deb) \| [tar.gz](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.0/autoglm-gui-1.4.0.tar.gz) | 通用格式,支持主流发行版 |
82
+ | 🪟 **Windows** (x64) | [📦 下载便携版 EXE](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.1/AutoGLM.GUI.1.4.1.exe) | 适用于 Windows 10/11,免安装 |
83
+ | 🍎 **macOS** (Apple Silicon) | [📦 下载 DMG](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.1/AutoGLM.GUI-1.4.1-arm64.dmg) | 适用于 M 芯片 Mac |
84
+ | 🐧 **Linux** (x64) | [📦 下载 AppImage](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.1/AutoGLM.GUI-1.4.1.AppImage) \| [deb](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.1/autoglm-gui_1.4.1_amd64.deb) \| [tar.gz](https://github.com/suyiiyii/AutoGLM-GUI/releases/download/v1.4.1/autoglm-gui-1.4.1.tar.gz) | 通用格式,支持主流发行版 |
82
85
 
83
86
  </div>
84
87
 
@@ -282,6 +285,16 @@ AutoGLM-GUI 支持同时控制多个 Android 设备:
282
285
  4. **对话** - 描述你想要做什么(例如:"去美团点一杯霸王茶姬的伯牙绝弦")
283
286
  5. **观察** - Agent 会逐步执行操作,每一步的思考过程和动作都会实时显示
284
287
 
288
+ ### 🤖 选择 Agent 类型
289
+
290
+ 在初始化设备时,可以选择不同的 Agent 类型(默认:GLM Agent):
291
+
292
+ - **GLM Agent**:基于 GLM 模型优化,成熟稳定,适合大多数任务
293
+ - **MAI Agent**:阿里通义团队开发的 Mobile Agent,支持多张历史截图上下文,适合复杂任务
294
+
295
+ MAI Agent 可配置参数:
296
+ - `history_n`:历史截图数量(1-10,默认:3)
297
+
285
298
  <a id="mode-classic"></a>
286
299
  ### 🌿 普通模式(单模型 / Open AutoGLM)
287
300
 
@@ -402,6 +415,7 @@ uv run autoglm-gui --base-url http://localhost:8080/v1 --reload
402
415
 
403
416
  # 前端开发服务器(热重载)
404
417
  cd frontend && pnpm dev
418
+ ```
405
419
 
406
420
  ### 构建和打包
407
421
 
@@ -413,6 +427,71 @@ uv run python scripts/build.py
413
427
  uv run python scripts/build.py --pack
414
428
  ```
415
429
 
430
+ ## 🐳 Docker 部署
431
+
432
+ AutoGLM-GUI 支持 Docker 容器化部署,适合服务器端远程控制 Android 设备的场景。
433
+
434
+ ### 快速启动
435
+
436
+ ```bash
437
+ # 1. 克隆仓库
438
+ git clone https://github.com/suyiiyii/AutoGLM-GUI.git
439
+ cd AutoGLM-GUI
440
+
441
+ # 2. 创建环境变量文件
442
+ cat > .env << EOF
443
+ AUTOGLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4
444
+ AUTOGLM_MODEL_NAME=autoglm-phone
445
+ AUTOGLM_API_KEY=sk-your-api-key
446
+ EOF
447
+
448
+ # 3. 启动容器
449
+ docker-compose up -d
450
+
451
+ # 4. 访问 http://localhost:8000
452
+ ```
453
+
454
+ ### 手动构建
455
+
456
+ ```bash
457
+ # 构建镜像
458
+ docker build -t autoglm-gui:latest .
459
+
460
+ # 运行容器 (Linux 推荐 host 网络)
461
+ docker run -d --network host \
462
+ -e AUTOGLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4 \
463
+ -e AUTOGLM_MODEL_NAME=autoglm-phone \
464
+ -e AUTOGLM_API_KEY=sk-xxx \
465
+ -v autoglm_config:/root/.config/autoglm \
466
+ -v autoglm_logs:/app/logs \
467
+ autoglm-gui:latest
468
+ ```
469
+
470
+ ### 环境变量
471
+
472
+ | 变量 | 说明 | 默认值 |
473
+ |------|------|--------|
474
+ | `AUTOGLM_BASE_URL` | 模型 API 地址 | (必填) |
475
+ | `AUTOGLM_MODEL_NAME` | 模型名称 | `autoglm-phone` |
476
+ | `AUTOGLM_API_KEY` | API 密钥 | (必填) |
477
+
478
+ ### 连接远程设备
479
+
480
+ Docker 容器中连接 Android 设备推荐使用 **WiFi 调试**:
481
+
482
+ 1. 在 Android 设备上开启「开发者选项」→「无线调试」
483
+ 2. 记录设备的 IP 地址和端口号
484
+ 3. 在 Web 界面点击「添加无线设备」→ 输入 IP:端口 → 连接
485
+
486
+ > ⚠️ **注意**:二维码配对功能在 Docker bridge 网络中可能受限(依赖 mDNS 多播)。Linux 系统建议使用 `network_mode: host`。
487
+
488
+ ### 健康检查
489
+
490
+ ```bash
491
+ # 检查服务状态
492
+ curl http://localhost:8000/api/health
493
+ ```
494
+
416
495
  ## 📝 开源协议
417
496
 
418
497
  Apache License 2.0
@@ -1,21 +1,23 @@
1
1
  AutoGLM_GUI/__init__.py,sha256=ic002QIj6sw9cyhh7e_60DFIb7omtPcF01-L6M4lllM,2176
2
- AutoGLM_GUI/__main__.py,sha256=ogFi2KO9kCn47ZvT5jqBzoBTnqG191TFKfEM9lcal1w,6390
3
- AutoGLM_GUI/config.py,sha256=SRBPcIKqR5HxrxiiwpOUAPxHvp-36igtFhDEptG_Zz4,619
4
- AutoGLM_GUI/config_manager.py,sha256=-y8ld-b9FVaFdsPQlw_1sxReM_Xs_IR6dZHCUbfW1J0,21143
2
+ AutoGLM_GUI/__main__.py,sha256=ygs0rleqyOdDOZBVc7eZoPOjrVmAwKptYjeWOwTtvMc,6251
3
+ AutoGLM_GUI/config_manager.py,sha256=Jy10Ka5A-c7Hq94zc3nPUXjV6uqgXW2Dqhip4TG7yz4,23603
4
+ AutoGLM_GUI/device_adapter.py,sha256=eA0bOlU-lzJig9cNCZrS0FqApEVbSOg7oke5P1iJ1rA,9011
5
5
  AutoGLM_GUI/device_manager.py,sha256=dAHP29a63H6GY43f715dOlJdwOz1eWa2XnPf2_SbIiY,26923
6
- AutoGLM_GUI/exceptions.py,sha256=dPKKuRPbsGMgUtPVZuxny8MNHswS78WzUepYtuZGZQc,3457
6
+ AutoGLM_GUI/device_protocol.py,sha256=PyjCckBz8dtU6XUSqLy2epYbIsREoA4xcfOLktYEFNo,6919
7
+ AutoGLM_GUI/exceptions.py,sha256=QXTQzxdbEHDUrtaD1G4zuKZydFJO5ymNvuo8ZvVjUdA,3487
7
8
  AutoGLM_GUI/logger.py,sha256=wLzjbRqsHAsOtI9yB0bcUZFVzgGK6qM8330std6FjVw,2553
8
- AutoGLM_GUI/metrics.py,sha256=l8KIGfiDlAccLOmk7nneacJMs3tlIdCFyXe3szodUE0,9442
9
- AutoGLM_GUI/phone_agent_manager.py,sha256=2ofFw9wdE1cs8SMo8OGbcs-J54ZGxDIOgVBYm-mT3oM,27532
10
- AutoGLM_GUI/phone_agent_patches.py,sha256=-RfpL9RHxOI2GyC2v8L49S0gXiK8l1cldEX3Ysrpl5c,4887
11
- AutoGLM_GUI/platform_utils.py,sha256=bYlQGAYYpU1ZqOUD-F-fIzpEA5z53dMFfaErsFhAvYk,2357
9
+ AutoGLM_GUI/metrics.py,sha256=L72hzMNzw1eemQLz6Hoth5sRW6OQlI4Np5BIFSFwl1c,8994
10
+ AutoGLM_GUI/phone_agent_manager.py,sha256=6ySHfglvMqXv8lLPAKoTE59Mi_8NLWjlAJ63ejQx0Ms,32573
11
+ AutoGLM_GUI/phone_agent_patches.py,sha256=xbuD7rBVPyCQ7wMb4MNW-cvSDIdtup7pJT_KvB88foM,4948
12
+ AutoGLM_GUI/platform_utils.py,sha256=fAu_3rHYVlIpDxs09eCkJBUI5_qb2_cHu1si6FuuM1U,2448
12
13
  AutoGLM_GUI/prompts.py,sha256=lC5UktW0KRpWKij6T0GG1OrNKV9gAfM6dUcp_tjX7mo,4814
13
- AutoGLM_GUI/schemas.py,sha256=n_9bCYh4U5aZXYrsgNlv85ca06wCbixxE-U6Eubq3u4,15943
14
+ AutoGLM_GUI/schemas.py,sha256=udgl1SY5c3EbklkSQKrLRu257Wf6w4IFC6S1MmN_mp0,17707
14
15
  AutoGLM_GUI/scrcpy_protocol.py,sha256=h6AtAGq3zMM30FWM7EYiYKJz7BDikpNCOjcKtFcRx3s,997
15
- AutoGLM_GUI/scrcpy_stream.py,sha256=-tGyDNaML1TnZLzTNiVUgxMTv_7TQS0CKZ35G2RuPfg,16595
16
- AutoGLM_GUI/server.py,sha256=GGGz7llG5rflIpO61nil8Zde12PLkv6OIzghuxwSkoI,249
17
- AutoGLM_GUI/socketio_server.py,sha256=__fWLlqBYFFl1YXanm8Vls-_Wr8pQ68kU7N_3q4WZfY,3535
18
- AutoGLM_GUI/state.py,sha256=ziw-t69JrojUOfzuNmVrZPc6J3-adBFpN4J3Xhq1sdU,1825
16
+ AutoGLM_GUI/scrcpy_stream.py,sha256=LScxoEXDQu-Vn3TeG5y0fJ4Z66kMVOUdCkrgYq_tTlc,16913
17
+ AutoGLM_GUI/server.py,sha256=Fo2Afl6CFT5B6MBggvR0n_savNxqxW22bFdN51NXRJs,299
18
+ AutoGLM_GUI/socketio_server.py,sha256=ekEyyuIR1CwREDhDyGhIu8wxJ9S42Cemrf4fOdRxNKs,3826
19
+ AutoGLM_GUI/state.py,sha256=qGnqZKdWFDpegVBLB4KHDbzfAVWc89C0AiCum_R9LSk,912
20
+ AutoGLM_GUI/types.py,sha256=LEheZvDfeBrR2Hh_bEwRx3x_8qNDlEV7t8-9ymzzjFE,2532
19
21
  AutoGLM_GUI/version.py,sha256=z0MPXu-k9UO7RXxVGcAwim2M3yklrRqLwgOq66zSdzo,178
20
22
  AutoGLM_GUI/workflow_manager.py,sha256=AZ_zrhSeEvtlF1HTNMNS3f4qBwRuX-bzImdZEVZVXV8,5552
21
23
  AutoGLM_GUI/adb_plus/__init__.py,sha256=YwYYbeMrsKv8BGPcD6dv0PB8-wW93Ik9pMHo02i8QoU,945
@@ -24,41 +26,50 @@ AutoGLM_GUI/adb_plus/ip.py,sha256=dt6ffIjiW_jGoEqUKUDi9fRGbWN1W9LxfT8rWIqgS-0,22
24
26
  AutoGLM_GUI/adb_plus/keyboard_installer.py,sha256=dsQqLgWd2kkTeljdTTT77mFwJpcM-IJecMcJJEutbHo,13541
25
27
  AutoGLM_GUI/adb_plus/mdns.py,sha256=AwQ4MQsPKVW1tPjIUpOWvBQGlJxvVrO9eTvxoqsgPUo,5862
26
28
  AutoGLM_GUI/adb_plus/pair.py,sha256=3UElG0yjylhmZBtPpDER2BNMZs9CwDeavjuQOwOC4sw,1945
27
- AutoGLM_GUI/adb_plus/qr_pair.py,sha256=K0Jnn5FcuQVU7cM74BTi7WdKh3UF9H85qyua9YPwIEE,13574
29
+ AutoGLM_GUI/adb_plus/qr_pair.py,sha256=WHvx76q2xfTnJPXf5y_qI9cNuiBC_KeWwqYpuROdPkw,13571
28
30
  AutoGLM_GUI/adb_plus/screenshot.py,sha256=pr7BpmUcGZO9rthpDeMRn4D1oAGX9aEEVL3ZnTQzrIo,3211
29
31
  AutoGLM_GUI/adb_plus/serial.py,sha256=lDbBXTIoOJ5lbFj50K5X7PZnfpKt1vRpFiWVjolfEhQ,3142
30
32
  AutoGLM_GUI/adb_plus/touch.py,sha256=7cyMon9nOd6zQvZdHK7BHWoRLLWkdK-qWfTuSFfFzJA,2301
31
33
  AutoGLM_GUI/adb_plus/version.py,sha256=u5u1N40izJceeORN9kFhyz0wq_hZtlwBXaiytfA3px8,2428
32
- AutoGLM_GUI/api/__init__.py,sha256=YuWTvgPs9SSkH_YKGtdx2wbfcfvJtUU0sbTU-OalGMw,3923
33
- AutoGLM_GUI/api/agents.py,sha256=JnwaBXTWj8j0P__GOLcIMJsCQ2MHXROSAg1oJUTLY34,18978
34
+ AutoGLM_GUI/agents/__init__.py,sha256=-LiMODXhqyXEcsMLOMGgnAhn8xIGokzWE4RyfZVcx_4,430
35
+ AutoGLM_GUI/agents/factory.py,sha256=3_hmikWufNBHn-eg1rLAWqe9mPBYpzTBeNDADPOMoIY,4969
36
+ AutoGLM_GUI/agents/mai_adapter.py,sha256=rUxw1LtHtKtfhLUDbQdbKWlsDMirNqAwBdpwH7wf2nY,21132
37
+ AutoGLM_GUI/agents/protocols.py,sha256=3x3xutUZpF4BFXj0kwp8ZbGA39IIMSU9ymTtDnuoBfs,559
38
+ AutoGLM_GUI/api/__init__.py,sha256=GtfuNa-G6SdeWD5_Zj99hvkXiKPYOZ3_YTysrtrGpSk,5420
39
+ AutoGLM_GUI/api/agents.py,sha256=GtAwiakLxI3fAOaFmNfYerNw-QsUFmiIVb1INAWrJS8,20661
34
40
  AutoGLM_GUI/api/control.py,sha256=G-7hfdqTl5fq-UXOwE2CI_HMbfmRH1Vd589t2FTNZyY,3155
35
- AutoGLM_GUI/api/devices.py,sha256=AzHUwFGjaj5xaHMmDylyNy0CUGZQHwlCdE3t41azqQk,11186
36
- AutoGLM_GUI/api/dual_model.py,sha256=4MERvARZX2XX46vcoMnpFcs2UKRvpZX-YLm038jtg8U,10370
37
- AutoGLM_GUI/api/layered_agent.py,sha256=qAMkdul_XnVauBlVqq8k6tAmy3VkmgLweDH1WQODdhw,23347
38
- AutoGLM_GUI/api/mcp.py,sha256=5zuG81kTGcU3niD6mmsQOjxVNN15ilsmxYpEHxyTv7M,4683
41
+ AutoGLM_GUI/api/devices.py,sha256=Z_DAPrsPo9DhZSZl29yFGF_ml6obZNQDI-p4Co9oXGI,11088
42
+ AutoGLM_GUI/api/dual_model.py,sha256=PYKM9pDcSWpRMwko0aBJ5M8ekkGd9c98YiMoPQ9Jg0k,10648
43
+ AutoGLM_GUI/api/health.py,sha256=ZedFUv-IHst_YUyeQ_MpL5JXOKuOhWVlHBVwAY3MM0I,266
44
+ AutoGLM_GUI/api/layered_agent.py,sha256=Ts8R5i11woTFHHHUlAOATb5xRVy3TY9MSwfte3iUsSI,26297
45
+ AutoGLM_GUI/api/mcp.py,sha256=ppqzBxiS_8tUeJ5P8AszH_4m6q0sNe0oT2SCU_uISks,4625
39
46
  AutoGLM_GUI/api/media.py,sha256=Ioncwd68CNkYvqmeTJJ3P5I1rjWfw6PM4WPz5Ixx8vI,1631
40
47
  AutoGLM_GUI/api/metrics.py,sha256=2h5MnUiMTRAshD6nvcarLVoSlOjtlyVAGg-LRXq03vQ,941
41
- AutoGLM_GUI/api/version.py,sha256=ZN99iH2PA_qx5A1fnSEO-Yihh4K7Bc4r1jOnP6IcXZo,5861
42
- AutoGLM_GUI/api/workflows.py,sha256=dis4k8b-xTYL99qlSYev-Qo5a7z9XdwA1xyC8fRJGLE,2360
48
+ AutoGLM_GUI/api/version.py,sha256=DYiSNNykJsxOXO1zvf3w5AcAtQJTs2c-qtA1HRVihx0,6085
49
+ AutoGLM_GUI/api/workflows.py,sha256=waYwlm2HxwkcfZwp7Bm2Z2l_bms6UmOy2xJEU9B_t0s,2431
50
+ AutoGLM_GUI/devices/__init__.py,sha256=3RHDxS3feJ1jGdWbDKeOHSFUogmexcEr9lpcXWhlnjg,1471
51
+ AutoGLM_GUI/devices/adb_device.py,sha256=9xOMQupSdtnr-wxJRcWhzfjisrwUNCrjxoi8LoMuweM,6406
52
+ AutoGLM_GUI/devices/mock_device.py,sha256=Fnf9q-tjdqUZ-jKRf2cGbguAWAQhWr3qwVcFC4E4FCo,5700
53
+ AutoGLM_GUI/devices/remote_device.py,sha256=x5MDsHwYj5YHR9KBPfjntJA8W8jpKoJwZ4vxslZFYUE,5566
43
54
  AutoGLM_GUI/dual_model/__init__.py,sha256=EQSMFAZ4NvZZywfnEJtDT9JrBJ4KuKWtGUHX4KcJIyY,1179
44
- AutoGLM_GUI/dual_model/decision_model.py,sha256=05_x2pTFdvlPU6s5RRyU44cvJFi58qtyrk6Bz-zIBto,21833
55
+ AutoGLM_GUI/dual_model/decision_model.py,sha256=4UPTFikplV1GDnKEJNxNTMEXfgB2wjnstYL-yGXaI2g,21925
45
56
  AutoGLM_GUI/dual_model/dual_agent.py,sha256=HIw6Z5Ae9UH5-JEk9IPI2ry4uodvN-qdUQSIYrU0ecc,31574
46
57
  AutoGLM_GUI/dual_model/protocols.py,sha256=CZEqXUgsCoBeq7u1Ne43R031BjojGHvsZgvZqVn7Dh0,10534
47
58
  AutoGLM_GUI/dual_model/vision_model.py,sha256=Vaj-BVWVZpedUbAVr-dl-eXUQ2IHaQpo0P1AIBuTvdg,14440
48
- AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py,sha256=82rnTo0iYcv60nHdREoPc4-xn79I0HCIfHcNO3FWHjw,9254
59
+ AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py,sha256=6VZqsPlRHthjf0Q5tJzH7RqNc-zdrRryPnKPCyyJVkY,9322
49
60
  AutoGLM_GUI/static/favicon.ico,sha256=uB4wrcENiFaF-K4V-FzNp51XLW8GC4-ujpBDHgISfGM,781
50
- AutoGLM_GUI/static/index.html,sha256=Up24pO96Iiv1riuUBhYTgVL_zk49em5DdqH3FW0hPvw,761
61
+ AutoGLM_GUI/static/index.html,sha256=8GRBiZjc-NI6PxboyyZ8QmZrGtGIrb2Y41tbWR3w8TE,761
51
62
  AutoGLM_GUI/static/logo-192.png,sha256=FOXgZxFrN0ZleO0VjlCnvD5cDkd1okJPkwPDZqsflNs,35243
52
63
  AutoGLM_GUI/static/logo-512.png,sha256=HlwSg09dQ3_Jtb09Ght4l2VK3YNLZlP_941LCdpDkWk,251598
53
- AutoGLM_GUI/static/assets/about-DeclntHg.js,sha256=QecpNFR4smtHsdMcFTKuMtw_dqJodEnHPNNOk2WK--Q,155
54
- AutoGLM_GUI/static/assets/chat-Iut2yhSw.js,sha256=YmIodM0eFchn_p43yBGS5h8Y9ArqGgC1hj1zNbFnE7E,316649
55
- AutoGLM_GUI/static/assets/dialog-BfdcBs1x.js,sha256=ALMSKReQ0IPZVzIP4hLqo8Ikkd93Vnq8KklEcKRt7A4,27378
56
- AutoGLM_GUI/static/assets/index-5hCCwHA7.css,sha256=lrUhICgckXE0lbz732Xm8oAw37J9U0fbzUsj3kKJ8O4,79591
57
- AutoGLM_GUI/static/assets/index-DHF1NZh0.js,sha256=BBguL9SX_Kelo-GWPm7xhp5bxhr4q2HAxQ9JTRRDl8I,387094
58
- AutoGLM_GUI/static/assets/index-zQ4KKDHt.js,sha256=0ke1LC-kHtUh4fwdtI9hoV9Q3D5Qaj_XnIGgDl7ooI0,229
64
+ AutoGLM_GUI/static/assets/about-_XNhzQZX.js,sha256=SmhdB_v2cKcfR7bxET7KdyIAIR7-zR2aAAPrQl-OZgo,155
65
+ AutoGLM_GUI/static/assets/chat-DwJpiAWf.js,sha256=YWM2KdefYkk3dvykyrhN1_kOt5p9aBPdDh2tL9Txs20,321638
66
+ AutoGLM_GUI/static/assets/dialog-B3uW4T8V.js,sha256=ZVyV1aFhZSgX4eNo7QXs1EgPY7zxjnzIY1jLCH491A4,27378
67
+ AutoGLM_GUI/static/assets/index-Cpv2gSF1.css,sha256=k7xMpqu3sXEnofUa3E6k2dI76d3lRRlBZbCBZml3Xos,80508
68
+ AutoGLM_GUI/static/assets/index-Cy8TmmHV.js,sha256=BOHPeP6BPgHKbLar3Q357uWM_mkYSpHXpZrOsGkXijg,229
69
+ AutoGLM_GUI/static/assets/index-UYYauTly.js,sha256=y0cmKh3RwdWH85mKmevbxyr9czFvIYGQLpdODbfOgwY,389521
59
70
  AutoGLM_GUI/static/assets/logo-Cyfm06Ym.png,sha256=6GRUFH2KNmB4ZmnrmJ1wxpoIYEmOW-__XlmKImyAMKM,4955675
60
71
  AutoGLM_GUI/static/assets/worker-D6BRitjy.js,sha256=RL1NIRIks9dXdDhXOHK1cCg-OELT0uv9a6u_UEHfQ0Q,173494
61
- AutoGLM_GUI/static/assets/workflows-xiplap-r.js,sha256=2h9sp1HE0SoXjcEwdJPYXcHKua_DHgzHGRGXkJRdhO8,3459
72
+ AutoGLM_GUI/static/assets/workflows-Du_de-dt.js,sha256=f9FeE4WAt69qNniqvi5i71iU-PbkVxzpoCrTwNYxrys,3459
62
73
  phone_agent/__init__.py,sha256=7sCmGiY-ePdb6L08MGG6DkOiu8goop6wq-v2SiM62tE,360
63
74
  phone_agent/agent.py,sha256=1SgHpv70_XbujG1ElYRZbvRO5-d4l7gBgiFRBz_FIH8,8157
64
75
  phone_agent/agent_ios.py,sha256=R7EBsoHVghEUBtI5TB7M0_btpFcb4NHquMNxktrqrJE,9338
@@ -92,9 +103,15 @@ phone_agent/xctest/connection.py,sha256=m2lT9aNZgknDzItPoA7KOiFaDVOPqx4Imdhl6GcH
92
103
  phone_agent/xctest/device.py,sha256=sOAPMoliMIabcrtsmzuGDKnZXtitqlc0lo8RSjQta7A,13655
93
104
  phone_agent/xctest/input.py,sha256=aMp1YCRGBtGsRMKhA9rrjxuLkri0_k3n6hisi0EBJ2o,8164
94
105
  phone_agent/xctest/screenshot.py,sha256=H6dsQGgf38h8dvuSeigiR2Qr8NucPanIgTK8kjlV9hA,6027
106
+ mai_agent/base.py,sha256=hYjGQXe_qguYkGq2qVWVO_xzsLzNqayA-dox_usCCM0,4546
107
+ mai_agent/mai_grounding_agent.py,sha256=d1X6giinaFVnTtKwh7oFktGZCkDjUySLb22toItykxM,8664
108
+ mai_agent/mai_naivigation_agent.py,sha256=7oA_mjxHfUEYEz2-oLdCEIImNQvAK-V4ADIMCqPPM2s,18264
109
+ mai_agent/prompt.py,sha256=Zgnj5JmVhCGjgM0sB-q19q0G76OCcNdffLypmwxRdl8,7111
110
+ mai_agent/unified_memory.py,sha256=AqEux-0I2WUIQOaY_yCRi-MDuU1BuY6dEWgClQETnEk,2194
111
+ mai_agent/utils.py,sha256=oJ50FEOM13-NwPHY4ttQ0tH49GS-w2TEy1_1FMCaxLg,2304
95
112
  scrcpy-server-v3.3.3,sha256=fnAyO6fyWWSd1KzOl6xP77roECssbZHi575hP9U1S-A,90164
96
- autoglm_gui-1.4.0.dist-info/METADATA,sha256=jbe8WQ1AAvaRtywV6FuuJi7NSnag8_5amjJmdEs1Zaw,18248
97
- autoglm_gui-1.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
98
- autoglm_gui-1.4.0.dist-info/entry_points.txt,sha256=sz4rBO_kgrYmOiT0QnhCCv0b9QqBdWyCjugJgY8AEOI,58
99
- autoglm_gui-1.4.0.dist-info/licenses/LICENSE,sha256=0IkSHDewdtmXnmYzTNq4U47EJYjTuhjQNpT0bZKuqWc,11342
100
- autoglm_gui-1.4.0.dist-info/RECORD,,
113
+ autoglm_gui-1.4.1.dist-info/METADATA,sha256=OCTjbmz6oXyvTATdzv7nuSF87wxvZnD3WHUDJhDKkK0,20387
114
+ autoglm_gui-1.4.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
115
+ autoglm_gui-1.4.1.dist-info/entry_points.txt,sha256=sz4rBO_kgrYmOiT0QnhCCv0b9QqBdWyCjugJgY8AEOI,58
116
+ autoglm_gui-1.4.1.dist-info/licenses/LICENSE,sha256=0IkSHDewdtmXnmYzTNq4U47EJYjTuhjQNpT0bZKuqWc,11342
117
+ autoglm_gui-1.4.1.dist-info/RECORD,,
mai_agent/base.py ADDED
@@ -0,0 +1,137 @@
1
+ # Copyright (c) 2025, Alibaba Cloud and its affiliates;
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ """Base agent class for mobile GUI automation agents."""
15
+
16
+ from abc import ABC, abstractmethod
17
+ from typing import Any, Dict, List, Tuple
18
+
19
+ from unified_memory import TrajMemory
20
+
21
+
22
+ class BaseAgent(ABC):
23
+ """
24
+ Abstract base class for all GUI automation agents.
25
+
26
+ This class provides common functionality for trajectory management
27
+ and defines the interface that all agents must implement.
28
+ """
29
+
30
+ def __init__(self) -> None:
31
+ """Initialize the base agent with empty trajectory memory."""
32
+ self.traj_memory = TrajMemory(
33
+ task_goal="",
34
+ task_id="",
35
+ steps=[],
36
+ )
37
+
38
+ @property
39
+ def thoughts(self) -> List[str]:
40
+ """Return list of thoughts from trajectory memory."""
41
+ return [step.thought if step.thought else "" for step in self.traj_memory.steps]
42
+
43
+ @property
44
+ def actions(self) -> List[Dict[str, Any]]:
45
+ """Return list of actions from trajectory memory."""
46
+ return [step.action for step in self.traj_memory.steps]
47
+
48
+ @property
49
+ def conclusions(self) -> List[str]:
50
+ """Return list of conclusions from trajectory memory."""
51
+ return [step.conclusion for step in self.traj_memory.steps]
52
+
53
+ @property
54
+ def observations(self) -> List[Dict[str, Any]]:
55
+ """Return list of observations from trajectory memory."""
56
+ return [
57
+ {
58
+ "screenshot": step.screenshot_bytes,
59
+ "accessibility_tree": step.accessibility_tree,
60
+ }
61
+ for step in self.traj_memory.steps
62
+ ]
63
+
64
+ @property
65
+ def history_images(self) -> List[bytes]:
66
+ """Return list of screenshot bytes from trajectory memory."""
67
+ return [step.screenshot_bytes for step in self.traj_memory.steps]
68
+
69
+ @property
70
+ def history_responses(self) -> List[str]:
71
+ """Return list of predictions from trajectory memory."""
72
+ return [step.prediction for step in self.traj_memory.steps]
73
+
74
+ @abstractmethod
75
+ def predict(
76
+ self,
77
+ instruction: str,
78
+ obs: Dict[str, Any],
79
+ **kwargs: Any,
80
+ ) -> Tuple[str, Dict[str, Any]]:
81
+ """
82
+ Predict the next action based on the current observation.
83
+
84
+ Args:
85
+ instruction: Task instruction/goal.
86
+ obs: Current observation containing screenshot and optional accessibility tree.
87
+ **kwargs: Additional keyword arguments.
88
+
89
+ Returns:
90
+ Tuple of (prediction_text, action_dict).
91
+ """
92
+ pass
93
+
94
+ def reset(self) -> None:
95
+ """Reset the trajectory memory for a new task."""
96
+ self.traj_memory = TrajMemory(
97
+ task_goal="",
98
+ task_id="",
99
+ steps=[],
100
+ )
101
+
102
+ def load_traj(self, traj_memory: TrajMemory) -> None:
103
+ """
104
+ Load trajectory from existing TrajMemory object.
105
+
106
+ Args:
107
+ traj_memory: TrajMemory object containing trajectory data.
108
+ """
109
+ self.traj_memory = traj_memory
110
+
111
+ def save_traj(self) -> Dict[str, Any]:
112
+ """
113
+ Save current trajectory to a dictionary format.
114
+
115
+ Returns:
116
+ Dictionary containing the trajectory data that can be serialized.
117
+ """
118
+ steps_data = []
119
+ for step in self.traj_memory.steps:
120
+ step_dict = {
121
+ "screenshot_bytes": step.screenshot_bytes,
122
+ "accessibility_tree": step.accessibility_tree,
123
+ "prediction": step.prediction,
124
+ "action": step.action,
125
+ "conclusion": step.conclusion,
126
+ "thought": step.thought,
127
+ "step_index": step.step_index,
128
+ "agent_type": step.agent_type,
129
+ "model_name": step.model_name,
130
+ }
131
+ steps_data.append(step_dict)
132
+
133
+ return {
134
+ "task_goal": self.traj_memory.task_goal,
135
+ "task_id": self.traj_memory.task_id,
136
+ "steps": steps_data,
137
+ }