RootCauseArena / demo.py
Kandukuri
Add demo.py as duplicate of inference.py for strict grader matching
5bee1c0
Raw History Blame Contribute Delete
6.52 kB
"""
inference.py — OpenEnv standard inference entry point for Repair Strategy System V2.
Demonstrates how an external agent interacts with RepairEnvV2:
1. Pick a task
2. Reset the environment with that task config
3. Step through with actions
4. Grade the final episode via get_episode_result()
Run:
python inference.py # runs 'easy' task
python inference.py --task medium
python inference.py --task hard
python inference.py --task multi_root
python inference.py --all # runs all 4 tasks and prints a summary table
"""
from __future__ import annotations
import sys
import os
import argparse
# Fix Windows console encoding for non-ASCII characters
if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
# Ensure the repo root is on the path regardless of where this is called from
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from env_v2 import RepairEnvV2
from models_v2 import Action, ActionType, ComponentTarget
from tasks_v2 import TASKS
# ---------------------------------------------------------------------------
# Simple deterministic baseline agent (inspect root-cause area, then repair)
# ---------------------------------------------------------------------------
def inspect_then_fix_agent(obs, task_config: dict, step: int) -> Action:
"""
Minimal deterministic agent:
- Step 0: inspect the (first) suspected root cause component
- Step 1+: attempt repair on whichever component has the highest error_rate
"""
root_cause = task_config["root_cause"]
if isinstance(root_cause, list):
root_cause = root_cause[0]
# Step 0: inspect first
if step == 0:
inspect_map = {
"database": ActionType.inspect_database,
"api": ActionType.inspect_api,
"cache": ActionType.inspect_cache,
"queue": ActionType.inspect_queue,
}
return Action(
action_type=inspect_map.get(root_cause, ActionType.inspect_database),
target=ComponentTarget(root_cause),
)
# Step 1+: pick component with worst error_rate and try to fix it
worst_comp = max(obs.metrics.items(), key=lambda kv: kv[1].error_rate)[0]
repair_map = {
"database": ActionType.repair_database,
"api": ActionType.restart_service,
"cache": ActionType.clear_cache,
"queue": ActionType.restart_service,
}
return Action(
action_type=repair_map.get(worst_comp, ActionType.restart_service),
target=ComponentTarget(worst_comp),
)
# ---------------------------------------------------------------------------
# Main inference loop
# ---------------------------------------------------------------------------
def run_inference(task_id: str = "easy", verbose: bool = True) -> dict:
"""
Run one full episode on the specified task using the inspect-then-fix agent.
Returns the EpisodeResultV2 as a dict with:
task_id, steps_taken, total_reward, score, success,
final_health_ratio, efficiency, correct_root_fix_ratio
"""
if task_id not in TASKS:
raise ValueError(
f"Unknown task_id '{task_id}'. Choose from: {list(TASKS.keys())}"
)
config = TASKS[task_id]
# --- V2 API: __init__() takes no args; reset() takes (task_config, task_id) ---
env = RepairEnvV2()
obs = env.reset(task_config=config, task_id=task_id)
done = False
step = 0
if verbose:
print(f"\n{'='*62}")
print(f" Task : {task_id}")
print(f" Difficulty : {config.get('difficulty', task_id)}")
print(f" Max steps : {config['max_steps']} | Optimal: {config['optimal_steps']}")
print(f" Description: {config['description']}")
print(f"{'='*62}")
while not done:
action = inspect_then_fix_agent(obs, config, step)
obs, reward, done, info = env.step(action)
if verbose:
print(
f" Step {step + 1:>2}: {str(action.action_type):<22} > {str(action.target):<10}"
f"| reward={reward.value:+.2f} reason={reward.reason}"
)
step += 1
# --- Use the env's built-in result calculation ---
result = env.get_episode_result()
if verbose:
print(f"\n -- Episode complete ------------------------------------------")
print(f" Steps taken : {result.steps_taken} (optimal: {config['optimal_steps']})")
print(f" Total reward : {result.total_reward:.4f}")
print(f" Score : {result.score:.4f}")
print(f" Success : {result.success}")
print(f" Final health ratio : {result.final_health_ratio:.2%}")
print(f" Efficiency : {result.efficiency:.2%}")
print(f" Correct root fixes : {result.correct_root_fix_ratio:.2%}")
print(f"{'='*62}\n")
return result.model_dump()
# ---------------------------------------------------------------------------
# CLI entry point
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description="OpenEnv inference runner — Repair Strategy System V2"
)
parser.add_argument(
"--task",
default="easy",
choices=list(TASKS.keys()),
help="Task ID to run (default: easy)",
)
parser.add_argument(
"--all",
action="store_true",
help="Run ALL tasks and print a summary table",
)
args = parser.parse_args()
if args.all:
print("\nRunning all tasks...\n")
results = []
for tid in TASKS:
r = run_inference(task_id=tid, verbose=True)
results.append(r)
# Summary table
print(f"\n{'-'*70}")
print(f"{'Task':<12} {'Steps':>6} {'Optimal':>8} {'Reward':>8} {'Score':>7} {'Success':>8} {'Health':>7}")
print(f"{'-'*70}")
for r in results:
optimal = TASKS[r['task_id']]['optimal_steps']
print(
f"{r['task_id']:<12} {r['steps_taken']:>6} {optimal:>8} "
f"{r['total_reward']:>8.3f} {r['score']:>7.3f} "
f"{str(r['success']):>8} {r['final_health_ratio']:>7.2%}"
)
print(f"{'-'*70}\n")
else:
run_inference(task_id=args.task, verbose=True)
if __name__ == "__main__":
main()