- Rename metadata fields to meta_data for consistency across domain models - Update agent_identity, agent_performance, agent_portfolio, amm, analytics, bounty, certification, community, cross_chain_bridge, cross_chain_reputation, decentralized_memory, miner, pricing_models, trading, and wallet models - Rename chain_metadata to chain_meta_data in CrossChainMapping - Rename verification_metadata to verification_meta_data
153 lines
4.6 KiB
Python
Executable File
153 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Simple working coordinator API for GPU miner
|
|
"""
|
|
|
|
import logging
|
|
from fastapi import FastAPI, HTTPException, Header
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from typing import Optional, Dict, Any
|
|
from pydantic import BaseModel
|
|
import time
|
|
|
|
# Setup logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Create FastAPI app
|
|
app = FastAPI(
|
|
title="AITBC Coordinator API - Working",
|
|
version="0.1.0",
|
|
description="Simple working coordinator service for GPU miner",
|
|
)
|
|
|
|
# Add CORS middleware
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
allow_headers=["*"]
|
|
)
|
|
|
|
# Simple in-memory storage
|
|
miners: Dict[str, Dict[str, Any]] = {}
|
|
jobs: Dict[str, Dict[str, Any]] = {}
|
|
|
|
# Pydantic models
|
|
class MinerRegister(BaseModel):
|
|
miner_id: str
|
|
capabilities: list[str] = []
|
|
region: str = "default"
|
|
concurrency: int = 1
|
|
|
|
class MinerHeartbeat(BaseModel):
|
|
miner_id: str
|
|
status: str = "online"
|
|
inflight: int = 0
|
|
|
|
class JobSubmit(BaseModel):
|
|
prompt: str
|
|
model: str = "gemma3:1b"
|
|
priority: str = "normal"
|
|
|
|
# Basic auth (simple for testing)
|
|
API_KEY = "miner_test"
|
|
|
|
def verify_api_key(api_key: Optional[str] = Header(None), x_api_key: Optional[str] = Header(None)):
|
|
key = api_key or x_api_key
|
|
if key != API_KEY:
|
|
raise HTTPException(status_code=401, detail="invalid api key")
|
|
return key
|
|
|
|
@app.get("/health", tags=["health"], summary="Service healthcheck")
|
|
async def health() -> dict[str, str]:
|
|
"""Health check endpoint"""
|
|
return {"status": "ok", "service": "coordinator-api"}
|
|
|
|
@app.get("/v1/health", tags=["health"], summary="Service healthcheck")
|
|
async def health_v1() -> dict[str, str]:
|
|
"""Health check endpoint v1"""
|
|
return {"status": "ok", "service": "coordinator-api"}
|
|
|
|
@app.post("/v1/miners/register", tags=["miner"], summary="Register or update miner")
|
|
async def register_miner(
|
|
request: dict,
|
|
api_key: Optional[str] = Header(None),
|
|
x_api_key: Optional[str] = Header(None),
|
|
miner_id: Optional[str] = None
|
|
) -> dict[str, str]:
|
|
"""Register a miner"""
|
|
key = api_key or x_api_key
|
|
if key != API_KEY:
|
|
raise HTTPException(status_code=401, detail="invalid api key")
|
|
|
|
# Get miner_id from query parameter or request body
|
|
mid = miner_id or request.get("miner_id", "miner_test")
|
|
|
|
# Register the miner with simple data
|
|
miners[mid] = {
|
|
"id": mid,
|
|
"capabilities": ["gpu"],
|
|
"region": request.get("region", "localhost"),
|
|
"concurrency": request.get("concurrency", 1),
|
|
"status": "online",
|
|
"inflight": 0,
|
|
"last_heartbeat": time.time(),
|
|
"session_token": f"token_{mid}_{int(time.time())}"
|
|
}
|
|
|
|
logger.info(f"Miner {mid} registered")
|
|
return {"status": "ok", "session_token": miners[mid]["session_token"]}
|
|
|
|
@app.post("/v1/miners/heartbeat", tags=["miner"], summary="Send miner heartbeat")
|
|
async def miner_heartbeat(
|
|
request: dict,
|
|
api_key: Optional[str] = Header(None),
|
|
x_api_key: Optional[str] = Header(None),
|
|
miner_id: Optional[str] = None
|
|
) -> dict[str, str]:
|
|
"""Receive miner heartbeat"""
|
|
key = api_key or x_api_key
|
|
if key != API_KEY:
|
|
raise HTTPException(status_code=401, detail="invalid api key")
|
|
|
|
# Get miner_id from query parameter or request body
|
|
mid = miner_id or request.get("miner_id", "miner_test")
|
|
|
|
if mid not in miners:
|
|
raise HTTPException(status_code=404, detail="miner not registered")
|
|
|
|
miners[mid].update({
|
|
"status": request.get("status", "online"),
|
|
"inflight": request.get("current_jobs", 0),
|
|
"last_heartbeat": time.time()
|
|
})
|
|
|
|
return {"status": "ok"}
|
|
|
|
@app.post("/v1/miners/poll", tags=["miner"], summary="Poll for next job")
|
|
async def poll_for_job(
|
|
request: dict,
|
|
api_key: Optional[str] = Header(None),
|
|
x_api_key: Optional[str] = Header(None),
|
|
miner_id: Optional[str] = None
|
|
) -> Dict[str, Any]:
|
|
"""Poll for next job"""
|
|
key = api_key or x_api_key
|
|
if key != API_KEY:
|
|
raise HTTPException(status_code=401, detail="invalid api key")
|
|
|
|
# For now, return no jobs (empty response)
|
|
return {"status": "no_jobs"}
|
|
|
|
@app.get("/", tags=["root"], summary="Root endpoint")
|
|
async def root() -> dict[str, str]:
|
|
"""Root endpoint"""
|
|
return {"service": "AITBC Coordinator API", "status": "running"}
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
logger.info("Starting working coordinator API on port 9080")
|
|
uvicorn.run(app, host="127.0.0.1", port=9080)
|