Files
Open-AutoGLM/dashboard/main.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

176 lines
4.5 KiB
Python

"""
AutoGLM Dashboard - FastAPI Main Application.
This is the main entry point for the web dashboard.
Run with: uvicorn dashboard.main:app --host 0.0.0.0 --port 8080 --reload
"""
import os
from contextlib import asynccontextmanager
from datetime import datetime
from pathlib import Path
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from dashboard.api import devices_router, tasks_router, websocket_router
from dashboard.config import config
from dashboard.dependencies import (
get_device_manager,
get_task_executor,
get_ws_manager,
)
from dashboard.services.device_manager import DeviceManager
from dashboard.services.task_executor import TaskExecutor
from dashboard.services.websocket_manager import WebSocketManager
# Load .env file
load_dotenv()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan manager.
Handles startup and shutdown events.
"""
# Startup
print("=" * 50)
print("AutoGLM Dashboard Starting...")
print("=" * 50)
# Initialize services
device_manager = get_device_manager()
task_executor = get_task_executor()
ws_manager = get_ws_manager()
# Link services
task_executor.set_ws_manager(ws_manager)
# Scan for devices on startup
print("Scanning for devices...")
try:
devices = await device_manager.refresh_devices()
print(f"Found {len(devices)} device(s)")
for device in devices:
status = "connected" if device.is_connected else "disconnected"
print(f" - {device.device_id} ({status})")
except Exception as e:
print(f"Error scanning devices: {e}")
print("=" * 50)
print(f"Dashboard running on http://{config.HOST}:{config.PORT}")
print(f"API docs: http://{config.HOST}:{config.PORT}/docs")
print("=" * 50)
yield
# Shutdown
print("Shutting down dashboard...")
# Create FastAPI app
app = FastAPI(
title="AutoGLM Dashboard",
description="Web-based multi-device control interface for AutoGLM",
version="0.1.0",
lifespan=lifespan,
)
# Configure CORS for LAN access
app.add_middleware(
CORSMiddleware,
allow_origins=config.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Exception handlers
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Global exception handler."""
return JSONResponse(
status_code=500,
content={"error": str(exc), "type": type(exc).__name__},
)
# Include routers
app.include_router(devices_router, prefix="/api")
app.include_router(tasks_router, prefix="/api")
app.include_router(websocket_router)
# Health check
@app.get("/health")
async def health_check():
"""Health check endpoint."""
device_manager = get_device_manager()
task_executor = get_task_executor()
ws_manager = get_ws_manager()
devices = device_manager.list_all_devices()
connected_devices = sum(1 for d in devices if d.is_connected)
return {
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"devices": {
"total": len(devices),
"connected": connected_devices,
},
"tasks": {
"active": task_executor.get_active_task_count(),
},
"websocket": {
"connections": ws_manager.get_connection_count(),
},
}
# Root endpoint - serve dashboard or API info
@app.get("/")
async def root():
"""Root endpoint - returns API info or serves dashboard."""
# Check if static files exist
static_path = Path(__file__).parent / "static" / "index.html"
if static_path.exists():
return FileResponse(static_path)
# Return API info if dashboard not built
return {
"name": "AutoGLM Dashboard API",
"version": "0.1.0",
"docs": "/docs",
"endpoints": {
"devices": "/api/devices",
"tasks": "/api/tasks",
"websocket": "/ws",
"health": "/health",
},
}
# Mount static files for dashboard (if exists)
static_path = Path(__file__).parent / "static"
if static_path.exists():
app.mount("/static", StaticFiles(directory=str(static_path)), name="static")
# Run script entry point
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"dashboard.main:app",
host=config.HOST,
port=config.PORT,
reload=config.DEBUG,
)