CoolFace
Apppublic

ashucode/metaxhuggingfacehackathon

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
inference.py233 linesDownload Raw Back to root
1from __future__ import annotations2 3import argparse4import os5import re6import subprocess7import sys8import time9from typing import Any, Dict, Optional10 11import requests12from openai import OpenAI13 14 15DEFAULT_BASE_URL = "http://localhost:7860"16ACTION_NAMES = ["UP", "DOWN", "LEFT", "RIGHT", "COLLECT"]17ACTION_NAME_TO_ID = {name: idx for idx, name in enumerate(ACTION_NAMES)}18 19SYSTEM_PROMPT = """You are an expert shopping agent navigating a grid supermarket.20GRID LAYOUT (8 rows x 7 cols):21  - Spawn:    row 0, col 322  - Counter:  row 7, col 3  (EXIT -- go here after collecting all items)23  - Aisles:   A1(1,1) A2(3,1) A3(5,1) A4(1,5) A5(3,5) A6(5,5)24ACTIONS (reply with EXACTLY the action name -- nothing else):25  UP      move one row up    (row - 1)26  DOWN    move one row down  (row + 1)27  LEFT    move one col left  (col - 1)28  RIGHT   move one col right (col + 1)29  COLLECT pick up item at current aisle (only when standing on an aisle that has a target)30RULES:31  1. In Phase 0: navigate to each target aisle and COLLECT all required items.32  2. In Phase 1: return to the Counter at (7, 3) to complete the task.33  3. COLLECT is only valid when action_mask[4] is true (you are on a target aisle).34  4. On HARD level you MUST collect the CLOSEST item first.35  5. Every step costs reward; reach the counter as fast as possible.36Reply with ONE word -- the action name. No explanation, no punctuation."""37 38 39def build_user_message(state: Dict[str, Any]) -> str:40    mask_str = ", ".join(41        f"{name}={'OK' if ok else 'BLOCKED'}"42        for name, ok in zip(ACTION_NAMES, state["action_mask"])43    )44    lines = [45        f"Position : {state['agent_pos']}",46        f"Phase    : {state['phase']} ({state['phase_label']})",47        f"Targets  : {state['targets']}",48        f"Inventory: {state['inventory']}",49        f"Remaining: {[t for t in state['targets'] if t not in state['inventory']]}",50        f"Closest  : {state['closest_target']}",51        f"Steps    : {state['steps_taken']} / {state['steps_taken'] + state['steps_remaining']}",52        f"Total rwd: {state['total_reward']:.2f}",53        f"Masks    : {mask_str}",54        f"Last evt : {state.get('event', '')}",55    ]56    return "\n".join(lines)57 58 59def parse_action(text: str) -> Optional[int]:60    clean = text.strip().upper()61    if clean in ACTION_NAME_TO_ID:62        return ACTION_NAME_TO_ID[clean]63    for name, idx in ACTION_NAME_TO_ID.items():64        if re.search(rf"\b{name}\b", clean):65            return idx66    return None67 68 69def log_start(session_id: str, level: str, model: str):70    print(f"[START] session_id={session_id} level={level} model={model}", flush=True)71 72 73def log_step(step_num: int, action_name: str, state: Dict[str, Any]):74    print(75        f"[STEP] step={step_num} action={action_name} "76        f"step_reward={state.get('step_reward', 0.0):.4f} "77        f"score={state.get('normalised_score', 0.0):.4f}",78        flush=True,79    )80 81 82def log_end(session_id: str, state: Dict[str, Any]):83    print(84        f"[END] session_id={session_id} status={state.get('task_status', 'Unknown')} "85        f"steps={state.get('steps_taken', 0)} "86        f"final_score={state.get('normalised_score', 0.0):.4f}",87        flush=True,88    )89 90 91def ensure_server_running(base_url: str) -> None:92    """Start uvicorn server if not already reachable."""93    try:94        r = requests.get(f"{base_url}/healthz", timeout=3)95        if r.status_code == 200:96            print("[INFO] Server already running.", flush=True)97            return98    except requests.RequestException:99        pass100 101    print("[INFO] Starting uvicorn server...", flush=True)102    subprocess.Popen(103        [104            sys.executable, "-m", "uvicorn",105            "app:app",106            "--host", "0.0.0.0",107            "--port", "7860",108            "--workers", "1",109        ],110        stdout=subprocess.DEVNULL,111        stderr=subprocess.DEVNULL,112    )113    for _ in range(30):114        time.sleep(1)115        try:116            r = requests.get(f"{base_url}/healthz", timeout=2)117            if r.status_code == 200:118                print("[INFO] Server is up.", flush=True)119                return120        except requests.RequestException:121            continue122    raise RuntimeError("Server did not become reachable within 30 seconds.")123 124 125def run_agent(level: str, base_url: str, products=None, seed=None) -> float:126    ensure_server_running(base_url)127 128    api_base_url  = os.environ.get("API_BASE_URL", "").strip()129    model         = os.environ.get("MODEL_NAME", "gpt-4o-mini").strip()130    hf_token      = os.environ.get("HF_TOKEN", "").strip()131    effective_key = hf_token if hf_token else "placeholder-key"132 133    try:134        client = OpenAI(135            api_key=effective_key,136            base_url=api_base_url if api_base_url else None,137        )138    except Exception as exc:139        print(f"[ERROR] Failed to create OpenAI client: {exc}", flush=True)140        sys.exit(1)141 142    reset_payload: Dict[str, Any] = {"level": level}143    if products:144        reset_payload["products"] = products145    if seed is not None:146        reset_payload["seed"] = seed147 148    try:149        r = requests.post(f"{base_url}/reset", json=reset_payload, timeout=10)150        r.raise_for_status()151    except requests.RequestException as exc:152        print(f"[ERROR] Could not reach server at {base_url}: {exc}", flush=True)153        sys.exit(1)154 155    state      = r.json()156    session_id = state["session_id"]157    step_num   = 0158    conversation: list = []159 160    log_start(session_id, level, model)161 162    while (163        not state.get("terminated", False)164        and not state.get("truncated", False)165        and not state.get("done", False)166        and state.get("task_status") == "In-Progress"167    ):168        user_msg = build_user_message(state)169        conversation.append({"role": "user", "content": user_msg})170        trimmed_history = conversation[-20:]171 172        try:173            completion = client.chat.completions.create(174                model=model,175                messages=[{"role": "system", "content": SYSTEM_PROMPT}] + trimmed_history,176                max_tokens=10,177                temperature=0.0,178            )179            reply = completion.choices[0].message.content or ""180        except Exception as exc:181            print(f"[ERROR] LLM call failed: {exc}", flush=True)182            break183 184        action_id = parse_action(reply)185        if action_id is None:186            action_id = 1187 188        action_name = ACTION_NAMES[action_id]189        conversation.append({"role": "assistant", "content": action_name})190 191        try:192            r = requests.post(193                f"{base_url}/step/{session_id}",194                json={"action": action_id},195                timeout=10,196            )197            r.raise_for_status()198        except requests.RequestException as exc:199            print(f"[ERROR] Step request failed: {exc}", flush=True)200            break201 202        state    = r.json()203        step_num += 1204        log_step(step_num, action_name, state)205 206    log_end(session_id, state)207 208    # Clamp strictly between 0 and 1 as required by the evaluator209    raw_score = state.get("normalised_score", 0.0)210    return max(0.001, min(0.999, raw_score))211 212 213if __name__ == "__main__":214    parser = argparse.ArgumentParser(description="LLM agent for SupermarketNav")215    parser.add_argument("--level", default="easy", choices=["easy", "medium", "hard"])216    parser.add_argument("--base-url", default=DEFAULT_BASE_URL, dest="base_url")217    parser.add_argument("--products", nargs="*", help="Optional product names")218    parser.add_argument("--seed", type=int, default=None)219    args = parser.parse_args()220 221    try:222        score = run_agent(223            level=args.level,224            base_url=args.base_url,225            products=args.products,226            seed=args.seed,227        )228        print(f"[DONE] score={score:.4f}", flush=True)229    except Exception as exc:230        print(f"[FATAL] {exc}", flush=True)231        sys.exit(1)232 233    sys.exit(0)