autoglm-gui 1.5.0__py3-none-any.whl → 1.5.2__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 +1 -1
- AutoGLM_GUI/__main__.py +11 -2
- AutoGLM_GUI/adb_plus/qr_pair.py +3 -3
- AutoGLM_GUI/agents/__init__.py +7 -2
- AutoGLM_GUI/agents/factory.py +46 -6
- AutoGLM_GUI/agents/glm/agent.py +8 -3
- AutoGLM_GUI/agents/glm/async_agent.py +515 -0
- AutoGLM_GUI/agents/glm/parser.py +4 -2
- AutoGLM_GUI/agents/mai/agent.py +3 -0
- AutoGLM_GUI/agents/protocols.py +111 -1
- AutoGLM_GUI/agents/stream_runner.py +11 -7
- AutoGLM_GUI/api/__init__.py +3 -1
- AutoGLM_GUI/api/agents.py +103 -37
- AutoGLM_GUI/api/devices.py +72 -0
- AutoGLM_GUI/api/history.py +27 -1
- AutoGLM_GUI/api/layered_agent.py +9 -8
- AutoGLM_GUI/api/mcp.py +6 -4
- AutoGLM_GUI/config_manager.py +38 -1
- AutoGLM_GUI/device_manager.py +28 -4
- AutoGLM_GUI/device_metadata_manager.py +174 -0
- AutoGLM_GUI/devices/mock_device.py +8 -1
- AutoGLM_GUI/models/history.py +45 -1
- AutoGLM_GUI/phone_agent_manager.py +145 -32
- AutoGLM_GUI/scheduler_manager.py +52 -6
- AutoGLM_GUI/schemas.py +101 -0
- AutoGLM_GUI/scrcpy_stream.py +2 -1
- AutoGLM_GUI/static/assets/{about-BQm96DAl.js → about-D7r9gCvG.js} +1 -1
- AutoGLM_GUI/static/assets/{alert-dialog-B42XxGPR.js → alert-dialog-BKM-yRiQ.js} +1 -1
- AutoGLM_GUI/static/assets/chat-k6TTD7PW.js +129 -0
- AutoGLM_GUI/static/assets/{circle-alert-D4rSJh37.js → circle-alert-sohSDLhl.js} +1 -1
- AutoGLM_GUI/static/assets/{dialog-DZ78cEcj.js → dialog-BgtPh0d5.js} +1 -1
- AutoGLM_GUI/static/assets/eye-DLqKbQmg.js +1 -0
- AutoGLM_GUI/static/assets/history-Bv1lfGUU.js +1 -0
- AutoGLM_GUI/static/assets/index-CV7jGxGm.css +1 -0
- AutoGLM_GUI/static/assets/index-CxWwh1VO.js +1 -0
- AutoGLM_GUI/static/assets/{index-CssG-3TH.js → index-SysdKciY.js} +5 -5
- AutoGLM_GUI/static/assets/label-DTUnzN4B.js +1 -0
- AutoGLM_GUI/static/assets/{logs-eoFxn5of.js → logs-BIhnDizW.js} +1 -1
- AutoGLM_GUI/static/assets/{popover-DLsuV5Sx.js → popover-CikYqu2P.js} +1 -1
- AutoGLM_GUI/static/assets/scheduled-tasks-B-KBsGbl.js +1 -0
- AutoGLM_GUI/static/assets/{textarea-BX6y7uM5.js → textarea-knJZrz77.js} +1 -1
- AutoGLM_GUI/static/assets/workflows-DzcSYwLZ.js +1 -0
- AutoGLM_GUI/static/index.html +2 -2
- {autoglm_gui-1.5.0.dist-info → autoglm_gui-1.5.2.dist-info}/METADATA +58 -7
- autoglm_gui-1.5.2.dist-info/RECORD +119 -0
- AutoGLM_GUI/device_adapter.py +0 -263
- AutoGLM_GUI/static/assets/chat-C0L2gQYG.js +0 -129
- AutoGLM_GUI/static/assets/history-DFBv7TGc.js +0 -1
- AutoGLM_GUI/static/assets/index-Bzyv2yQ2.css +0 -1
- AutoGLM_GUI/static/assets/index-CmZSnDqc.js +0 -1
- AutoGLM_GUI/static/assets/label-BCUzE_nm.js +0 -1
- AutoGLM_GUI/static/assets/scheduled-tasks-MyqGJvy_.js +0 -1
- AutoGLM_GUI/static/assets/square-pen-zGWYrdfj.js +0 -1
- AutoGLM_GUI/static/assets/workflows-CYFs6ssC.js +0 -1
- autoglm_gui-1.5.0.dist-info/RECORD +0 -157
- 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/actions/handler_ios.py +0 -278
- phone_agent/adb/__init__.py +0 -51
- phone_agent/adb/connection.py +0 -358
- phone_agent/adb/device.py +0 -253
- phone_agent/adb/input.py +0 -108
- 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.py +0 -227
- phone_agent/config/apps_harmonyos.py +0 -256
- phone_agent/config/apps_ios.py +0 -339
- phone_agent/config/i18n.py +0 -81
- phone_agent/config/prompts.py +0 -80
- phone_agent/config/prompts_en.py +0 -79
- phone_agent/config/prompts_zh.py +0 -82
- phone_agent/config/timing.py +0 -167
- 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
- {autoglm_gui-1.5.0.dist-info → autoglm_gui-1.5.2.dist-info}/WHEEL +0 -0
- {autoglm_gui-1.5.0.dist-info → autoglm_gui-1.5.2.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-1.5.0.dist-info → autoglm_gui-1.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import queue
|
|
2
2
|
import threading
|
|
3
|
-
import typing
|
|
4
3
|
from contextlib import contextmanager
|
|
5
|
-
from typing import Any, Callable, Iterator, Optional
|
|
4
|
+
from typing import Any, Callable, Iterator, Optional, TYPE_CHECKING
|
|
6
5
|
|
|
7
6
|
from AutoGLM_GUI.agents.events import AgentEvent, AgentEventType
|
|
8
7
|
|
|
9
|
-
if
|
|
8
|
+
if TYPE_CHECKING:
|
|
10
9
|
from AutoGLM_GUI.agents.protocols import BaseAgent
|
|
11
10
|
|
|
12
11
|
|
|
@@ -76,7 +75,7 @@ class AgentStepStreamer:
|
|
|
76
75
|
def _start_worker(self) -> None:
|
|
77
76
|
"""启动 worker 线程."""
|
|
78
77
|
|
|
79
|
-
def worker():
|
|
78
|
+
def worker() -> None:
|
|
80
79
|
try:
|
|
81
80
|
# 检查停止事件
|
|
82
81
|
if self._stop_event.is_set():
|
|
@@ -87,7 +86,7 @@ class AgentStepStreamer:
|
|
|
87
86
|
# 假设 agent 有 _thinking_callback 属性
|
|
88
87
|
original_callback = getattr(self._agent, "_thinking_callback", None)
|
|
89
88
|
|
|
90
|
-
def on_thinking(chunk: str):
|
|
89
|
+
def on_thinking(chunk: str) -> None:
|
|
91
90
|
self._event_queue.put(
|
|
92
91
|
(AgentEventType.THINKING.value, {"chunk": chunk})
|
|
93
92
|
)
|
|
@@ -99,9 +98,14 @@ class AgentStepStreamer:
|
|
|
99
98
|
|
|
100
99
|
try:
|
|
101
100
|
# 执行 step 循环
|
|
101
|
+
# 使用会话级别的标记,而不是 agent.step_count
|
|
102
|
+
# 这样每次新对话开始时,第一步都会传递 task
|
|
103
|
+
is_first_in_session = True
|
|
102
104
|
while not self._stop_event.is_set():
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
result = self._agent.step(
|
|
106
|
+
self._task if is_first_in_session else None
|
|
107
|
+
)
|
|
108
|
+
is_first_in_session = False
|
|
105
109
|
|
|
106
110
|
# 发射 step 事件
|
|
107
111
|
self._event_queue.put(
|
AutoGLM_GUI/api/__init__.py
CHANGED
|
@@ -7,6 +7,8 @@ from contextlib import asynccontextmanager
|
|
|
7
7
|
from importlib.resources import files
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
from typing import AsyncGenerator
|
|
11
|
+
|
|
10
12
|
from fastapi import FastAPI
|
|
11
13
|
from fastapi.middleware.cors import CORSMiddleware
|
|
12
14
|
from fastapi.responses import FileResponse
|
|
@@ -82,7 +84,7 @@ def create_app() -> FastAPI:
|
|
|
82
84
|
|
|
83
85
|
# Define combined lifespan
|
|
84
86
|
@asynccontextmanager
|
|
85
|
-
async def combined_lifespan(app: FastAPI):
|
|
87
|
+
async def combined_lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
86
88
|
"""Combine app startup logic with MCP lifespan."""
|
|
87
89
|
# App startup
|
|
88
90
|
asyncio.create_task(qr_pairing_manager.cleanup_expired_sessions())
|
AutoGLM_GUI/api/agents.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Agent lifecycle and chat routes."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import json
|
|
4
5
|
|
|
5
6
|
from fastapi import APIRouter, HTTPException
|
|
6
7
|
from fastapi.responses import StreamingResponse
|
|
7
8
|
from pydantic import ValidationError
|
|
8
9
|
|
|
9
|
-
from AutoGLM_GUI.agents.events import AgentEventType
|
|
10
10
|
from AutoGLM_GUI.config import AgentConfig, ModelConfig
|
|
11
11
|
from AutoGLM_GUI.logger import logger
|
|
12
12
|
from AutoGLM_GUI.schemas import (
|
|
@@ -154,8 +154,8 @@ def init_agent(request: InitRequest) -> dict:
|
|
|
154
154
|
|
|
155
155
|
|
|
156
156
|
@router.post("/api/chat", response_model=ChatResponse)
|
|
157
|
-
def chat(request: ChatRequest) -> ChatResponse:
|
|
158
|
-
"""发送任务给 Agent
|
|
157
|
+
async def chat(request: ChatRequest) -> ChatResponse:
|
|
158
|
+
"""发送任务给 Agent 并执行(支持 AsyncAgent)。
|
|
159
159
|
|
|
160
160
|
Agent 会在首次使用时自动初始化,无需手动调用 /api/init。
|
|
161
161
|
"""
|
|
@@ -165,15 +165,27 @@ def chat(request: ChatRequest) -> ChatResponse:
|
|
|
165
165
|
device_id = request.device_id
|
|
166
166
|
manager = PhoneAgentManager.get_instance()
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
acquired = False
|
|
169
169
|
try:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
acquired = await asyncio.to_thread(
|
|
171
|
+
manager.acquire_device, device_id, timeout=None, auto_initialize=False
|
|
172
|
+
)
|
|
173
|
+
# Use chat context with async agent
|
|
174
|
+
agent = await asyncio.to_thread(
|
|
175
|
+
manager.get_agent_with_context,
|
|
176
|
+
device_id,
|
|
177
|
+
context="chat",
|
|
178
|
+
agent_type="glm-async",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# AsyncAgent is always used for chat context
|
|
182
|
+
result = await agent.run(request.message) # type: ignore[misc]
|
|
183
|
+
|
|
184
|
+
steps = agent.step_count
|
|
185
|
+
agent.reset()
|
|
186
|
+
return ChatResponse(result=result, steps=steps, success=True) # type: ignore[arg-type]
|
|
187
|
+
|
|
175
188
|
except AgentInitializationError as e:
|
|
176
|
-
# 配置错误或初始化失败
|
|
177
189
|
logger.error(f"Failed to initialize agent for {device_id}: {e}")
|
|
178
190
|
raise HTTPException(
|
|
179
191
|
status_code=500,
|
|
@@ -186,68 +198,118 @@ def chat(request: ChatRequest) -> ChatResponse:
|
|
|
186
198
|
except Exception as e:
|
|
187
199
|
logger.exception(f"Unexpected error in chat for {device_id}")
|
|
188
200
|
return ChatResponse(result=str(e), steps=0, success=False)
|
|
201
|
+
finally:
|
|
202
|
+
if acquired:
|
|
203
|
+
await asyncio.to_thread(manager.release_device, device_id)
|
|
189
204
|
|
|
190
205
|
|
|
191
206
|
@router.post("/api/chat/stream")
|
|
192
|
-
def chat_stream(request: ChatRequest):
|
|
207
|
+
async def chat_stream(request: ChatRequest):
|
|
193
208
|
"""发送任务给 Agent 并实时推送执行进度(SSE,多设备支持)。
|
|
194
209
|
|
|
195
210
|
Agent 会在首次使用时自动初始化,无需手动调用 /api/init。
|
|
211
|
+
|
|
212
|
+
Chat API 使用 AsyncAgent 实现原生 async streaming 和立即取消。
|
|
196
213
|
"""
|
|
197
214
|
from datetime import datetime
|
|
198
215
|
|
|
199
|
-
from AutoGLM_GUI.agents.stream_runner import AgentStepStreamer
|
|
200
216
|
from AutoGLM_GUI.device_manager import DeviceManager
|
|
201
217
|
from AutoGLM_GUI.exceptions import AgentInitializationError, DeviceBusyError
|
|
202
218
|
from AutoGLM_GUI.history_manager import history_manager
|
|
203
|
-
from AutoGLM_GUI.models.history import ConversationRecord
|
|
219
|
+
from AutoGLM_GUI.models.history import ConversationRecord, MessageRecord
|
|
204
220
|
from AutoGLM_GUI.phone_agent_manager import PhoneAgentManager
|
|
205
221
|
|
|
206
222
|
device_id = request.device_id
|
|
207
223
|
manager = PhoneAgentManager.get_instance()
|
|
208
224
|
|
|
209
|
-
def event_generator():
|
|
225
|
+
async def event_generator():
|
|
210
226
|
acquired = False
|
|
211
227
|
start_time = datetime.now()
|
|
212
228
|
final_message = ""
|
|
213
229
|
final_success = False
|
|
214
230
|
final_steps = 0
|
|
215
231
|
|
|
232
|
+
# 收集完整对话消息
|
|
233
|
+
messages: list[MessageRecord] = []
|
|
234
|
+
# 添加用户消息
|
|
235
|
+
messages.append(
|
|
236
|
+
MessageRecord(
|
|
237
|
+
role="user",
|
|
238
|
+
content=request.message,
|
|
239
|
+
timestamp=start_time,
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
|
|
216
243
|
try:
|
|
217
|
-
|
|
218
|
-
|
|
244
|
+
# 获取设备锁(在线程池中执行)
|
|
245
|
+
acquired = await asyncio.to_thread(
|
|
246
|
+
manager.acquire_device,
|
|
247
|
+
device_id,
|
|
248
|
+
timeout=0,
|
|
249
|
+
raise_on_timeout=True,
|
|
250
|
+
auto_initialize=False,
|
|
219
251
|
)
|
|
220
252
|
|
|
221
253
|
try:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
254
|
+
# 使用 chat context 获取 AsyncAgent
|
|
255
|
+
agent = await asyncio.to_thread(
|
|
256
|
+
manager.get_agent_with_context,
|
|
257
|
+
device_id,
|
|
258
|
+
context="chat",
|
|
259
|
+
agent_type="glm-async",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
logger.info(f"Using AsyncAgent for device {device_id}")
|
|
263
|
+
|
|
264
|
+
# 注册异步取消处理器
|
|
265
|
+
async def cancel_handler():
|
|
266
|
+
await agent.cancel() # type: ignore[union-attr]
|
|
267
|
+
|
|
268
|
+
await asyncio.to_thread(
|
|
269
|
+
manager.register_abort_handler, device_id, cancel_handler
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
# 直接使用 agent.stream()
|
|
274
|
+
async for event in agent.stream(request.message): # type: ignore[union-attr]
|
|
229
275
|
event_type = event["type"]
|
|
230
276
|
event_data_dict = event["data"]
|
|
231
277
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
278
|
+
# 收集每个 step 的消息
|
|
279
|
+
if event_type == "step":
|
|
280
|
+
messages.append(
|
|
281
|
+
MessageRecord(
|
|
282
|
+
role="assistant",
|
|
283
|
+
content="",
|
|
284
|
+
timestamp=datetime.now(),
|
|
285
|
+
thinking=event_data_dict.get("thinking"),
|
|
286
|
+
action=event_data_dict.get("action"),
|
|
287
|
+
step=event_data_dict.get("step"),
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
if event_type == "done":
|
|
239
292
|
final_message = event_data_dict.get("message", "")
|
|
240
293
|
final_success = event_data_dict.get("success", False)
|
|
241
294
|
final_steps = event_data_dict.get("steps", 0)
|
|
242
295
|
|
|
243
|
-
|
|
244
|
-
|
|
296
|
+
# 发送 SSE 事件
|
|
297
|
+
sse_event = _create_sse_event(event_type, event_data_dict)
|
|
245
298
|
yield f"event: {event_type}\n"
|
|
246
|
-
yield f"data: {json.dumps(
|
|
299
|
+
yield f"data: {json.dumps(sse_event, ensure_ascii=False)}\n\n"
|
|
300
|
+
|
|
301
|
+
except asyncio.CancelledError:
|
|
302
|
+
logger.info(f"AsyncAgent task cancelled for device {device_id}")
|
|
303
|
+
yield "event: cancelled\n"
|
|
304
|
+
yield f"data: {json.dumps({'message': 'Task cancelled by user'})}\n\n"
|
|
305
|
+
raise
|
|
306
|
+
|
|
307
|
+
finally:
|
|
308
|
+
await asyncio.to_thread(manager.unregister_abort_handler, device_id)
|
|
247
309
|
|
|
248
310
|
finally:
|
|
249
311
|
if acquired:
|
|
250
|
-
manager.release_device
|
|
312
|
+
await asyncio.to_thread(manager.release_device, device_id)
|
|
251
313
|
|
|
252
314
|
device_manager = DeviceManager.get_instance()
|
|
253
315
|
serialno = device_manager.get_serial_by_device_id(device_id)
|
|
@@ -263,6 +325,7 @@ def chat_stream(request: ChatRequest):
|
|
|
263
325
|
duration_ms=int((end_time - start_time).total_seconds() * 1000),
|
|
264
326
|
source="chat",
|
|
265
327
|
error_message=None if final_success else final_message,
|
|
328
|
+
messages=messages,
|
|
266
329
|
)
|
|
267
330
|
history_manager.add_record(serialno, record)
|
|
268
331
|
|
|
@@ -350,14 +413,15 @@ def reset_agent(request: ResetRequest) -> dict:
|
|
|
350
413
|
|
|
351
414
|
|
|
352
415
|
@router.post("/api/chat/abort")
|
|
353
|
-
def abort_chat(request: AbortRequest) -> dict:
|
|
354
|
-
"""
|
|
416
|
+
async def abort_chat(request: AbortRequest) -> dict:
|
|
417
|
+
"""中断正在进行的对话流 (支持 AsyncAgent)。"""
|
|
355
418
|
from AutoGLM_GUI.phone_agent_manager import PhoneAgentManager
|
|
356
419
|
|
|
357
420
|
device_id = request.device_id
|
|
358
421
|
manager = PhoneAgentManager.get_instance()
|
|
359
422
|
|
|
360
|
-
|
|
423
|
+
# 使用异步方法 (支持 AsyncAgent 和 BaseAgent)
|
|
424
|
+
success = await manager.abort_streaming_chat_async(device_id)
|
|
361
425
|
|
|
362
426
|
return {
|
|
363
427
|
"success": success,
|
|
@@ -388,6 +452,7 @@ def get_config_endpoint() -> ConfigResponse:
|
|
|
388
452
|
agent_type=effective_config.agent_type,
|
|
389
453
|
agent_config_params=effective_config.agent_config_params,
|
|
390
454
|
default_max_steps=effective_config.default_max_steps,
|
|
455
|
+
layered_max_turns=effective_config.layered_max_turns,
|
|
391
456
|
decision_base_url=effective_config.decision_base_url,
|
|
392
457
|
decision_model_name=effective_config.decision_model_name,
|
|
393
458
|
decision_api_key=effective_config.decision_api_key,
|
|
@@ -431,6 +496,7 @@ def save_config_endpoint(request: ConfigSaveRequest) -> dict:
|
|
|
431
496
|
agent_type=request.agent_type,
|
|
432
497
|
agent_config_params=request.agent_config_params,
|
|
433
498
|
default_max_steps=request.default_max_steps,
|
|
499
|
+
layered_max_turns=request.layered_max_turns,
|
|
434
500
|
decision_base_url=request.decision_base_url,
|
|
435
501
|
decision_model_name=request.decision_model_name,
|
|
436
502
|
decision_api_key=request.decision_api_key,
|
AutoGLM_GUI/api/devices.py
CHANGED
|
@@ -15,6 +15,8 @@ from AutoGLM_GUI.logger import logger
|
|
|
15
15
|
|
|
16
16
|
from AutoGLM_GUI.schemas import (
|
|
17
17
|
DeviceListResponse,
|
|
18
|
+
DeviceNameResponse,
|
|
19
|
+
DeviceNameUpdateRequest,
|
|
18
20
|
DeviceResponse,
|
|
19
21
|
MdnsDeviceResponse,
|
|
20
22
|
MdnsDiscoverResponse,
|
|
@@ -454,3 +456,73 @@ def remove_remote_device(
|
|
|
454
456
|
message=message,
|
|
455
457
|
error=None if success else "remove_failed",
|
|
456
458
|
)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@router.put("/api/devices/{serial}/name", response_model=DeviceNameResponse)
|
|
462
|
+
def update_device_name(
|
|
463
|
+
serial: str, request: DeviceNameUpdateRequest
|
|
464
|
+
) -> DeviceNameResponse:
|
|
465
|
+
"""Update or clear device display name.
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
serial: Device hardware serial number
|
|
469
|
+
request: Contains display_name (str or None to clear)
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
DeviceNameResponse with updated name or error
|
|
473
|
+
"""
|
|
474
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
device_manager = DeviceManager.get_instance()
|
|
478
|
+
device_manager.set_device_display_name(serial, request.display_name)
|
|
479
|
+
|
|
480
|
+
return DeviceNameResponse(
|
|
481
|
+
success=True,
|
|
482
|
+
serial=serial,
|
|
483
|
+
display_name=request.display_name,
|
|
484
|
+
)
|
|
485
|
+
except ValueError as e:
|
|
486
|
+
logger.warning(f"Failed to update device name for {serial}: {e}")
|
|
487
|
+
return DeviceNameResponse(
|
|
488
|
+
success=False,
|
|
489
|
+
serial=serial,
|
|
490
|
+
error=str(e),
|
|
491
|
+
)
|
|
492
|
+
except Exception as e:
|
|
493
|
+
logger.exception(f"Unexpected error updating device name for {serial}")
|
|
494
|
+
return DeviceNameResponse(
|
|
495
|
+
success=False,
|
|
496
|
+
serial=serial,
|
|
497
|
+
error=f"Internal error: {str(e)}",
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
@router.get("/api/devices/{serial}/name", response_model=DeviceNameResponse)
|
|
502
|
+
def get_device_name(serial: str) -> DeviceNameResponse:
|
|
503
|
+
"""Get device display name.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
serial: Device hardware serial number
|
|
507
|
+
|
|
508
|
+
Returns:
|
|
509
|
+
DeviceNameResponse with current display name or None if not set
|
|
510
|
+
"""
|
|
511
|
+
from AutoGLM_GUI.device_manager import DeviceManager
|
|
512
|
+
|
|
513
|
+
try:
|
|
514
|
+
device_manager = DeviceManager.get_instance()
|
|
515
|
+
display_name = device_manager.get_device_display_name(serial)
|
|
516
|
+
|
|
517
|
+
return DeviceNameResponse(
|
|
518
|
+
success=True,
|
|
519
|
+
serial=serial,
|
|
520
|
+
display_name=display_name,
|
|
521
|
+
)
|
|
522
|
+
except Exception as e:
|
|
523
|
+
logger.exception(f"Unexpected error getting device name for {serial}")
|
|
524
|
+
return DeviceNameResponse(
|
|
525
|
+
success=False,
|
|
526
|
+
serial=serial,
|
|
527
|
+
error=f"Internal error: {str(e)}",
|
|
528
|
+
)
|
AutoGLM_GUI/api/history.py
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
from fastapi import APIRouter, HTTPException
|
|
4
4
|
|
|
5
5
|
from AutoGLM_GUI.history_manager import history_manager
|
|
6
|
-
from AutoGLM_GUI.schemas import
|
|
6
|
+
from AutoGLM_GUI.schemas import (
|
|
7
|
+
HistoryListResponse,
|
|
8
|
+
HistoryRecordResponse,
|
|
9
|
+
MessageRecordResponse,
|
|
10
|
+
)
|
|
7
11
|
|
|
8
12
|
router = APIRouter()
|
|
9
13
|
|
|
@@ -34,6 +38,17 @@ def list_history(
|
|
|
34
38
|
source=r.source,
|
|
35
39
|
source_detail=r.source_detail,
|
|
36
40
|
error_message=r.error_message,
|
|
41
|
+
messages=[
|
|
42
|
+
MessageRecordResponse(
|
|
43
|
+
role=m.role,
|
|
44
|
+
content=m.content,
|
|
45
|
+
timestamp=m.timestamp.isoformat(),
|
|
46
|
+
thinking=m.thinking,
|
|
47
|
+
action=m.action,
|
|
48
|
+
step=m.step,
|
|
49
|
+
)
|
|
50
|
+
for m in r.messages
|
|
51
|
+
],
|
|
37
52
|
)
|
|
38
53
|
for r in records
|
|
39
54
|
],
|
|
@@ -61,6 +76,17 @@ def get_history_record(serialno: str, record_id: str) -> HistoryRecordResponse:
|
|
|
61
76
|
source=record.source,
|
|
62
77
|
source_detail=record.source_detail,
|
|
63
78
|
error_message=record.error_message,
|
|
79
|
+
messages=[
|
|
80
|
+
MessageRecordResponse(
|
|
81
|
+
role=m.role,
|
|
82
|
+
content=m.content,
|
|
83
|
+
timestamp=m.timestamp.isoformat(),
|
|
84
|
+
thinking=m.thinking,
|
|
85
|
+
action=m.action,
|
|
86
|
+
step=m.step,
|
|
87
|
+
)
|
|
88
|
+
for m in record.messages
|
|
89
|
+
],
|
|
64
90
|
)
|
|
65
91
|
|
|
66
92
|
|
AutoGLM_GUI/api/layered_agent.py
CHANGED
|
@@ -7,7 +7,7 @@ a decision model for planning and autoglm-phone for execution.
|
|
|
7
7
|
import asyncio
|
|
8
8
|
import json
|
|
9
9
|
import threading
|
|
10
|
-
from typing import TYPE_CHECKING, Any
|
|
10
|
+
from typing import TYPE_CHECKING, Any, AsyncGenerator
|
|
11
11
|
|
|
12
12
|
from agents import Agent, Runner, SQLiteSession, function_tool
|
|
13
13
|
|
|
@@ -216,7 +216,7 @@ def _sync_chat(device_id: str, message: str) -> str:
|
|
|
216
216
|
# 重置 agent 确保干净状态
|
|
217
217
|
agent.reset()
|
|
218
218
|
|
|
219
|
-
result = agent.run(message)
|
|
219
|
+
result = agent.run(message) # type: ignore[misc]
|
|
220
220
|
steps = agent.step_count
|
|
221
221
|
|
|
222
222
|
# 检查是否达到步数限制
|
|
@@ -384,7 +384,7 @@ class LayeredAgentRequest(BaseModel):
|
|
|
384
384
|
|
|
385
385
|
|
|
386
386
|
@router.post("/api/layered-agent/chat")
|
|
387
|
-
async def layered_agent_chat(request: LayeredAgentRequest):
|
|
387
|
+
async def layered_agent_chat(request: LayeredAgentRequest) -> StreamingResponse:
|
|
388
388
|
"""
|
|
389
389
|
Layered agent chat API with streaming execution steps.
|
|
390
390
|
|
|
@@ -407,7 +407,7 @@ async def layered_agent_chat(request: LayeredAgentRequest):
|
|
|
407
407
|
from AutoGLM_GUI.history_manager import history_manager
|
|
408
408
|
from AutoGLM_GUI.models.history import ConversationRecord
|
|
409
409
|
|
|
410
|
-
async def event_generator():
|
|
410
|
+
async def event_generator() -> AsyncGenerator[str, None]:
|
|
411
411
|
start_time = datetime.now()
|
|
412
412
|
final_output = ""
|
|
413
413
|
final_success = False
|
|
@@ -418,11 +418,12 @@ async def layered_agent_chat(request: LayeredAgentRequest):
|
|
|
418
418
|
session_id = request.session_id or request.device_id or "default"
|
|
419
419
|
session = _get_or_create_session(session_id)
|
|
420
420
|
|
|
421
|
-
|
|
421
|
+
effective_config = config_manager.get_effective_config()
|
|
422
|
+
|
|
422
423
|
result = Runner.run_streamed(
|
|
423
424
|
agent,
|
|
424
425
|
request.message,
|
|
425
|
-
max_turns=
|
|
426
|
+
max_turns=effective_config.layered_max_turns,
|
|
426
427
|
session=session,
|
|
427
428
|
)
|
|
428
429
|
|
|
@@ -663,7 +664,7 @@ class AbortSessionRequest(BaseModel):
|
|
|
663
664
|
|
|
664
665
|
|
|
665
666
|
@router.post("/api/layered-agent/abort")
|
|
666
|
-
def abort_session(request: AbortSessionRequest):
|
|
667
|
+
def abort_session(request: AbortSessionRequest) -> dict[str, Any]:
|
|
667
668
|
"""
|
|
668
669
|
Abort a running layered agent session.
|
|
669
670
|
|
|
@@ -697,7 +698,7 @@ class ResetSessionRequest(BaseModel):
|
|
|
697
698
|
|
|
698
699
|
|
|
699
700
|
@router.post("/api/layered-agent/reset")
|
|
700
|
-
def reset_session(request: ResetSessionRequest):
|
|
701
|
+
def reset_session(request: ResetSessionRequest) -> dict[str, Any]:
|
|
701
702
|
"""
|
|
702
703
|
Reset/clear a session to forget conversation history.
|
|
703
704
|
|
AutoGLM_GUI/api/mcp.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""MCP (Model Context Protocol) tools for AutoGLM-GUI."""
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
3
5
|
from typing_extensions import TypedDict
|
|
4
6
|
|
|
5
7
|
from fastmcp import FastMCP
|
|
@@ -56,12 +58,12 @@ def chat(device_id: str, message: str) -> ChatResult:
|
|
|
56
58
|
# Reset agent before each chat to ensure clean state
|
|
57
59
|
agent.reset()
|
|
58
60
|
|
|
59
|
-
result = agent.run(message)
|
|
61
|
+
result = agent.run(message) # type: ignore[misc]
|
|
60
62
|
steps = agent.step_count
|
|
61
63
|
|
|
62
64
|
# Check if MCP step limit was reached
|
|
63
65
|
if steps >= MCP_MAX_STEPS and result == "Max steps reached":
|
|
64
|
-
return {
|
|
66
|
+
return { # type: ignore[return-value]
|
|
65
67
|
"result": (
|
|
66
68
|
f"已达到 MCP 最大步数限制({MCP_MAX_STEPS}步)。任务可能未完成,"
|
|
67
69
|
"建议将任务拆分为更小的子任务。"
|
|
@@ -70,7 +72,7 @@ def chat(device_id: str, message: str) -> ChatResult:
|
|
|
70
72
|
"success": False,
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
return {"result": result, "steps": steps, "success": True}
|
|
75
|
+
return {"result": result, "steps": steps, "success": True} # type: ignore[return-value]
|
|
74
76
|
|
|
75
77
|
finally:
|
|
76
78
|
# Restore original config
|
|
@@ -123,7 +125,7 @@ def list_devices() -> list[DeviceResponse]:
|
|
|
123
125
|
return devices_with_agents
|
|
124
126
|
|
|
125
127
|
|
|
126
|
-
def get_mcp_asgi_app():
|
|
128
|
+
def get_mcp_asgi_app() -> Any:
|
|
127
129
|
"""
|
|
128
130
|
Get the MCP server's ASGI app for mounting in FastAPI.
|
|
129
131
|
|