Commit ·
b870822
1
Parent(s): 0cca1b6
Final Commit
Browse files- inference.py +125 -0
- server.py +29 -6
inference.py
CHANGED
|
@@ -220,6 +220,131 @@ def run_episode(client: Optional[OpenAI], task_id: str, episode_num: int) -> flo
|
|
| 220 |
return task_score
|
| 221 |
|
| 222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
def main() -> None:
|
| 224 |
if not ENV_URL:
|
| 225 |
raise SystemExit("ENV_URL is required. Example: ENV_URL=http://localhost:7860")
|
|
|
|
| 220 |
return task_score
|
| 221 |
|
| 222 |
|
| 223 |
+
def run_all_tasks() -> Dict[str, float]:
|
| 224 |
+
"""
|
| 225 |
+
Run all three tasks and return deterministic scores.
|
| 226 |
+
|
| 227 |
+
This is used by the FastAPI server to show live demo results on the Space.
|
| 228 |
+
"""
|
| 229 |
+
|
| 230 |
+
# Prefer local in-process execution when running inside the server (no ENV_URL needed).
|
| 231 |
+
try:
|
| 232 |
+
from acre.tasks.task_registry import TaskRegistry
|
| 233 |
+
from openenv_interface import OpenEnvRefactorEnv
|
| 234 |
+
except Exception:
|
| 235 |
+
TaskRegistry = None # type: ignore[assignment]
|
| 236 |
+
OpenEnvRefactorEnv = None # type: ignore[assignment]
|
| 237 |
+
|
| 238 |
+
registry = TaskRegistry() if TaskRegistry is not None else None
|
| 239 |
+
env = OpenEnvRefactorEnv(registry=registry) if OpenEnvRefactorEnv is not None else None
|
| 240 |
+
|
| 241 |
+
def _choose_action_name(code: str, task_id: str) -> int:
|
| 242 |
+
# Reuse the same heuristic logic (deterministic).
|
| 243 |
+
has_generic = re.search(r"\b(x|tmp|i)\b", code) is not None
|
| 244 |
+
has_if_false = re.search(r"\bif\s+False\b", code) is not None
|
| 245 |
+
has_if_true = re.search(r"\bif\s+True\b", code) is not None
|
| 246 |
+
has_append_loop = ".append(" in code and "for " in code
|
| 247 |
+
has_double_not = "not not" in code
|
| 248 |
+
has_add_call = "add(" in code
|
| 249 |
+
|
| 250 |
+
if task_id == "rename_variables":
|
| 251 |
+
if has_generic:
|
| 252 |
+
return 0
|
| 253 |
+
if has_if_false or "unused" in code:
|
| 254 |
+
return 1
|
| 255 |
+
if has_append_loop:
|
| 256 |
+
return 2
|
| 257 |
+
if has_if_true or has_double_not:
|
| 258 |
+
return 3
|
| 259 |
+
return 4
|
| 260 |
+
|
| 261 |
+
if task_id == "remove_dead_code":
|
| 262 |
+
if has_if_false or "unused" in code:
|
| 263 |
+
return 1
|
| 264 |
+
if has_append_loop:
|
| 265 |
+
return 2
|
| 266 |
+
if has_if_true or has_double_not:
|
| 267 |
+
return 3
|
| 268 |
+
if has_generic:
|
| 269 |
+
return 0
|
| 270 |
+
return 4
|
| 271 |
+
|
| 272 |
+
if has_generic:
|
| 273 |
+
return 0
|
| 274 |
+
if has_append_loop:
|
| 275 |
+
return 2
|
| 276 |
+
if has_if_false or has_if_true or has_double_not:
|
| 277 |
+
return 3
|
| 278 |
+
if has_add_call:
|
| 279 |
+
return 4
|
| 280 |
+
return 1
|
| 281 |
+
|
| 282 |
+
# Map tasks → nice names for demo output.
|
| 283 |
+
task_plan = [
|
| 284 |
+
("easy_task", "rename_variables"),
|
| 285 |
+
("medium_task", "remove_dead_code"),
|
| 286 |
+
("hard_task", "full_refactor"),
|
| 287 |
+
]
|
| 288 |
+
|
| 289 |
+
results: Dict[str, float] = {"easy": 0.0, "medium": 0.0, "hard": 0.0, "final": 0.0}
|
| 290 |
+
scores: List[float] = []
|
| 291 |
+
|
| 292 |
+
# If we have a local env, use it. Otherwise fall back to HTTP (requires ENV_URL).
|
| 293 |
+
if env is None or registry is None:
|
| 294 |
+
if not ENV_URL:
|
| 295 |
+
return results
|
| 296 |
+
# Use existing HTTP-driven path.
|
| 297 |
+
client: Optional[OpenAI] = None
|
| 298 |
+
for label, task_id in task_plan:
|
| 299 |
+
print(f"START {label}", flush=True)
|
| 300 |
+
reset_env(task_id)
|
| 301 |
+
for _ in range(5):
|
| 302 |
+
state = get_state()
|
| 303 |
+
action = _choose_action_name(str(state.get("current_code", "")), task_id)
|
| 304 |
+
action_name = ACTION_MEANINGS.get(int(action), "unknown")
|
| 305 |
+
print(f"STEP {action_name}", flush=True)
|
| 306 |
+
step_env(action)
|
| 307 |
+
final_state = get_state()
|
| 308 |
+
score = float(grade(task_id, final_state.get("current_code", "")))
|
| 309 |
+
print(f"END score: {score:.2f}", flush=True)
|
| 310 |
+
scores.append(score)
|
| 311 |
+
if task_id == "rename_variables":
|
| 312 |
+
results["easy"] = score
|
| 313 |
+
elif task_id == "remove_dead_code":
|
| 314 |
+
results["medium"] = score
|
| 315 |
+
else:
|
| 316 |
+
results["hard"] = score
|
| 317 |
+
|
| 318 |
+
results["final"] = float(sum(scores) / len(scores)) if scores else 0.0
|
| 319 |
+
return results
|
| 320 |
+
|
| 321 |
+
# Local in-process execution (fast + no network recursion).
|
| 322 |
+
for label, task_id in task_plan:
|
| 323 |
+
print(f"START {label}", flush=True)
|
| 324 |
+
env.reset(seed=0, task_id=task_id)
|
| 325 |
+
for _ in range(5):
|
| 326 |
+
st = env.state()
|
| 327 |
+
code = str(st.current_code)
|
| 328 |
+
action = int(_choose_action_name(code, task_id))
|
| 329 |
+
action_name = env.action_meanings.get(action, "unknown")
|
| 330 |
+
print(f"STEP {action_name}", flush=True)
|
| 331 |
+
env.step(action)
|
| 332 |
+
st = env.state()
|
| 333 |
+
task = registry.get_task(task_id)
|
| 334 |
+
score = float(task.grade_against_expected(st.current_code)) if task is not None else 0.0
|
| 335 |
+
print(f"END score: {score:.2f}", flush=True)
|
| 336 |
+
scores.append(score)
|
| 337 |
+
if task_id == "rename_variables":
|
| 338 |
+
results["easy"] = score
|
| 339 |
+
elif task_id == "remove_dead_code":
|
| 340 |
+
results["medium"] = score
|
| 341 |
+
else:
|
| 342 |
+
results["hard"] = score
|
| 343 |
+
|
| 344 |
+
results["final"] = float(sum(scores) / len(scores)) if scores else 0.0
|
| 345 |
+
return results
|
| 346 |
+
|
| 347 |
+
|
| 348 |
def main() -> None:
|
| 349 |
if not ENV_URL:
|
| 350 |
raise SystemExit("ENV_URL is required. Example: ENV_URL=http://localhost:7860")
|
server.py
CHANGED
|
@@ -22,7 +22,7 @@ import uvicorn
|
|
| 22 |
import numpy as np
|
| 23 |
from fastapi import FastAPI, HTTPException
|
| 24 |
from fastapi.middleware.cors import CORSMiddleware
|
| 25 |
-
from fastapi.responses import HTMLResponse
|
| 26 |
from openai import OpenAI
|
| 27 |
|
| 28 |
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -468,10 +468,25 @@ def _demo_html() -> str:
|
|
| 468 |
# Routes
|
| 469 |
# ---------------------------------------------------------------------------
|
| 470 |
|
| 471 |
-
@app.get("/"
|
| 472 |
-
def
|
| 473 |
-
"""
|
| 474 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
|
| 476 |
|
| 477 |
@app.get("/health", response_model=CompatibilityHealthResponse)
|
|
@@ -480,7 +495,15 @@ def health_compat() -> CompatibilityHealthResponse:
|
|
| 480 |
return CompatibilityHealthResponse(status="healthy", service="acre-env")
|
| 481 |
|
| 482 |
|
| 483 |
-
@app.get("/demo"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
def demo_ui() -> HTMLResponse:
|
| 485 |
"""Simple UI to compare original and optimized code side-by-side."""
|
| 486 |
return HTMLResponse(content=_demo_html())
|
|
|
|
| 22 |
import numpy as np
|
| 23 |
from fastapi import FastAPI, HTTPException
|
| 24 |
from fastapi.middleware.cors import CORSMiddleware
|
| 25 |
+
from fastapi.responses import HTMLResponse, JSONResponse
|
| 26 |
from openai import OpenAI
|
| 27 |
|
| 28 |
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
| 468 |
# Routes
|
| 469 |
# ---------------------------------------------------------------------------
|
| 470 |
|
| 471 |
+
@app.get("/")
|
| 472 |
+
def root() -> JSONResponse:
|
| 473 |
+
"""
|
| 474 |
+
Live demo root endpoint.
|
| 475 |
+
|
| 476 |
+
Hugging Face Spaces typically render the response from `/` in the preview.
|
| 477 |
+
We run a deterministic demo episode over all tasks and return the results.
|
| 478 |
+
"""
|
| 479 |
+
from inference import run_all_tasks
|
| 480 |
+
|
| 481 |
+
results = run_all_tasks()
|
| 482 |
+
payload = {
|
| 483 |
+
"status": "ok",
|
| 484 |
+
"env": "ACRE",
|
| 485 |
+
"version": "1.0.0",
|
| 486 |
+
"message": "ACRE running successfully",
|
| 487 |
+
"results": results,
|
| 488 |
+
}
|
| 489 |
+
return JSONResponse(content=payload)
|
| 490 |
|
| 491 |
|
| 492 |
@app.get("/health", response_model=CompatibilityHealthResponse)
|
|
|
|
| 495 |
return CompatibilityHealthResponse(status="healthy", service="acre-env")
|
| 496 |
|
| 497 |
|
| 498 |
+
@app.get("/demo")
|
| 499 |
+
def demo() -> JSONResponse:
|
| 500 |
+
"""Run all tasks and return JSON results."""
|
| 501 |
+
from inference import run_all_tasks
|
| 502 |
+
|
| 503 |
+
return JSONResponse(content={"results": run_all_tasks()})
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
@app.get("/ui", response_class=HTMLResponse)
|
| 507 |
def demo_ui() -> HTMLResponse:
|
| 508 |
"""Simple UI to compare original and optimized code side-by-side."""
|
| 509 |
return HTMLResponse(content=_demo_html())
|