Datavision / backend /api /v1 /endpoints /deploy.py
DataVision CI/CD Bot
release: clean production build for HuggingFace Space
ee7d7b9
Raw
History Blame Contribute Delete
5.88 kB
"""
Deploy API — ML Model Deployment Endpoints
============================================
POST /deploy/{model_id} — Deploy a trained model
GET /deploy/list — List user's deployments
GET /deploy/{deploy_id}/status — Health check for a deployment
DELETE /deploy/{deploy_id} — Undeploy / deactivate
POST /deploy/predict/{deploy_id} — Run inference
"""
from fastapi import APIRouter, HTTPException, Depends, Header, Request
from typing import Dict, Any, Optional, List
from pydantic import BaseModel
import datetime
import logging
from ml.model_deployer import get_model_deployer
from database.db import get_db
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from database.orm import MLDeployment
from core.rate_limiter import check_rate_limit
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/deploy", tags=["Deploy"])
class DeployRequest(BaseModel):
version: Optional[int] = None
class PredictRequest(BaseModel):
data: Dict[str, Any]
@router.post("/{model_id}")
async def deploy_model(
model_id: str,
request_obj: Request,
request: DeployRequest = None,
x_user_id: Optional[str] = Header(None, alias="X-User-ID"),
db: AsyncSession = Depends(get_db)
):
"""Deploy a trained model to an active endpoint and save to DB"""
user_id = x_user_id or model_id
if not user_id:
raise HTTPException(status_code=400, detail="Missing user context")
await check_rate_limit(request_obj, "deploy", user_id)
try:
deployer = get_model_deployer()
deployment = deployer.deploy_model(user_id, version=request.version if request else None)
# Save to PostgreSQL
new_db_deployment = MLDeployment(
deploy_id=deployment['deploy_id'],
user_id=user_id,
model_name=deployment['model_name'],
task_type=deployment['task_type'],
version=deployment['version'],
api_key=deployment['api_key'],
status='active'
)
db.add(new_db_deployment)
await db.commit()
return {"success": True, "deployment": deployment}
except ValueError as e:
await db.rollback()
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
await db.rollback()
logger.error(f"Deploy failed: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.get("/list")
async def list_deployments(
x_user_id: Optional[str] = Header(None, alias="X-User-ID"),
db: AsyncSession = Depends(get_db)
):
"""List all active deployments for a user from PostgreSQL"""
if not x_user_id:
raise HTTPException(status_code=400, detail="Missing user context")
try:
stmt = select(MLDeployment).where(MLDeployment.user_id == x_user_id)
result = await db.execute(stmt)
deployments = result.scalars().all()
deployments_list = []
for d in deployments:
deployments_list.append({
"deploy_id": d.deploy_id,
"model_name": d.model_name,
"task_type": d.task_type,
"version": d.version,
"api_key": d.api_key,
"status": d.status,
"created_at": d.created_at.isoformat() if d.created_at else None,
"endpoint": f"/api/v1/deploy/predict/{d.deploy_id}"
})
return {"success": True, "deployments": deployments_list}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/{deploy_id}/status")
async def deployment_status(
deploy_id: str,
x_user_id: Optional[str] = Header(None, alias="X-User-ID"),
):
"""Get health / metadata for a single deployment"""
try:
deployer = get_model_deployer()
status = deployer.get_deployment_status(deploy_id)
return {"success": True, **status}
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.delete("/{deploy_id}")
async def undeploy_model(
deploy_id: str,
x_user_id: Optional[str] = Header(None, alias="X-User-ID"),
db: AsyncSession = Depends(get_db)
):
"""Deactivate a deployment"""
user_id = x_user_id
if not user_id:
raise HTTPException(status_code=400, detail="Missing user context")
try:
deployer = get_model_deployer()
result = deployer.undeploy(deploy_id, user_id=user_id)
# Update PostgreSQL
try:
stmt = select(MLDeployment).where(MLDeployment.deploy_id == deploy_id)
db_result = await db.execute(stmt)
db_deployment = db_result.scalars().first()
if db_deployment:
db_deployment.status = "inactive"
await db.commit()
except Exception as db_err:
logger.warning(f"DB update for undeploy failed: {db_err}")
return {"success": True, **result}
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/predict/{deploy_id}")
async def predict_model(
deploy_id: str,
request: PredictRequest,
x_api_key: Optional[str] = Header(None, alias="X-API-Key")
):
"""Run inference against a deployed model"""
try:
deployer = get_model_deployer()
result = deployer.predict(deploy_id, request.data, api_key=x_api_key)
return {"success": True, "prediction": result}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))