| | """ |
| | Nexus-Core Inference API (Fixed) |
| | Fast and efficient chess engine with proper error handling |
| | """ |
| |
|
| | from fastapi import FastAPI, HTTPException |
| | from fastapi.middleware.cors import CORSMiddleware |
| | from pydantic import BaseModel, Field |
| | import time |
| | import logging |
| | import os |
| | from typing import Optional |
| |
|
| | from engine import NexusCoreEngine |
| |
|
| | logging.basicConfig( |
| | level=logging.INFO, |
| | format='%(asctime)s - %(levelname)s - %(message)s' |
| | ) |
| | logger = logging.getLogger(__name__) |
| |
|
| | app = FastAPI( |
| | title="Nexus-Core Inference API", |
| | description="Fast chess engine (13M params)", |
| | version="2.0.0" |
| | ) |
| |
|
| | app.add_middleware( |
| | CORSMiddleware, |
| | allow_origins=["*"], |
| | allow_credentials=True, |
| | allow_methods=["*"], |
| | allow_headers=["*"], |
| | ) |
| |
|
| | engine = None |
| |
|
| |
|
| | class MoveRequest(BaseModel): |
| | fen: str |
| | depth: Optional[int] = Field(4, ge=1, le=6) |
| | time_limit: Optional[int] = Field(3000, ge=1000, le=10000) |
| |
|
| |
|
| | class MoveResponse(BaseModel): |
| | best_move: str |
| | evaluation: float |
| | depth_searched: int |
| | nodes_evaluated: int |
| | time_taken: int |
| |
|
| |
|
| | class HealthResponse(BaseModel): |
| | status: str |
| | model_loaded: bool |
| | version: str |
| | model_path: Optional[str] = None |
| |
|
| |
|
| | @app.on_event("startup") |
| | async def startup_event(): |
| | global engine |
| | logger.info("🚀 Starting Nexus-Core API...") |
| | |
| | |
| | model_path = "/app/models/nexus_core.onnx" |
| | |
| | |
| | if os.path.exists("/app/models"): |
| | logger.info(f"📂 Files in /app/models/:") |
| | for f in os.listdir("/app/models"): |
| | full_path = os.path.join("/app/models", f) |
| | size = os.path.getsize(full_path) / (1024*1024) |
| | logger.info(f" - {f} ({size:.2f} MB)") |
| | else: |
| | logger.error("❌ /app/models/ directory does not exist!") |
| | raise FileNotFoundError("/app/models/ not found") |
| | |
| | |
| | if not os.path.exists(model_path): |
| | logger.error(f"❌ Model not found at {model_path}") |
| | logger.error("Available files:", os.listdir("/app/models") if os.path.exists("/app/models") else "No models dir") |
| | raise FileNotFoundError(f"Model file not found: {model_path}") |
| | |
| | logger.info(f"✅ Model found: {model_path} ({os.path.getsize(model_path)/(1024*1024):.2f} MB)") |
| | |
| | try: |
| | engine = NexusCoreEngine( |
| | model_path=model_path, |
| | num_threads=2 |
| | ) |
| | logger.info("✅ Nexus-Core engine loaded successfully") |
| | except Exception as e: |
| | logger.error(f"❌ Failed to load engine: {e}", exc_info=True) |
| | raise |
| |
|
| |
|
| | @app.get("/health", response_model=HealthResponse) |
| | async def health_check(): |
| | return { |
| | "status": "healthy" if engine else "unhealthy", |
| | "model_loaded": engine is not None, |
| | "version": "2.0.0", |
| | "model_path": "/app/models/nexus_core.onnx" if engine else None |
| | } |
| |
|
| |
|
| | @app.post("/get-move", response_model=MoveResponse) |
| | async def get_move(request: MoveRequest): |
| | if not engine: |
| | raise HTTPException(503, "Engine not loaded") |
| | |
| | if not engine.validate_fen(request.fen): |
| | raise HTTPException(400, "Invalid FEN") |
| | |
| | start = time.time() |
| | |
| | try: |
| | result = engine.get_best_move( |
| | request.fen, |
| | request.depth, |
| | request.time_limit |
| | ) |
| | |
| | logger.info( |
| | f"Move: {result['best_move']} | " |
| | f"Eval: {result['evaluation']:+.2f} | " |
| | f"Depth: {result['depth_searched']} | " |
| | f"Nodes: {result['nodes_evaluated']} | " |
| | f"Time: {result['time_taken']}ms" |
| | ) |
| | |
| | return MoveResponse(**result) |
| | |
| | except Exception as e: |
| | logger.error(f"Error during search: {e}", exc_info=True) |
| | raise HTTPException(500, str(e)) |
| |
|
| |
|
| | @app.get("/") |
| | async def root(): |
| | return { |
| | "name": "Nexus-Core API", |
| | "version": "2.0.0", |
| | "model": "13M parameters", |
| | "speed": "Ultra-fast (0.5-1s per move)", |
| | "status": "healthy" if engine else "loading", |
| | "endpoints": { |
| | "POST /get-move": "Get best move", |
| | "GET /health": "Health check", |
| | "GET /docs": "API documentation" |
| | } |
| | } |
| |
|
| |
|
| | if __name__ == "__main__": |
| | import uvicorn |
| | uvicorn.run(app, host="0.0.0.0", port=7860, log_level="info") |