Spaces:
Sleeping
Sleeping
File size: 4,847 Bytes
c5292d8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
"""Instructor API endpoint for receiving repo submissions."""
from datetime import datetime
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from instructor.database import Database
from shared.logger import setup_logger
from shared.models import RepoSubmission
logger = setup_logger(__name__)
app = FastAPI(
title="LLM Code Deployment - Instructor API",
description="API endpoint for receiving and validating repo submissions",
version="1.0.0",
)
# Initialize database
db = Database()
@app.on_event("startup")
async def startup_event():
"""Create database tables on startup."""
db.create_tables()
logger.info("Database initialized")
@app.post("/api/evaluate")
async def evaluate_submission(submission: RepoSubmission) -> JSONResponse:
"""Receive and validate repo submission.
Args:
submission: Repository submission from student
Returns:
HTTP 200 on success, HTTP 400 on validation failure
"""
logger.info(
f"Received submission: {submission.email}, "
f"task {submission.task}, round {submission.round}"
)
# Validate submission against task record
task = db.get_task_by_nonce(submission.nonce)
if not task:
logger.error(f"Invalid nonce: {submission.nonce}")
raise HTTPException(status_code=400, detail="Invalid nonce")
# Validate fields match
if task.email != submission.email:
logger.error(f"Email mismatch: expected {task.email}, got {submission.email}")
raise HTTPException(status_code=400, detail="Email mismatch")
if task.task != submission.task:
logger.error(f"Task mismatch: expected {task.task}, got {submission.task}")
raise HTTPException(status_code=400, detail="Task mismatch")
if task.round != submission.round:
logger.error(f"Round mismatch: expected {task.round}, got {submission.round}")
raise HTTPException(status_code=400, detail="Round mismatch")
# Check if already submitted
try:
existing = db.get_session().query(db.Repo).filter_by(nonce=submission.nonce).first()
if existing:
logger.warning(f"Duplicate submission for nonce {submission.nonce}")
return JSONResponse(
status_code=200,
content={
"status": "duplicate",
"message": "Submission already received",
},
)
finally:
db.get_session().close()
# Add to repos table
try:
repo = db.add_repo(
{
"timestamp": datetime.utcnow(),
"email": submission.email,
"task": submission.task,
"round": submission.round,
"nonce": submission.nonce,
"repo_url": submission.repo_url,
"commit_sha": submission.commit_sha,
"pages_url": submission.pages_url,
}
)
logger.info(
f"Added repo submission: {submission.task}, round {submission.round}, "
f"repo {submission.repo_url}"
)
return JSONResponse(
status_code=200,
content={
"status": "success",
"message": "Submission received and queued for evaluation",
"repo_id": repo.id,
},
)
except Exception as e:
logger.error(f"Failed to add repo submission: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")
@app.get("/api/submissions/{email}")
async def get_submissions(email: str) -> JSONResponse:
"""Get all submissions for an email.
Args:
email: Student email
Returns:
List of submissions
"""
repos = db.get_repos(email=email)
return JSONResponse(content=[repo.to_dict() for repo in repos])
@app.get("/api/results/{email}")
async def get_results(email: str) -> JSONResponse:
"""Get all evaluation results for an email.
Args:
email: Student email
Returns:
List of evaluation results
"""
results = db.get_results(email=email)
return JSONResponse(content=[result.to_dict() for result in results])
@app.get("/health")
async def health_check() -> JSONResponse:
"""Health check endpoint.
Returns:
Health status
"""
return JSONResponse(
content={
"status": "healthy",
"timestamp": datetime.utcnow().isoformat(),
}
)
if __name__ == "__main__":
import uvicorn
from shared.config import settings
logger.info(f"Starting Instructor API on port {settings.instructor_api_port}")
uvicorn.run(
"instructor.api:app",
host="0.0.0.0",
port=settings.instructor_api_port,
reload=True,
)
|