# main.py from fastapi import FastAPI, HTTPException, BackgroundTasks from pydantic import BaseModel import requests,time, os from dotenv import load_dotenv from github import Github # from PyGithub from llm_utils import generate_app_files # your LLM logic from typing import Optional, List, Dict app = FastAPI() load_dotenv() # take environment variables from .env SHARED_SECRET = os.getenv("SECRET_KEY") GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") class TaskRequest(BaseModel): email: str task: str brief: str checks: List[str] round: int nonce: str secret: str evaluation_url: str attachments: Optional[List[Dict[str, str]]] = None # added to accept attachments like data URIs @app.get("/") def root(): return {"status": "ok"} async def process_task_in_background(req: TaskRequest): # 2. Generate files and handle repo based on round app_files = generate_app_files( brief=req.brief, checks=req.checks, attachments=req.attachments, round=req.round, task=req.task ) # Validate LLM output if not isinstance(app_files, dict) or "index" not in app_files or "README" not in app_files: raise HTTPException(status_code=500, detail="LLM did not return expected file structure") # Initialize GitHub client g = Github(GITHUB_TOKEN) user = g.get_user() repo_name = f"{req.task}" if req.round == 1: # Round 1: Create new repo try: repo = user.create_repo(repo_name, private=False, auto_init=False, license_template="mit") except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to create repo: {e}") else: # Round >1: Get existing repo and update try: # Use get_repo with full path repo = g.get_repo(f"{user.login}/{repo_name}") except Exception as e: raise HTTPException(status_code=404, detail=f"Repository not found: {e}") # Prepare files to commit files_to_commit = { "index.html": app_files["index"], "README.md": app_files["README"], } if isinstance(app_files.get("assets"), dict): files_to_commit.update(app_files["assets"]) # Add/Update files & commit for path, content in files_to_commit.items(): try: if req.round == 1: # Create new file for round 1 repo.create_file(path, f"add {path}", content) else: # Update existing file for later rounds try: # Get current file content file = repo.get_contents(path) # Update file repo.update_file(path, f"update {path} for round {req.round}", content, file.sha) except Exception: # File doesn't exist, create it repo.create_file(path, f"add {path} for round {req.round}", content) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to handle file {path}: {e}") # Enable GitHub Pages only for round 1 if req.round == 1: try: requests.post( f"https://api.github.com/repos/{user.login}/{repo_name}/pages", headers={ "Authorization": f"token {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json", }, json={"source": {"branch": "main", "path": "/"}} ) except Exception: pass # Get latest commit SHA commit_sha = repo.get_commits()[0].sha # Prepare evaluation JSON payload = { "email": req.email, "task": req.task, "round": req.round, "nonce": req.nonce, "repo_url": repo.html_url, "commit_sha": commit_sha, "pages_url": f"https://{user.login}.github.io/{repo_name}/", } time.sleep(60) # 8. POST to evaluation URL (with exponential backoff) — include JSON Content-Type & try up to 5 times delay = 1 headers = {"Content-Type": "application/json"} for _ in range(5): try: r = requests.post(req.evaluation_url, json=payload, headers=headers, timeout=10) if r.status_code == 200: break except Exception: pass time.sleep(delay) delay *= 2 @app.post("/task1") async def handle_task(req: TaskRequest, background_tasks: BackgroundTasks): # 1. Secret verification if req.secret != SHARED_SECRET: raise HTTPException(status_code=403, detail="Invalid secret") if req.email!="22f3000730@ds.study.iitm.ac.in": raise HTTPException(status_code=403, detail="Invalid email") # Add the processing to background tasks background_tasks.add_task(process_task_in_background, req) # Return simple acknowledgment return {"status": "accepted", "message": "Request received and processing"} if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)