Spaces:
Sleeping
Sleeping
| """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() | |
| async def startup_event(): | |
| """Create database tables on startup.""" | |
| db.create_tables() | |
| logger.info("Database initialized") | |
| 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") | |
| 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]) | |
| 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]) | |
| 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, | |
| ) | |