Morget-01 / api /workers.py
MGFeng's picture
Update api/workers.py
0998bb4 verified
Raw
History Blame Contribute Delete
5.13 kB
import time
from datetime import datetime
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Optional, Dict, Any
from config import MASTER_SECRET, HEARTBEAT_TIMEOUT
from core.state import workers, add_worker, update_worker, get_worker_by_id
from core.task_manager import get_next_task_for_worker
router = APIRouter(prefix="/api/workers", tags=["workers"])
class WorkerRegisterRequest(BaseModel):
worker_id: str
worker_type: str
capabilities: Dict[str, Any]
cpu_cores: int
memory_gb: float
class WorkerHeartbeatRequest(BaseModel):
worker_id: str
token: str
current_load: float
memory_used_gb: float
gpu_used_gb: Optional[float] = None
current_task: Optional[str] = None
backup_task: Optional[str] = None
progress: Optional[float] = 0.0
status_detail: Optional[str] = None
def generate_token(worker_id: str) -> str:
import hashlib
salt = str(time.time())
return hashlib.sha256(f"{worker_id}:{MASTER_SECRET}:{salt}".encode()).hexdigest()[:32]
def verify_token(worker_id: str, token: str) -> bool:
if worker_id not in workers:
return False
return workers[worker_id].get("token") == token
@router.post("/register")
async def register_worker(data: WorkerRegisterRequest):
try:
worker_id = data.worker_id
if worker_id in workers:
workers[worker_id]["token"] = generate_token(worker_id)
workers[worker_id]["last_heartbeat"] = time.time()
workers[worker_id]["status"] = "idle"
workers[worker_id]["status_detail"] = "idle"
workers[worker_id]["info"] = data.dict()
print(f"✅ Worker {worker_id} re-registered")
return {"status": "updated", "worker_id": worker_id, "token": workers[worker_id]["token"]}
token = generate_token(worker_id)
workers[worker_id] = {
"info": data.dict(),
"token": token,
"last_heartbeat": time.time(),
"status": "idle",
"status_detail": "idle",
"current_task": None,
"backup_task": None,
"progress": 0.0,
"completed_tasks": 0,
"registered_at": datetime.now().isoformat(),
}
print(f"✅ Worker {worker_id} registered")
return {"status": "registered", "worker_id": worker_id, "token": token}
except Exception as e:
print(f"❌ Register error: {e}")
raise HTTPException(500, str(e))
@router.post("/heartbeat")
async def worker_heartbeat(data: WorkerHeartbeatRequest):
try:
if data.worker_id not in workers:
raise HTTPException(404, "Worker not found")
if not verify_token(data.worker_id, data.token):
raise HTTPException(401, "Invalid token")
w = workers[data.worker_id]
w["last_heartbeat"] = time.time()
w["status"] = "busy" if data.current_task else "idle"
w["current_task"] = data.current_task
w["backup_task"] = data.backup_task
w["progress"] = data.progress or 0.0
w["status_detail"] = data.status_detail or "idle"
return {"status": "ok"}
except HTTPException:
raise
except Exception as e:
print(f"❌ Heartbeat error: {e}")
raise HTTPException(500, str(e))
@router.get("/list")
async def list_workers():
try:
now = time.time()
result = []
for wid, w in workers.items():
is_online = (now - w.get("last_heartbeat", 0)) < HEARTBEAT_TIMEOUT
result.append({
"worker_id": wid,
"worker_type": w.get("info", {}).get("worker_type", "unknown"),
"status": "online" if is_online else "offline",
"status_detail": w.get("status_detail", "idle"),
"current_task": w.get("current_task"),
"backup_task": w.get("backup_task"),
"progress": w.get("progress", 0),
"completed_tasks": w.get("completed_tasks", 0),
"last_heartbeat": datetime.fromtimestamp(w.get("last_heartbeat", 0)).isoformat() if w.get("last_heartbeat") else None,
})
return {"workers": result, "total": len(result)}
except Exception as e:
print(f"❌ List error: {e}")
return {"workers": [], "total": 0}
@router.get("/next-task")
async def get_next_task(worker_id: str, token: str):
try:
if worker_id not in workers:
raise HTTPException(404, "Worker not found")
if not verify_token(worker_id, token):
raise HTTPException(401, "Invalid token")
task = get_next_task_for_worker(worker_id)
if task:
workers[worker_id]["current_task"] = task["task_id"]
workers[worker_id]["status"] = "busy"
return {"current_task": task, "backup_task": None}
else:
return {"current_task": None, "backup_task": None}
except HTTPException:
raise
except Exception as e:
print(f"❌ Get next task error: {e}")
raise HTTPException(500, str(e))