Spaces:
Sleeping
Sleeping
| """ | |
| inference.py — Baseline agent for the Supergames environment. | |
| Runs all 4 tasks using an LLM agent via OpenAI client. | |
| Required env vars: | |
| API_BASE_URL The API endpoint for the LLM | |
| MODEL_NAME The model identifier | |
| HF_TOKEN Your Hugging Face / API key | |
| """ | |
| import os | |
| import json | |
| import time | |
| from openai import OpenAI | |
| from server.environment import SupergamesEnvironment | |
| from models import SupergamesAction, Assignment | |
| API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1") | |
| API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY") | |
| MODEL_NAME = os.getenv("MODEL_NAME") | |
| TEMPERATURE = 0.2 | |
| MAX_TOKENS = 512 | |
| client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY) | |
| SYSTEM_PROMPT = """ | |
| You are an engineering manager at Super Games. | |
| Your job is to allocate staff to bugs and features each sprint to maximise revenue. | |
| You will receive the current state of the environment as text. | |
| Respond with ONLY a valid JSON object in this exact format, no explanation: | |
| { | |
| "assignments": [ | |
| {"workItemID": "<id>", "staff": <int>}, | |
| ... | |
| ] | |
| } | |
| Rules: | |
| - Total staff across all assignments must not exceed staffPool total | |
| - Prioritise crisis items immediately when they appear | |
| - High severity bugs cause compounding churn if left unresolved | |
| - Features take longer but have higher long term payoff | |
| """ | |
| def buildPrompt(obs) -> str: | |
| # your job — describe the observation to the LLM | |
| # hint: include goal, currentStep, totalSteps, | |
| # staffPool.available, and a summary of workQueue items | |
| lines = [ | |
| "Current Supergames state:", | |
| f"Goal: {obs.goal}", | |
| f"Sprint: {obs.currentStep}/{obs.totalSteps}", | |
| f"Staff available: {obs.staffPool.available} (total={obs.staffPool.total})", | |
| f"Queue size: {len(obs.workQueue)}", | |
| "Work queue:", | |
| ] | |
| if not obs.workQueue: | |
| lines.append("- No items in queue") | |
| else: | |
| for item in obs.workQueue: | |
| lines.append( | |
| "- " | |
| f"id={item.id} " | |
| f"type={item.workType.value} " | |
| f"severity={int(item.severity)} " | |
| f"crisis={item.crisis} " | |
| f"effort={item.effort} " | |
| f"daysWorked={item.daysWorked} " | |
| f"revenueImpact={item.revenueImpact} " | |
| f"impactDelay={item.impactDelay}" | |
| f"daysWorked={item.daysWorked}/{item.effort} " | |
| f"lastSprintStaff={item.lastSprintStaff} " | |
| ) | |
| lines.extend( | |
| [ | |
| "", | |
| "Return ONLY JSON in the requested schema.", | |
| "Do not include markdown code fences.", | |
| ] | |
| ) | |
| return "\n".join(lines) | |
| def parseAction(response: str) -> SupergamesAction: | |
| # your job — parse the LLM's JSON response into a SupergamesAction | |
| # hint: use json.loads(), then build Assignment objects | |
| # if parsing fails, return a fallback action | |
| fallback = SupergamesAction(assignments=[]) | |
| try: | |
| raw = response.strip() | |
| if not raw: | |
| return fallback | |
| # Handle markdown-wrapped JSON or extra text around the payload. | |
| if raw.startswith("```"): | |
| raw = raw.strip("`") | |
| if raw.lower().startswith("json"): | |
| raw = raw[4:].strip() | |
| try: | |
| data = json.loads(raw) | |
| except json.JSONDecodeError: | |
| start = raw.find("{") | |
| end = raw.rfind("}") | |
| if start == -1 or end == -1 or end <= start: | |
| return fallback | |
| data = json.loads(raw[start : end + 1]) | |
| assignments_data = data.get("assignments", []) if isinstance(data, dict) else [] | |
| if not isinstance(assignments_data, list): | |
| return fallback | |
| assignments = [] | |
| for item in assignments_data: | |
| if not isinstance(item, dict): | |
| continue | |
| work_item_id = item.get("workItemID") | |
| staff = item.get("staff") | |
| if not isinstance(work_item_id, str) or not work_item_id.strip(): | |
| continue | |
| try: | |
| staff_int = int(staff) | |
| except (TypeError, ValueError): | |
| continue | |
| if staff_int < 1: | |
| continue | |
| assignments.append(Assignment(workItemID=work_item_id.strip(), staff=staff_int)) | |
| return SupergamesAction(assignments=assignments) | |
| except Exception: | |
| return fallback | |
| def runTask(taskId: int, seed: int = 42) -> float: | |
| env = SupergamesEnvironment() | |
| obs = env.reset(task_id=taskId, seed=seed) | |
| print(f"\nTask {taskId} | {obs.goal[:80]}...") | |
| while not obs.done: | |
| prompt = buildPrompt(obs) | |
| try: | |
| response = client.chat.completions.create( | |
| model=MODEL_NAME, | |
| messages=[ | |
| {"role": "system", "content": SYSTEM_PROMPT}, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| temperature=TEMPERATURE, | |
| max_tokens=MAX_TOKENS, | |
| ) | |
| raw = response.choices[0].message.content or "" | |
| except Exception as e: | |
| print(f" LLM error: {e}") | |
| raw = '{"assignments": []}' | |
| action = parseAction(raw) | |
| obs = env.step(action) | |
| print(f" Step {obs.currentStep}/{obs.totalSteps} | reward: {obs.reward:.4f}") | |
| time.sleep(1) | |
| return obs.reward or 0.0 | |
| def main(): | |
| print("Supergames Basic Inference") | |
| scores = [] | |
| for taskId in range(1, 5): | |
| try: | |
| score = runTask(taskId) | |
| scores.append(score) | |
| print(f"Task {taskId} final score: {score:.4f}") | |
| except Exception as e: | |
| print(f"Task {taskId} failed: {e}") | |
| scores.append(0.0) | |
| print("\n=== RESULTS ===") | |
| for i, score in enumerate(scores, 1): | |
| bar = "█" * int(score * 20) + "░" * (20 - int(score * 20)) | |
| print(f"Task {i}: [{bar}] {score:.4f}") | |
| print(f"Mean: {sum(scores)/len(scores):.4f}") | |
| if __name__ == "__main__": | |
| main() |