synapse-x / scripts /inference.py
Nithin1026's picture
Initial submission Synapse-X
cb330aa
Raw
History Blame Contribute Delete
9.38 kB
"""
SYNAPSE-X scripts/inference.py
================================
Multi-task inference script used for benchmarking and the report.py helper.
Runs all four tasks and writes inference_results.json.
This is the SCRIPTS version (benchmarking helper). The official hackathon
submission entrypoint is the root-level inference.py.
"""
import json
import os
import re
import sys
import time
from pathlib import Path
from typing import List, Optional
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
try:
from openai import OpenAI
_OPENAI_AVAILABLE = True
except ImportError:
OpenAI = None
_OPENAI_AVAILABLE = False
from agents.baseline import select_action as select_baseline_action
from env.environment import SynapseXEnvironment
from env.grader import TASK_REGISTRY, TASK_SEEDS, grade
from env.models import Action, ActionPayload, Observation
API_BASE_URL = os.environ.get("API_BASE_URL", "https://router.huggingface.co/v1")
MODEL_NAME = os.environ.get("MODEL_NAME", "meta-llama/Meta-Llama-3-8B-Instruct")
HF_TOKEN = os.environ.get("HF_TOKEN", "")
LOCAL_IMAGE_NAME = os.environ.get("LOCAL_IMAGE_NAME", "synapse-x")
MAX_STEPS = 20
TEMPERATURE = 0.0
INFERENCE_MODE = os.environ.get("INFERENCE_MODE", "auto").lower()
TASK_NAMES = tuple(TASK_REGISTRY.keys())
USE_LLM = bool(HF_TOKEN) and INFERENCE_MODE not in {"baseline", "offline"} and _OPENAI_AVAILABLE
SYSTEM_PROMPT = (
"You are an expert task scheduler inside the SYNAPSE-X decision environment.\n\n"
"You receive a JSON observation with pending tasks, current time, and resources.\n"
"Respond with ONLY a single valid JSON action object.\n"
'Format: {"action_type": "execute"|"delay"|"reallocate", "task_id": <int>}\n'
)
FALLBACK_ACTION: ActionPayload = {"action_type": "delay", "task_id": 0}
BENCHMARK = "synapse-x"
def _write(line: str) -> None:
sys.stdout.write(f"{line}\n")
sys.stdout.flush()
def _bool(v: bool) -> str:
return str(bool(v)).lower()
def _compact(v: object) -> str:
return json.dumps(v, separators=(",", ":"), ensure_ascii=True)
def _safe_error(err: Optional[str]) -> str:
if not err:
return "null"
return _compact(str(err).replace("\n", " ").strip())
def log_start(task: str, env: str, model: str) -> None:
_write(f"[START] task={task} env={env} model={model}")
def log_step(step: int, action: ActionPayload, reward: float, done: bool, error: Optional[str]) -> None:
_write(
f"[STEP] step={step} action={_compact(action)} reward={reward:.2f} "
f"done={_bool(done)} error={_safe_error(error)}"
)
def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
rewards_text = ",".join(f"{r:.2f}" for r in rewards)
_write(f"[END] success={_bool(success)} steps={steps} score={score:.3f} rewards={rewards_text}")
def _obs_to_prompt(obs: Observation) -> str:
return json.dumps(
{
"time": obs.time,
"resources": obs.resources,
"tasks": [
{
"id": t.id, "name": t.name, "priority": t.priority,
"risk": t.risk, "uncertainty": t.uncertainty,
"deadline": t.deadline, "future_risk": t.future_risk,
"deadline_pressure": t.deadline_pressure,
"resources_required": t.resources_required,
"completed": t.completed, "failed": t.failed,
}
for t in obs.tasks
],
},
indent=2,
)
def _parse_action(text: str) -> ActionPayload:
m = re.search(r"\{.*?\}", text, re.DOTALL)
if m:
try:
return json.loads(m.group())
except json.JSONDecodeError:
pass
try:
return json.loads(text.strip())
except json.JSONDecodeError:
return FALLBACK_ACTION.copy()
def _coerce_action(raw: ActionPayload) -> Action:
try:
return Action(**raw)
except Exception:
return Action(**FALLBACK_ACTION)
def _baseline(obs: Observation) -> Action:
try:
return select_baseline_action(obs)
except Exception:
return Action(**FALLBACK_ACTION)
def _call_llm(client, obs: Observation) -> ActionPayload:
completion = client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": _obs_to_prompt(obs)},
],
temperature=TEMPERATURE,
max_tokens=64,
stream=False,
)
return _parse_action((completion.choices[0].message.content or "").strip())
def run_baseline_episode(task_name: str, seed: int = 42, verbose: bool = True) -> tuple:
env = SynapseXEnvironment(task_config=TASK_REGISTRY[task_name], seed=seed)
obs = env.reset()
actions: List[ActionPayload] = []
traces: list = []
for step in range(MAX_STEPS):
if obs.episode_done:
break
action = _baseline(obs)
actions.append(action.model_dump())
result = env.step(action)
traces.append({"step": step + 1, "reward": result.reward, "done": result.done, "info": result.info})
if verbose:
err = (result.info or {}).get("error") or None
log_step(step + 1, action.model_dump(), float(result.reward), bool(result.done), err)
obs = result.observation
if result.done:
break
return actions, traces
def run_llm_episode(client, task_name: str, seed: int = 42, verbose: bool = True) -> tuple:
env = SynapseXEnvironment(task_config=TASK_REGISTRY[task_name], seed=seed)
obs = env.reset()
actions: List[ActionPayload] = []
traces: list = []
for step in range(MAX_STEPS):
if obs.episode_done:
break
try:
raw = _call_llm(client, obs)
except Exception as exc:
print(f"[DEBUG] LLM error step {step+1}: {exc}", file=sys.stderr)
raw = _baseline(obs).model_dump()
action = _coerce_action(raw)
actions.append(action.model_dump())
result = env.step(action)
traces.append({"step": step + 1, "reward": result.reward, "done": result.done, "info": result.info})
if verbose:
err = ((result.info or {}).get("error") or None)
log_step(step + 1, action.model_dump(), float(result.reward), bool(result.done), err)
obs = result.observation
if result.done:
break
return actions, traces
def run_episode(task_name: str, seed: int = 42, verbose: bool = False) -> float:
"""Run one episode and return the grader score. Used by report.py."""
use_baseline = not USE_LLM
if use_baseline:
actions, _ = run_baseline_episode(task_name, seed=seed, verbose=verbose)
else:
try:
client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
actions, _ = run_llm_episode(client, task_name, seed=seed, verbose=verbose)
except Exception:
actions, _ = run_baseline_episode(task_name, seed=seed, verbose=verbose)
return grade(task_name, actions).score
def main() -> None:
use_baseline = not USE_LLM
runtime_model = MODEL_NAME if not use_baseline else "baseline-fallback"
client = None
if not use_baseline:
try:
client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
except Exception as exc:
print(f"[DEBUG] Client init failed: {exc}", file=sys.stderr)
use_baseline = True
all_scores: dict = {}
per_task: dict = {}
started_at = time.perf_counter()
for task_name in TASK_NAMES:
seed = TASK_SEEDS.get(task_name, 42)
log_start(task=task_name, env=BENCHMARK, model=runtime_model)
if use_baseline or client is None:
actions, traces = run_baseline_episode(task_name, seed=seed, verbose=True)
else:
actions, traces = run_llm_episode(client, task_name, seed=seed, verbose=True)
result = grade(task_name, actions)
all_scores[task_name] = result.score
per_task[task_name] = {
"score": result.score,
"completion_rate": result.completion_rate,
"efficiency": result.efficiency,
"reward_score": result.reward_score,
"details": result.details,
"actions": actions,
"step_trace": traces,
}
log_end(
success=result.score >= 0.5,
steps=len(actions),
score=float(result.score),
rewards=[float(t["reward"]) for t in traces],
)
average = sum(all_scores.values()) / len(all_scores)
output = {
"model": runtime_model,
"mode": "llm" if not use_baseline else "baseline-fallback",
"api_base_url": API_BASE_URL,
"local_image_name": LOCAL_IMAGE_NAME,
"elapsed_seconds": round(time.perf_counter() - started_at, 4),
"average_score": round(average, 4),
"scores": all_scores,
"tasks": per_task,
}
with open(PROJECT_ROOT / "inference_results.json", "w", encoding="utf-8") as fh:
json.dump(output, fh, indent=2)
if __name__ == "__main__":
main()