Files
Open-AutoGLM/dashboard/api/tasks.py
let5sne.win10 3552df23d6 Add Web Dashboard with multi-device control and callback hooks
Features:
- Web Dashboard: FastAPI-based dashboard with Vue.js frontend
  - Multi-device support (ADB, HDC, iOS)
  - Real-time WebSocket updates for task progress
  - Device management with status tracking
  - Task queue with execution controls (start/stop/re-execute)
  - Detailed task information display (thinking, actions, completion messages)
  - Screenshot viewing per device
  - LAN deployment support with configurable CORS

- Callback Hooks: Interrupt and modify task execution
  - step_callback: Called after each step with StepResult
  - before_action_callback: Called before executing action
  - Support for task interruption and dynamic task switching
  - Example scripts demonstrating callback usage

- Configuration: Environment-based configuration
  - .env file support for all settings
  - .env.example template with documentation
  - Model API configuration (base URL, model name, API key)
  - Dashboard configuration (host, port, CORS, device type)
  - Phone agent configuration (delays, max steps, language)

Technical improvements:
- Fixed forward reference issue with StepResult
- Added package exports for callback types and configs
- Enhanced dependencies with FastAPI, WebSocket support
- Thread-safe task execution with device locking
- Async WebSocket broadcasting from sync thread pool

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-09 02:20:06 +08:00

157 lines
3.9 KiB
Python

"""
Task management API endpoints.
"""
from typing import List
from fastapi import APIRouter, Depends, HTTPException
from dashboard.config import config
from dashboard.dependencies import get_task_executor
from dashboard.models.task import TaskCreateRequest, TaskSchema, TaskStatus
from dashboard.services.task_executor import TaskExecutor
router = APIRouter(prefix="/tasks", tags=["tasks"])
@router.post("/execute", response_model=dict)
async def execute_task(
request: TaskCreateRequest,
executor: TaskExecutor = Depends(get_task_executor),
):
"""Execute task on device.
Args:
request: Task creation request
Returns:
Task ID
"""
# Fill in model config from environment if using defaults
if request.base_url == "http://localhost:8000/v1":
request.base_url = config.MODEL_BASE_URL
if request.model_name == "autoglm-phone-9b":
request.model_name = config.MODEL_NAME
if request.api_key == "EMPTY":
request.api_key = config.MODEL_API_KEY
task_id = await executor.execute_task(request)
return {"task_id": task_id, "message": "Task started"}
@router.post("/{task_id}/stop")
async def stop_task(
task_id: str,
executor: TaskExecutor = Depends(get_task_executor),
):
"""Stop running task.
Args:
task_id: Task identifier
Returns:
Stop confirmation
"""
await executor.stop_task(task_id)
return {"message": f"Task {task_id} stop requested"}
@router.get("", response_model=List[TaskSchema])
async def list_tasks(
executor: TaskExecutor = Depends(get_task_executor),
):
"""List all tasks (active and recent).
Returns:
List of task schemas
"""
return await executor.list_tasks()
@router.get("/{task_id}", response_model=TaskSchema)
async def get_task_status(
task_id: str,
executor: TaskExecutor = Depends(get_task_executor),
):
"""Get task status.
Args:
task_id: Task identifier
Returns:
Task schema
Raises:
HTTPException: If task not found
"""
task = await executor.get_task_status(task_id)
if task is None:
raise HTTPException(status_code=404, detail=f"Task {task_id} not found")
return task
@router.get("/{task_id}/screenshot")
async def get_task_screenshot(
task_id: str,
executor: TaskExecutor = Depends(get_task_executor),
):
"""Get latest screenshot from task execution.
Args:
task_id: Task identifier
Returns:
Screenshot data (base64 encoded)
Raises:
HTTPException: If task not found or device unavailable
"""
task = await executor.get_task_status(task_id)
if task is None:
raise HTTPException(status_code=404, detail=f"Task {task_id} not found")
# Get screenshot from device manager
from dashboard.dependencies import get_device_manager
device_manager = get_device_manager()
screenshot = await device_manager.get_screenshot(task.device_id)
if screenshot is None:
raise HTTPException(
status_code=500, detail="Failed to capture screenshot from device"
)
return {"task_id": task_id, "device_id": task.device_id, "screenshot": screenshot}
@router.get("/stats/summary")
async def get_task_stats(
executor: TaskExecutor = Depends(get_task_executor),
):
"""Get task execution statistics.
Returns:
Task statistics summary
"""
tasks = await executor.list_tasks()
total = len(tasks)
running = sum(1 for t in tasks if t.status == TaskStatus.RUNNING)
completed = sum(1 for t in tasks if t.status == TaskStatus.COMPLETED)
failed = sum(1 for t in tasks if t.status == TaskStatus.FAILED)
stopped = sum(1 for t in tasks if t.status == TaskStatus.STOPPED)
return {
"total": total,
"running": running,
"completed": completed,
"failed": failed,
"stopped": stopped,
"active_count": executor.get_active_task_count(),
}