autoglm-gui 1.3.1__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.
- AutoGLM_GUI/__main__.py +0 -4
- AutoGLM_GUI/adb_plus/qr_pair.py +8 -8
- AutoGLM_GUI/agents/__init__.py +20 -0
- AutoGLM_GUI/agents/factory.py +160 -0
- AutoGLM_GUI/agents/mai_adapter.py +627 -0
- AutoGLM_GUI/agents/protocols.py +23 -0
- AutoGLM_GUI/api/__init__.py +50 -7
- AutoGLM_GUI/api/agents.py +61 -19
- AutoGLM_GUI/api/devices.py +12 -18
- AutoGLM_GUI/api/dual_model.py +24 -17
- AutoGLM_GUI/api/health.py +13 -0
- AutoGLM_GUI/api/layered_agent.py +659 -0
- AutoGLM_GUI/api/mcp.py +11 -10
- AutoGLM_GUI/api/version.py +23 -10
- AutoGLM_GUI/api/workflows.py +2 -1
- AutoGLM_GUI/config_manager.py +56 -24
- AutoGLM_GUI/device_adapter.py +263 -0
- AutoGLM_GUI/device_protocol.py +266 -0
- AutoGLM_GUI/devices/__init__.py +49 -0
- AutoGLM_GUI/devices/adb_device.py +205 -0
- AutoGLM_GUI/devices/mock_device.py +183 -0
- AutoGLM_GUI/devices/remote_device.py +172 -0
- AutoGLM_GUI/dual_model/decision_model.py +4 -4
- AutoGLM_GUI/dual_model/protocols.py +3 -3
- AutoGLM_GUI/exceptions.py +3 -3
- AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +291 -0
- AutoGLM_GUI/metrics.py +13 -20
- AutoGLM_GUI/phone_agent_manager.py +219 -134
- AutoGLM_GUI/phone_agent_patches.py +2 -1
- AutoGLM_GUI/platform_utils.py +5 -2
- AutoGLM_GUI/prompts.py +6 -1
- AutoGLM_GUI/schemas.py +45 -14
- AutoGLM_GUI/scrcpy_stream.py +17 -13
- AutoGLM_GUI/server.py +3 -1
- AutoGLM_GUI/socketio_server.py +16 -4
- AutoGLM_GUI/state.py +10 -30
- AutoGLM_GUI/static/assets/{about-Cj6QXqMf.js → about-_XNhzQZX.js} +1 -1
- AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +126 -0
- AutoGLM_GUI/static/assets/{dialog-CxJlnjzH.js → dialog-B3uW4T8V.js} +3 -3
- AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +1 -0
- AutoGLM_GUI/static/assets/{index-C_B-Arvf.js → index-Cy8TmmHV.js} +1 -1
- AutoGLM_GUI/static/assets/{index-CxJQuE4y.js → index-UYYauTly.js} +6 -6
- AutoGLM_GUI/static/assets/{workflows-BTiGCNI0.js → workflows-Du_de-dt.js} +1 -1
- AutoGLM_GUI/static/index.html +2 -2
- AutoGLM_GUI/types.py +125 -0
- {autoglm_gui-1.3.1.dist-info → autoglm_gui-1.4.1.dist-info}/METADATA +147 -65
- {autoglm_gui-1.3.1.dist-info → autoglm_gui-1.4.1.dist-info}/RECORD +58 -39
- mai_agent/base.py +137 -0
- mai_agent/mai_grounding_agent.py +263 -0
- mai_agent/mai_naivigation_agent.py +526 -0
- mai_agent/prompt.py +148 -0
- mai_agent/unified_memory.py +67 -0
- mai_agent/utils.py +73 -0
- phone_agent/config/prompts.py +6 -1
- phone_agent/config/prompts_zh.py +6 -1
- AutoGLM_GUI/config.py +0 -23
- AutoGLM_GUI/static/assets/chat-BJeomZgh.js +0 -124
- AutoGLM_GUI/static/assets/index-Z0uYCPOO.css +0 -1
- {autoglm_gui-1.3.1.dist-info → autoglm_gui-1.4.1.dist-info}/WHEEL +0 -0
- {autoglm_gui-1.3.1.dist-info → autoglm_gui-1.4.1.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-1.3.1.dist-info → autoglm_gui-1.4.1.dist-info}/licenses/LICENSE +0 -0
AutoGLM_GUI/schemas.py
CHANGED
|
@@ -60,6 +60,27 @@ class InitRequest(BaseModel):
|
|
|
60
60
|
model: APIModelConfig | None = Field(default=None, alias="model_config")
|
|
61
61
|
agent: APIAgentConfig | None = Field(default=None, alias="agent_config")
|
|
62
62
|
|
|
63
|
+
# Agent configuration (factory pattern)
|
|
64
|
+
agent_type: str = "glm" # Agent type to use (e.g., "glm", "mai")
|
|
65
|
+
agent_config_params: dict | None = None # Agent-specific configuration parameters
|
|
66
|
+
|
|
67
|
+
# Hot-reload support
|
|
68
|
+
force: bool = False # Force re-initialization even if agent already exists
|
|
69
|
+
|
|
70
|
+
@field_validator("agent_type")
|
|
71
|
+
@classmethod
|
|
72
|
+
def validate_agent_type(cls, v: str) -> str:
|
|
73
|
+
"""验证 agent_type 有效性."""
|
|
74
|
+
# Don't import at module level to avoid circular imports
|
|
75
|
+
from AutoGLM_GUI.agents import is_agent_type_registered
|
|
76
|
+
|
|
77
|
+
if not is_agent_type_registered(v):
|
|
78
|
+
raise ValueError(
|
|
79
|
+
f"Unknown agent_type: '{v}'. "
|
|
80
|
+
f"Please register the agent type first or use a known type."
|
|
81
|
+
)
|
|
82
|
+
return v
|
|
83
|
+
|
|
63
84
|
|
|
64
85
|
class ChatRequest(BaseModel):
|
|
65
86
|
message: str
|
|
@@ -326,8 +347,12 @@ class ConfigResponse(BaseModel):
|
|
|
326
347
|
decision_model_name: str = ""
|
|
327
348
|
decision_api_key: str = ""
|
|
328
349
|
|
|
329
|
-
#
|
|
330
|
-
|
|
350
|
+
# Agent 类型配置
|
|
351
|
+
agent_type: str = "glm" # Agent type (e.g., "glm", "mai")
|
|
352
|
+
agent_config_params: dict | None = None # Agent-specific configuration
|
|
353
|
+
|
|
354
|
+
# Agent 执行配置
|
|
355
|
+
default_max_steps: int = 100 # 单次任务最大执行步数
|
|
331
356
|
|
|
332
357
|
conflicts: list[dict] | None = None # 配置冲突信息(可选)
|
|
333
358
|
|
|
@@ -345,8 +370,24 @@ class ConfigSaveRequest(BaseModel):
|
|
|
345
370
|
decision_model_name: str | None = None
|
|
346
371
|
decision_api_key: str | None = None
|
|
347
372
|
|
|
348
|
-
#
|
|
349
|
-
|
|
373
|
+
# Agent 类型配置
|
|
374
|
+
agent_type: str = "glm" # Agent type to use (e.g., "glm", "mai")
|
|
375
|
+
agent_config_params: dict | None = None # Agent-specific configuration parameters
|
|
376
|
+
|
|
377
|
+
# Agent 执行配置
|
|
378
|
+
default_max_steps: int | None = None # 单次任务最大执行步数
|
|
379
|
+
|
|
380
|
+
@field_validator("default_max_steps")
|
|
381
|
+
@classmethod
|
|
382
|
+
def validate_default_max_steps(cls, v: int | None) -> int | None:
|
|
383
|
+
"""验证 default_max_steps 范围."""
|
|
384
|
+
if v is None:
|
|
385
|
+
return v
|
|
386
|
+
if v <= 0:
|
|
387
|
+
raise ValueError("default_max_steps must be positive")
|
|
388
|
+
if v > 1000:
|
|
389
|
+
raise ValueError("default_max_steps must be <= 1000")
|
|
390
|
+
return v
|
|
350
391
|
|
|
351
392
|
@field_validator("base_url")
|
|
352
393
|
@classmethod
|
|
@@ -367,16 +408,6 @@ class ConfigSaveRequest(BaseModel):
|
|
|
367
408
|
raise ValueError("model_name cannot be empty")
|
|
368
409
|
return v.strip()
|
|
369
410
|
|
|
370
|
-
@field_validator("thinking_mode")
|
|
371
|
-
@classmethod
|
|
372
|
-
def validate_thinking_mode(cls, v: str | None) -> str | None:
|
|
373
|
-
"""验证 thinking_mode."""
|
|
374
|
-
if v is None:
|
|
375
|
-
return v
|
|
376
|
-
if v not in ("fast", "deep"):
|
|
377
|
-
raise ValueError("thinking_mode must be 'fast' or 'deep'")
|
|
378
|
-
return v
|
|
379
|
-
|
|
380
411
|
@field_validator("decision_base_url")
|
|
381
412
|
@classmethod
|
|
382
413
|
def validate_decision_base_url(cls, v: str | None) -> str | None:
|
AutoGLM_GUI/scrcpy_stream.py
CHANGED
|
@@ -7,7 +7,7 @@ import subprocess
|
|
|
7
7
|
import sys
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from
|
|
10
|
+
from asyncio.subprocess import Process as AsyncProcess
|
|
11
11
|
|
|
12
12
|
from AutoGLM_GUI.adb_plus import check_device_available
|
|
13
13
|
from AutoGLM_GUI.logger import logger
|
|
@@ -69,7 +69,7 @@ class ScrcpyStreamer:
|
|
|
69
69
|
self.idr_interval_s = idr_interval_s
|
|
70
70
|
self.stream_options = stream_options or ScrcpyVideoStreamOptions()
|
|
71
71
|
|
|
72
|
-
self.scrcpy_process:
|
|
72
|
+
self.scrcpy_process: subprocess.Popen[bytes] | AsyncProcess | None = None
|
|
73
73
|
self.tcp_socket: socket.socket | None = None
|
|
74
74
|
self.forward_cleanup_needed = False
|
|
75
75
|
|
|
@@ -83,8 +83,9 @@ class ScrcpyStreamer:
|
|
|
83
83
|
def _find_scrcpy_server(self) -> str:
|
|
84
84
|
"""Find scrcpy-server binary path."""
|
|
85
85
|
# Priority 1: PyInstaller bundled path (for packaged executable)
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
meipass = getattr(sys, "_MEIPASS", None)
|
|
87
|
+
if meipass:
|
|
88
|
+
bundled_server = Path(meipass) / "scrcpy-server-v3.3.3"
|
|
88
89
|
if bundled_server.exists():
|
|
89
90
|
logger.info(f"Using bundled scrcpy-server: {bundled_server}")
|
|
90
91
|
return str(bundled_server)
|
|
@@ -262,14 +263,16 @@ class ScrcpyStreamer:
|
|
|
262
263
|
|
|
263
264
|
# Check if process is still running
|
|
264
265
|
error_msg = None
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
266
|
+
proc = self.scrcpy_process
|
|
267
|
+
if proc is not None:
|
|
268
|
+
if is_windows():
|
|
269
|
+
if proc.poll() is not None: # type: ignore[union-attr]
|
|
270
|
+
stdout, stderr = proc.communicate() # type: ignore[union-attr]
|
|
271
|
+
error_msg = stderr.decode() if stderr else stdout.decode()
|
|
272
|
+
else:
|
|
273
|
+
if proc.returncode is not None: # type: ignore[union-attr]
|
|
274
|
+
stdout, stderr = await proc.communicate() # type: ignore[union-attr]
|
|
275
|
+
error_msg = stderr.decode() if stderr else stdout.decode()
|
|
273
276
|
|
|
274
277
|
if error_msg is not None:
|
|
275
278
|
if "Address already in use" in error_msg:
|
|
@@ -431,7 +434,8 @@ class ScrcpyStreamer:
|
|
|
431
434
|
if self.scrcpy_process:
|
|
432
435
|
try:
|
|
433
436
|
self.scrcpy_process.terminate()
|
|
434
|
-
self.scrcpy_process.
|
|
437
|
+
if isinstance(self.scrcpy_process, subprocess.Popen):
|
|
438
|
+
self.scrcpy_process.wait(timeout=2)
|
|
435
439
|
except Exception:
|
|
436
440
|
try:
|
|
437
441
|
self.scrcpy_process.kill()
|
AutoGLM_GUI/server.py
CHANGED
|
@@ -5,6 +5,8 @@ from socketio import ASGIApp
|
|
|
5
5
|
from AutoGLM_GUI.api import app as fastapi_app
|
|
6
6
|
from AutoGLM_GUI.socketio_server import sio
|
|
7
7
|
|
|
8
|
-
app = ASGIApp(
|
|
8
|
+
app = ASGIApp(
|
|
9
|
+
other_asgi_app=fastapi_app, socketio_server=sio, socketio_path="/socket.io"
|
|
10
|
+
)
|
|
9
11
|
|
|
10
12
|
__all__ = ["app"]
|
AutoGLM_GUI/socketio_server.py
CHANGED
|
@@ -4,7 +4,9 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import time
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import NotRequired
|
|
8
|
+
|
|
9
|
+
from typing_extensions import TypedDict
|
|
8
10
|
|
|
9
11
|
import socketio
|
|
10
12
|
|
|
@@ -12,9 +14,19 @@ from AutoGLM_GUI.logger import logger
|
|
|
12
14
|
from AutoGLM_GUI.scrcpy_protocol import ScrcpyMediaStreamPacket
|
|
13
15
|
from AutoGLM_GUI.scrcpy_stream import ScrcpyStreamer
|
|
14
16
|
|
|
17
|
+
|
|
18
|
+
class VideoPacketPayload(TypedDict):
|
|
19
|
+
type: str
|
|
20
|
+
data: bytes
|
|
21
|
+
timestamp: int
|
|
22
|
+
keyframe: NotRequired[bool | None]
|
|
23
|
+
pts: NotRequired[int | None]
|
|
24
|
+
|
|
25
|
+
|
|
15
26
|
sio = socketio.AsyncServer(
|
|
16
27
|
async_mode="asgi",
|
|
17
28
|
cors_allowed_origins="*",
|
|
29
|
+
server_kwargs={"socketio_path": "/socket.io"},
|
|
18
30
|
)
|
|
19
31
|
|
|
20
32
|
_socket_streamers: dict[str, ScrcpyStreamer] = {}
|
|
@@ -64,8 +76,8 @@ async def _stream_packets(sid: str, streamer: ScrcpyStreamer) -> None:
|
|
|
64
76
|
await _stop_stream_for_sid(sid)
|
|
65
77
|
|
|
66
78
|
|
|
67
|
-
def _packet_to_payload(packet: ScrcpyMediaStreamPacket) ->
|
|
68
|
-
payload:
|
|
79
|
+
def _packet_to_payload(packet: ScrcpyMediaStreamPacket) -> VideoPacketPayload:
|
|
80
|
+
payload: VideoPacketPayload = {
|
|
69
81
|
"type": packet.type,
|
|
70
82
|
"data": packet.data,
|
|
71
83
|
"timestamp": int(time.time() * 1000),
|
|
@@ -87,7 +99,7 @@ async def disconnect(sid: str) -> None:
|
|
|
87
99
|
await _stop_stream_for_sid(sid)
|
|
88
100
|
|
|
89
101
|
|
|
90
|
-
@sio.on("connect-device")
|
|
102
|
+
@sio.on("connect-device") # type: ignore[misc]
|
|
91
103
|
async def connect_device(sid: str, data: dict | None) -> None:
|
|
92
104
|
payload = data or {}
|
|
93
105
|
device_id = payload.get("device_id") or payload.get("deviceId")
|
AutoGLM_GUI/state.py
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
"""Shared runtime state for the AutoGLM-GUI API.
|
|
1
|
+
"""Shared runtime state for the AutoGLM-GUI API.
|
|
2
|
+
|
|
3
|
+
NOTE: Agent instances and configurations are now managed internally
|
|
4
|
+
by PhoneAgentManager singleton. This module only contains:
|
|
5
|
+
- scrcpy_streamers: Video streaming state per device
|
|
6
|
+
- scrcpy_locks: Async locks for stream management
|
|
7
|
+
- non_blocking_takeover: Takeover callback handler
|
|
8
|
+
|
|
9
|
+
See PhoneAgentManager (phone_agent_manager.py) for agent lifecycle management.
|
|
10
|
+
"""
|
|
2
11
|
|
|
3
12
|
from __future__ import annotations
|
|
4
13
|
|
|
@@ -6,38 +15,9 @@ import asyncio
|
|
|
6
15
|
from typing import TYPE_CHECKING
|
|
7
16
|
|
|
8
17
|
from AutoGLM_GUI.logger import logger
|
|
9
|
-
from phone_agent.agent import AgentConfig
|
|
10
|
-
from phone_agent.model import ModelConfig
|
|
11
18
|
|
|
12
19
|
if TYPE_CHECKING:
|
|
13
20
|
from AutoGLM_GUI.scrcpy_stream import ScrcpyStreamer
|
|
14
|
-
from phone_agent import PhoneAgent
|
|
15
|
-
|
|
16
|
-
# Agent instances keyed by device_id
|
|
17
|
-
#
|
|
18
|
-
# IMPORTANT: Managed by PhoneAgentManager (AutoGLM_GUI/phone_agent_manager.py)
|
|
19
|
-
# - Do NOT directly modify these dictionaries
|
|
20
|
-
# - Use PhoneAgentManager.get_instance() for all agent operations
|
|
21
|
-
#
|
|
22
|
-
# device_id changes when connection method changes
|
|
23
|
-
# (e.g., USB "ABC123" → WiFi "192.168.1.100:5555")
|
|
24
|
-
#
|
|
25
|
-
# This means the same physical device may have different device_ids:
|
|
26
|
-
# - USB connection: device_id = hardware serial (e.g., "ABC123DEF")
|
|
27
|
-
# - WiFi connection: device_id = IP:port (e.g., "192.168.1.100:5555")
|
|
28
|
-
# - mDNS connection: device_id = service name (e.g., "adb-ABC123._adb-tls-connect._tcp")
|
|
29
|
-
#
|
|
30
|
-
# DeviceManager tracks devices by hardware serial and maintains
|
|
31
|
-
# device_id ↔ serial mapping. Use PhoneAgentManager.find_agent_by_serial()
|
|
32
|
-
# to find agents when device_id changes.
|
|
33
|
-
#
|
|
34
|
-
# See CLAUDE.md "Device Identification" section for details.
|
|
35
|
-
agents: dict[str, "PhoneAgent"] = {}
|
|
36
|
-
|
|
37
|
-
# Cached configs to rebuild agents on reset
|
|
38
|
-
# Keyed by device_id (same semantics as agents dict)
|
|
39
|
-
# IMPORTANT: Managed by PhoneAgentManager - do NOT modify directly
|
|
40
|
-
agent_configs: dict[str, tuple[ModelConfig, AgentConfig]] = {}
|
|
41
21
|
|
|
42
22
|
# Scrcpy streaming per device
|
|
43
23
|
scrcpy_streamers: dict[str, "ScrcpyStreamer"] = {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as o}from"./index-
|
|
1
|
+
import{j as o}from"./index-UYYauTly.js";function t(){return o.jsx("div",{className:"p-2",children:o.jsx("h3",{children:"About"})})}export{t as component};
|