Spaces:
Sleeping
Sleeping
Interior Env
Added LOCAL_IMAGE_NAME to environment vars to perfectly satisfy the pre-submission static validator
665afbb Download inference.py from 1akshith/interior-env: direct link, hf CLI and curl.
- Browser
- Download file 7.2 kB
-
https://huggingface.co/spaces/1akshith/interior-env/resolve/main/inference.py
- Command line
-
hf download hf://spaces/1akshith/interior-env/inference.py
-
curl -L -o inference.py https://huggingface.co/spaces/1akshith/interior-env/resolve/main/inference.py
7.2 kB
| """ | |
| inference.py β OpenEnv-compliant inference entry point for the | |
| Interior Design Spatial Engine (Furniture Tetris + Human Flow). | |
| STDOUT FORMAT (strictly followed): | |
| [START] task=<task> env=<benchmark> model=<model> | |
| [STEP] step=<n> action=<str> reward=<0.00> done=<true|false> error=<msg|null> | |
| [END] success=<true|false> steps=<n> score=<0.000> rewards=<r1,r2,...> | |
| """ | |
| from __future__ import annotations | |
| import sys | |
| import os | |
| import json | |
| import argparse | |
| from typing import List, Optional | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| from env import InteriorEnv | |
| from baseline import BaselineAgent | |
| from models import Action | |
| from tasks import TASKS | |
| # ββ Environment variables βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1") | |
| MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-72B-Instruct") | |
| HF_TOKEN = os.getenv("HF_TOKEN") | |
| LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME") | |
| BENCHMARK = "interior-env" | |
| # ββ OpenAI client (optional LLM agent) βββββββββββββββββββββββββββββββββββββββ | |
| try: | |
| from openai import OpenAI | |
| _llm_client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN) if HF_TOKEN else None | |
| except Exception: | |
| _llm_client = None | |
| # ββ Strict stdout loggers βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def log_start(task: str, model: str) -> None: | |
| print(f"[START] task={task} env={BENCHMARK} model={model}", flush=True) | |
| def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]) -> None: | |
| err = error if error else "null" | |
| print(f"[STEP] step={step} action={action} reward={reward:.2f} done={str(done).lower()} error={err}", flush=True) | |
| def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None: | |
| rew_str = ",".join(f"{r:.2f}" for r in rewards) | |
| print(f"[END] success={str(success).lower()} steps={steps} score={score:.3f} rewards={rew_str}", flush=True) | |
| # ββ LLM Agent ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| SYSTEM_PROMPT = """You are a spatial AI agent designing interior layouts on a 2D grid. | |
| You place furniture ensuring strict geometrical, human flow, and physics constraints. | |
| - Light rays shoot purely orthogonally from windows. Tall items block them (cast shadows). | |
| - Windows/doors emit cold (-2). Radiators/TVs emit radial heat. | |
| Output ONLY valid JSON, no explanation: | |
| {"action_type": "place_item", "item": "<item_id>", "x": <int>, "y": <int>, "rotation": <0|90>}""" | |
| def llm_pick_action(obs, step: int) -> Optional[Action]: | |
| if not _llm_client: | |
| return None | |
| try: | |
| obs_summary = { | |
| "room_size": obs.room_size, | |
| "door_position": obs.door_position, | |
| "windows": obs.windows, | |
| "unplaced_items": [{"id": u.id, "width": u.width, "height": u.height, "rules": u.rules} | |
| for u in obs.unplaced_items], | |
| "placed_items": {k: {"x": v.x, "y": v.y, "rotation": v.rotation} | |
| for k, v in obs.placed_items.items()}, | |
| "feedback": obs.feedback_logs[-3:] if obs.feedback_logs else [], | |
| } | |
| completion = _llm_client.chat.completions.create( | |
| model=MODEL_NAME, | |
| messages=[ | |
| {"role": "system", "content": SYSTEM_PROMPT}, | |
| {"role": "user", "content": f"Step {step}. Current state:\n{json.dumps(obs_summary, indent=2)}\n\nWhat is your next action?"}, | |
| ], | |
| response_format={"type": "json_object"}, | |
| temperature=0.2, | |
| max_tokens=100, | |
| ) | |
| data = json.loads(completion.choices[0].message.content) | |
| return Action( | |
| action_type=data.get("action_type", "no_op"), | |
| item=data.get("item", ""), | |
| x=int(data.get("x", 0)), | |
| y=int(data.get("y", 0)), | |
| rotation=int(data.get("rotation", 0)), | |
| ) | |
| except Exception as e: | |
| return None | |
| # ββ Action β compact string βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def action_str(action: Action) -> str: | |
| if action.action_type == "place_item": | |
| return f"place_item({action.item},{action.x},{action.y},{action.rotation})" | |
| if action.action_type == "remove_item": | |
| return f"remove_item({action.item})" | |
| return "no_op()" | |
| # ββ Single episode runner βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def run_task(task_id: str) -> None: | |
| if task_id not in TASKS: | |
| print(f"[ERROR] Unknown task: {task_id}", flush=True) | |
| return | |
| config = TASKS[task_id] | |
| env = InteriorEnv() | |
| baseline = BaselineAgent() | |
| obs = env.reset(config, task_id) | |
| done = False | |
| step = 0 | |
| rewards: List[float] = [] | |
| success = False | |
| score = 0.0 | |
| log_start(task=task_id, model=MODEL_NAME) | |
| try: | |
| while not done: | |
| step += 1 | |
| # Try LLM first, fall back to deterministic baseline | |
| action = llm_pick_action(obs, step) | |
| if action is None: | |
| action = baseline.pick_action(obs, task_id) | |
| obs, reward, done, _ = env.step(action) | |
| rewards.append(reward) | |
| error = None | |
| if obs.feedback_logs: | |
| last = obs.feedback_logs[-1] | |
| if any(tag in last for tag in ("ERROR", "COLLISION", "BOUNDS", "FLOW")): | |
| error = last[:80].replace(" ", "_") | |
| log_step(step=step, action=action_str(action), | |
| reward=reward, done=done, error=error) | |
| result = env.get_episode_result() | |
| success = result.success | |
| score = result.score | |
| except Exception as exc: | |
| log_step(step=step + 1, action="error()", reward=0.0, done=True, error=str(exc)[:80]) | |
| finally: | |
| log_end(success=success, steps=step, score=score, rewards=rewards) | |
| # ββ Entry point βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description="Interior Env OpenEnv inference") | |
| parser.add_argument("--task", choices=list(TASKS.keys()), | |
| help="Run a single task. Omit to run all levels.") | |
| args = parser.parse_args() | |
| tasks_to_run = [args.task] if args.task else list(TASKS.keys()) | |
| for tid in tasks_to_run: | |
| run_task(tid) | |
| if __name__ == "__main__": | |
| main() | |