hiropr/llm-code-deployment
0
1from dotenv import load_dotenv2load_dotenv()3 4from fastapi import FastAPI, BackgroundTasks, HTTPException5from pydantic import BaseModel6import os, sqlite3, time, requests7from helpers import hash_secret8from github_utils import create_and_push_repo9from llm_utils import generate_files_from_brief10 11# === CONFIG ===12STORED_SECRET_HASH = os.environ.get("STORED_SECRET_HASH")13OWNER_GITHUB = os.environ.get("GITHUB_USER")14DB_PATH = os.environ.get("DB_PATH", "./tasks.db")15 16# Ensure DB writable (Hugging Face-safe)17try:18 os.makedirs(os.path.dirname(DB_PATH) or ".", exist_ok=True)19 with open(os.path.join(os.path.dirname(DB_PATH) or ".", ".db_write_test"), "w") as f:20 f.write("")21except (OSError, IOError):22 DB_PATH = "/tmp/tasks.db"23 os.makedirs("/tmp", exist_ok=True)24 25app = FastAPI(title="IITM LLM Code Deployment API")26 27def init_db():28 conn = sqlite3.connect(DB_PATH)29 cur = conn.cursor()30 cur.execute("""31 CREATE TABLE IF NOT EXISTS tasks (32 id INTEGER PRIMARY KEY,33 email TEXT,34 task TEXT,35 round INTEGER,36 nonce TEXT,37 secret_hash TEXT,38 brief TEXT,39 evaluation_url TEXT,40 status TEXT,41 created_at DATETIME DEFAULT CURRENT_TIMESTAMP42 )43 """)44 conn.commit()45 conn.close()46init_db()47 48 49class TaskRequest(BaseModel):50 email: str51 secret: str52 task: str53 round: int54 nonce: str55 brief: str = ""56 checks: list = []57 evaluation_url: str = None58 attachments: list = []59 60 61@app.post("/api-endpoint")62async def receive_task(req: TaskRequest, background_tasks: BackgroundTasks):63 if not STORED_SECRET_HASH:64 raise HTTPException(status_code=500, detail="Server secret not configured")65 if hash_secret(req.secret) != STORED_SECRET_HASH:66 raise HTTPException(status_code=403, detail="Invalid secret")67 68 conn = sqlite3.connect(DB_PATH)69 cur = conn.cursor()70 cur.execute(71 "INSERT INTO tasks (email, task, round, nonce, secret_hash, brief, evaluation_url, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",72 (req.email, req.task, req.round, req.nonce, STORED_SECRET_HASH, req.brief, req.evaluation_url, "received"),73 )74 conn.commit()75 conn.close()76 77 background_tasks.add_task(process_task, req.dict())78 return {"status": "accepted", "task": req.task, "round": req.round, "nonce": req.nonce}79 80 81def process_task(data: dict):82 task = data["task"]83 nonce = data["nonce"]84 round_number = data.get("round", 1)85 short = nonce.replace("-", "")[:8]86 repo_name = f"{task}-{short}"87 88 print(f"Processing {task} (Round {round_number})")89 90 try:91 files = generate_files_from_brief(92 brief=data["brief"],93 attachments=data.get("attachments", []),94 round_number=round_number,95 user=OWNER_GITHUB,96 repo_name=repo_name,97 )98 files["LICENSE"] = get_mit_license_text()99 100 repo_url, commit_sha, pages_url = create_and_push_repo(101 repo_name, files,102 evaluation_data={103 "email": data["email"],104 "task": task,105 "round": round_number,106 "nonce": nonce,107 "evaluation_url": data.get("evaluation_url"),108 },109 )110 111 post_to_evaluation_url(data, repo_url, commit_sha, pages_url)112 113 conn = sqlite3.connect(DB_PATH)114 cur = conn.cursor()115 cur.execute(116 "UPDATE tasks SET status=? WHERE nonce=?",117 (f"completed: {task} round {round_number}", nonce),118 )119 conn.commit()120 conn.close()121 print(f"โ
Task {task} (round {round_number}) completed successfully")122 print(f"๐ Pages URL: {pages_url}")123 124 except Exception as e:125 print(f"โ Process failed for {task} (round {round_number}): {e}")126 conn = sqlite3.connect(DB_PATH)127 cur = conn.cursor()128 cur.execute("UPDATE tasks SET status=? WHERE nonce=?", (f"failed: {str(e)}", nonce))129 conn.commit()130 conn.close()131 132 133def post_to_evaluation_url(data, repo_url, commit_sha, pages_url):134 """POST to evaluation_url with exponential backoff (per IITM spec)."""135 if not data.get("evaluation_url"):136 print("โ ๏ธ No evaluation_url provided, skipping callback.")137 return138 139 payload = {140 "email": data["email"],141 "task": data["task"],142 "round": data["round"],143 "nonce": data["nonce"],144 "repo_url": repo_url,145 "commit_sha": commit_sha,146 "pages_url": pages_url,147 }148 149 for delay in [1, 2, 4, 8]:150 try:151 res = requests.post(data["evaluation_url"], json=payload, timeout=10)152 print(f"๐จ Evaluation POST โ {res.status_code}")153 if res.status_code == 200:154 print("โ
Evaluation server acknowledged successfully.")155 return156 except Exception as e:157 print(f"โ ๏ธ Evaluation POST failed (retrying in {delay}s): {e}")158 time.sleep(delay)159 print("โ Could not reach evaluation_url after multiple retries.")160 161 162def get_mit_license_text():163 return """MIT License164 165Copyright (c) 2025166 167Permission is hereby granted, free of charge, to any person obtaining a copy168of this software and associated documentation files (the "Software"), to deal169in the Software without restriction, including without limitation the rights170to use, copy, modify, merge, publish, distribute, sublicense, and/or sell171copies of the Software, and to permit persons to whom the Software is172furnished to do so, subject to the following conditions:173 174THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR175IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,176FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.177"""178 