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.
Files changed (97) hide show
  1. AutoGLM_GUI/__init__.py +1 -1
  2. AutoGLM_GUI/__main__.py +11 -2
  3. AutoGLM_GUI/adb_plus/qr_pair.py +3 -3
  4. AutoGLM_GUI/agents/__init__.py +7 -2
  5. AutoGLM_GUI/agents/factory.py +46 -6
  6. AutoGLM_GUI/agents/glm/agent.py +8 -3
  7. AutoGLM_GUI/agents/glm/async_agent.py +515 -0
  8. AutoGLM_GUI/agents/glm/parser.py +4 -2
  9. AutoGLM_GUI/agents/mai/agent.py +3 -0
  10. AutoGLM_GUI/agents/protocols.py +111 -1
  11. AutoGLM_GUI/agents/stream_runner.py +11 -7
  12. AutoGLM_GUI/api/__init__.py +3 -1
  13. AutoGLM_GUI/api/agents.py +103 -37
  14. AutoGLM_GUI/api/devices.py +72 -0
  15. AutoGLM_GUI/api/history.py +27 -1
  16. AutoGLM_GUI/api/layered_agent.py +9 -8
  17. AutoGLM_GUI/api/mcp.py +6 -4
  18. AutoGLM_GUI/config_manager.py +38 -1
  19. AutoGLM_GUI/device_manager.py +28 -4
  20. AutoGLM_GUI/device_metadata_manager.py +174 -0
  21. AutoGLM_GUI/devices/mock_device.py +8 -1
  22. AutoGLM_GUI/models/history.py +45 -1
  23. AutoGLM_GUI/phone_agent_manager.py +145 -32
  24. AutoGLM_GUI/scheduler_manager.py +52 -6
  25. AutoGLM_GUI/schemas.py +101 -0
  26. AutoGLM_GUI/scrcpy_stream.py +2 -1
  27. AutoGLM_GUI/static/assets/{about-BQm96DAl.js → about-D7r9gCvG.js} +1 -1
  28. AutoGLM_GUI/static/assets/{alert-dialog-B42XxGPR.js → alert-dialog-BKM-yRiQ.js} +1 -1
  29. AutoGLM_GUI/static/assets/chat-k6TTD7PW.js +129 -0
  30. AutoGLM_GUI/static/assets/{circle-alert-D4rSJh37.js → circle-alert-sohSDLhl.js} +1 -1
  31. AutoGLM_GUI/static/assets/{dialog-DZ78cEcj.js → dialog-BgtPh0d5.js} +1 -1
  32. AutoGLM_GUI/static/assets/eye-DLqKbQmg.js +1 -0
  33. AutoGLM_GUI/static/assets/history-Bv1lfGUU.js +1 -0
  34. AutoGLM_GUI/static/assets/index-CV7jGxGm.css +1 -0
  35. AutoGLM_GUI/static/assets/index-CxWwh1VO.js +1 -0
  36. AutoGLM_GUI/static/assets/{index-CssG-3TH.js → index-SysdKciY.js} +5 -5
  37. AutoGLM_GUI/static/assets/label-DTUnzN4B.js +1 -0
  38. AutoGLM_GUI/static/assets/{logs-eoFxn5of.js → logs-BIhnDizW.js} +1 -1
  39. AutoGLM_GUI/static/assets/{popover-DLsuV5Sx.js → popover-CikYqu2P.js} +1 -1
  40. AutoGLM_GUI/static/assets/scheduled-tasks-B-KBsGbl.js +1 -0
  41. AutoGLM_GUI/static/assets/{textarea-BX6y7uM5.js → textarea-knJZrz77.js} +1 -1
  42. AutoGLM_GUI/static/assets/workflows-DzcSYwLZ.js +1 -0
  43. AutoGLM_GUI/static/index.html +2 -2
  44. {autoglm_gui-1.5.0.dist-info → autoglm_gui-1.5.2.dist-info}/METADATA +58 -7
  45. autoglm_gui-1.5.2.dist-info/RECORD +119 -0
  46. AutoGLM_GUI/device_adapter.py +0 -263
  47. AutoGLM_GUI/static/assets/chat-C0L2gQYG.js +0 -129
  48. AutoGLM_GUI/static/assets/history-DFBv7TGc.js +0 -1
  49. AutoGLM_GUI/static/assets/index-Bzyv2yQ2.css +0 -1
  50. AutoGLM_GUI/static/assets/index-CmZSnDqc.js +0 -1
  51. AutoGLM_GUI/static/assets/label-BCUzE_nm.js +0 -1
  52. AutoGLM_GUI/static/assets/scheduled-tasks-MyqGJvy_.js +0 -1
  53. AutoGLM_GUI/static/assets/square-pen-zGWYrdfj.js +0 -1
  54. AutoGLM_GUI/static/assets/workflows-CYFs6ssC.js +0 -1
  55. autoglm_gui-1.5.0.dist-info/RECORD +0 -157
  56. mai_agent/base.py +0 -137
  57. mai_agent/mai_grounding_agent.py +0 -263
  58. mai_agent/mai_naivigation_agent.py +0 -526
  59. mai_agent/prompt.py +0 -148
  60. mai_agent/unified_memory.py +0 -67
  61. mai_agent/utils.py +0 -73
  62. phone_agent/__init__.py +0 -12
  63. phone_agent/actions/__init__.py +0 -5
  64. phone_agent/actions/handler.py +0 -400
  65. phone_agent/actions/handler_ios.py +0 -278
  66. phone_agent/adb/__init__.py +0 -51
  67. phone_agent/adb/connection.py +0 -358
  68. phone_agent/adb/device.py +0 -253
  69. phone_agent/adb/input.py +0 -108
  70. phone_agent/adb/screenshot.py +0 -108
  71. phone_agent/agent.py +0 -253
  72. phone_agent/agent_ios.py +0 -277
  73. phone_agent/config/__init__.py +0 -53
  74. phone_agent/config/apps.py +0 -227
  75. phone_agent/config/apps_harmonyos.py +0 -256
  76. phone_agent/config/apps_ios.py +0 -339
  77. phone_agent/config/i18n.py +0 -81
  78. phone_agent/config/prompts.py +0 -80
  79. phone_agent/config/prompts_en.py +0 -79
  80. phone_agent/config/prompts_zh.py +0 -82
  81. phone_agent/config/timing.py +0 -167
  82. phone_agent/device_factory.py +0 -166
  83. phone_agent/hdc/__init__.py +0 -53
  84. phone_agent/hdc/connection.py +0 -384
  85. phone_agent/hdc/device.py +0 -269
  86. phone_agent/hdc/input.py +0 -145
  87. phone_agent/hdc/screenshot.py +0 -127
  88. phone_agent/model/__init__.py +0 -5
  89. phone_agent/model/client.py +0 -290
  90. phone_agent/xctest/__init__.py +0 -47
  91. phone_agent/xctest/connection.py +0 -379
  92. phone_agent/xctest/device.py +0 -472
  93. phone_agent/xctest/input.py +0 -311
  94. phone_agent/xctest/screenshot.py +0 -226
  95. {autoglm_gui-1.5.0.dist-info → autoglm_gui-1.5.2.dist-info}/WHEEL +0 -0
  96. {autoglm_gui-1.5.0.dist-info → autoglm_gui-1.5.2.dist-info}/entry_points.txt +0 -0
  97. {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 typing.TYPE_CHECKING:
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
- is_first = self._agent.step_count == 0
104
- result = self._agent.step(self._task if is_first else None)
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(
@@ -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
- # use_agent 默认 auto_initialize=True,会自动初始化 Agent
168
+ acquired = False
169
169
  try:
170
- with manager.use_agent(device_id, timeout=None) as agent:
171
- result = agent.run(request.message)
172
- steps = agent.step_count
173
- agent.reset()
174
- return ChatResponse(result=result, steps=steps, success=True)
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
- acquired = manager.acquire_device(
218
- device_id, timeout=0, raise_on_timeout=True, auto_initialize=True
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
- agent = manager.get_agent(device_id)
223
- streamer = AgentStepStreamer(agent=agent, task=request.message)
224
-
225
- with streamer.stream_context() as abort_fn:
226
- manager.register_abort_handler(device_id, abort_fn)
227
-
228
- for event in streamer:
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
- if (
233
- event_type == AgentEventType.STEP.value
234
- and event_data_dict.get("step") == -1
235
- ):
236
- continue
237
-
238
- if event_type == AgentEventType.DONE.value:
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
- event_data = _create_sse_event(event_type, event_data_dict)
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(event_data, ensure_ascii=False)}\n\n"
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(device_id)
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
- success = manager.abort_streaming_chat(device_id)
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,
@@ -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
+ )
@@ -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 HistoryListResponse, HistoryRecordResponse
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
 
@@ -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
- # Run the agent with streaming and session for memory
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=50,
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