Kandukuri commited on
Commit
ed41ca2
·
1 Parent(s): 83cdd17

Add inference.py, stress_test.py, check.py 1000-sim validated

Browse files
Files changed (3) hide show
  1. check.py +49 -0
  2. inference.py +184 -0
  3. stress_test.py +192 -0
check.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Quick smoke test: checks all 3 required items for repair_env_v2."""
2
+ import sys, os
3
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
4
+
5
+ root = os.path.dirname(os.path.abspath(__file__))
6
+
7
+ print("=" * 50)
8
+ print(" repair_env_v2 — Pre-flight Check")
9
+ print("=" * 50)
10
+
11
+ # --- 1. Dockerfile ---
12
+ df = os.path.join(root, "Dockerfile")
13
+ ok1 = os.path.isfile(df)
14
+ print(f"[{'OK' if ok1 else 'MISSING'}] Dockerfile")
15
+
16
+ # --- 2. inference.py ---
17
+ inf = os.path.join(root, "inference.py")
18
+ ok2 = os.path.isfile(inf)
19
+ print(f"[{'OK' if ok2 else 'MISSING'}] inference.py")
20
+
21
+ # --- 3. openenv.yaml ---
22
+ yaml_f = os.path.join(root, "openenv.yaml")
23
+ ok3 = os.path.isfile(yaml_f)
24
+ print(f"[{'OK' if ok3 else 'MISSING'}] openenv.yaml")
25
+
26
+ # --- 4. inference.py actually runs ---
27
+ print("\n--- Running inference on all 4 tasks (verbose=False) ---")
28
+ try:
29
+ import inference as inf_mod
30
+ from tasks_v2 import TASKS
31
+ results = []
32
+ for tid in TASKS:
33
+ r = inf_mod.run_inference(task_id=tid, verbose=False)
34
+ results.append(r)
35
+
36
+ print(f"\n{'Task':<12} {'Steps':>5} {'Optimal':>7} {'Reward':>7} {'Score':>6} {'Success':>7} {'Health':>6}")
37
+ print("-" * 60)
38
+ for r in results:
39
+ opt = TASKS[r["task_id"]]["optimal_steps"]
40
+ print(f"{r['task_id']:<12} {r['steps_taken']:>5} {opt:>7} {r['total_reward']:>7.3f} {r['score']:>6.3f} {str(r['success']):>7} {r['final_health_ratio']:>6.2%}")
41
+ print("\n[OK] inference.py runs cleanly on all tasks")
42
+ except Exception as e:
43
+ print(f"[FAIL] inference.py error: {e}")
44
+ import traceback; traceback.print_exc()
45
+
46
+ print("\n" + "=" * 50)
47
+ all_ok = ok1 and ok2 and ok3
48
+ print("RESULT:", "ALL CHECKS PASSED" if all_ok else "SOME CHECKS FAILED")
49
+ print("=" * 50)
inference.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ inference.py — OpenEnv standard inference entry point for Repair Strategy System V2.
3
+
4
+ Demonstrates how an external agent interacts with RepairEnvV2:
5
+ 1. Pick a task
6
+ 2. Reset the environment with that task config
7
+ 3. Step through with actions
8
+ 4. Grade the final episode via get_episode_result()
9
+
10
+ Run:
11
+ python inference.py # runs 'easy' task
12
+ python inference.py --task medium
13
+ python inference.py --task hard
14
+ python inference.py --task multi_root
15
+ python inference.py --all # runs all 4 tasks and prints a summary table
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import sys
21
+ import os
22
+ import argparse
23
+
24
+ # Fix Windows console encoding for non-ASCII characters
25
+ if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
26
+ sys.stdout.reconfigure(encoding='utf-8', errors='replace')
27
+
28
+ # Ensure the repo root is on the path regardless of where this is called from
29
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
30
+
31
+ from env_v2 import RepairEnvV2
32
+ from models_v2 import Action, ActionType, ComponentTarget
33
+ from tasks_v2 import TASKS
34
+
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Simple deterministic baseline agent (inspect root-cause area, then repair)
38
+ # ---------------------------------------------------------------------------
39
+
40
+ def inspect_then_fix_agent(obs, task_config: dict, step: int) -> Action:
41
+ """
42
+ Minimal deterministic agent:
43
+ - Step 0: inspect the (first) suspected root cause component
44
+ - Step 1+: attempt repair on whichever component has the highest error_rate
45
+ """
46
+ root_cause = task_config["root_cause"]
47
+ if isinstance(root_cause, list):
48
+ root_cause = root_cause[0]
49
+
50
+ # Step 0: inspect first
51
+ if step == 0:
52
+ inspect_map = {
53
+ "database": ActionType.inspect_database,
54
+ "api": ActionType.inspect_api,
55
+ "cache": ActionType.inspect_cache,
56
+ "queue": ActionType.inspect_queue,
57
+ }
58
+ return Action(
59
+ action_type=inspect_map.get(root_cause, ActionType.inspect_database),
60
+ target=ComponentTarget(root_cause),
61
+ )
62
+
63
+ # Step 1+: pick component with worst error_rate and try to fix it
64
+ worst_comp = max(obs.metrics.items(), key=lambda kv: kv[1].error_rate)[0]
65
+ repair_map = {
66
+ "database": ActionType.repair_database,
67
+ "api": ActionType.restart_service,
68
+ "cache": ActionType.clear_cache,
69
+ "queue": ActionType.restart_service,
70
+ }
71
+ return Action(
72
+ action_type=repair_map.get(worst_comp, ActionType.restart_service),
73
+ target=ComponentTarget(worst_comp),
74
+ )
75
+
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # Main inference loop
79
+ # ---------------------------------------------------------------------------
80
+
81
+ def run_inference(task_id: str = "easy", verbose: bool = True) -> dict:
82
+ """
83
+ Run one full episode on the specified task using the inspect-then-fix agent.
84
+
85
+ Returns the EpisodeResultV2 as a dict with:
86
+ task_id, steps_taken, total_reward, score, success,
87
+ final_health_ratio, efficiency, correct_root_fix_ratio
88
+ """
89
+ if task_id not in TASKS:
90
+ raise ValueError(
91
+ f"Unknown task_id '{task_id}'. Choose from: {list(TASKS.keys())}"
92
+ )
93
+
94
+ config = TASKS[task_id]
95
+
96
+ # --- V2 API: __init__() takes no args; reset() takes (task_config, task_id) ---
97
+ env = RepairEnvV2()
98
+ obs = env.reset(task_config=config, task_id=task_id)
99
+
100
+ done = False
101
+ step = 0
102
+
103
+ if verbose:
104
+ print(f"\n{'='*62}")
105
+ print(f" Task : {task_id}")
106
+ print(f" Difficulty : {config.get('difficulty', task_id)}")
107
+ print(f" Max steps : {config['max_steps']} | Optimal: {config['optimal_steps']}")
108
+ print(f" Description: {config['description']}")
109
+ print(f"{'='*62}")
110
+
111
+ while not done:
112
+ action = inspect_then_fix_agent(obs, config, step)
113
+ obs, reward, done, info = env.step(action)
114
+
115
+ if verbose:
116
+ print(
117
+ f" Step {step + 1:>2}: {str(action.action_type):<22} > {str(action.target):<10}"
118
+ f"| reward={reward.value:+.2f} reason={reward.reason}"
119
+ )
120
+ step += 1
121
+
122
+ # --- Use the env's built-in result calculation ---
123
+ result = env.get_episode_result()
124
+
125
+ if verbose:
126
+ print(f"\n -- Episode complete ------------------------------------------")
127
+ print(f" Steps taken : {result.steps_taken} (optimal: {config['optimal_steps']})")
128
+ print(f" Total reward : {result.total_reward:.4f}")
129
+ print(f" Score : {result.score:.4f}")
130
+ print(f" Success : {result.success}")
131
+ print(f" Final health ratio : {result.final_health_ratio:.2%}")
132
+ print(f" Efficiency : {result.efficiency:.2%}")
133
+ print(f" Correct root fixes : {result.correct_root_fix_ratio:.2%}")
134
+ print(f"{'='*62}\n")
135
+
136
+ return result.model_dump()
137
+
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # CLI entry point
141
+ # ---------------------------------------------------------------------------
142
+
143
+ def main():
144
+ parser = argparse.ArgumentParser(
145
+ description="OpenEnv inference runner — Repair Strategy System V2"
146
+ )
147
+ parser.add_argument(
148
+ "--task",
149
+ default="easy",
150
+ choices=list(TASKS.keys()),
151
+ help="Task ID to run (default: easy)",
152
+ )
153
+ parser.add_argument(
154
+ "--all",
155
+ action="store_true",
156
+ help="Run ALL tasks and print a summary table",
157
+ )
158
+ args = parser.parse_args()
159
+
160
+ if args.all:
161
+ print("\nRunning all tasks...\n")
162
+ results = []
163
+ for tid in TASKS:
164
+ r = run_inference(task_id=tid, verbose=True)
165
+ results.append(r)
166
+
167
+ # Summary table
168
+ print(f"\n{'-'*70}")
169
+ print(f"{'Task':<12} {'Steps':>6} {'Optimal':>8} {'Reward':>8} {'Score':>7} {'Success':>8} {'Health':>7}")
170
+ print(f"{'-'*70}")
171
+ for r in results:
172
+ optimal = TASKS[r['task_id']]['optimal_steps']
173
+ print(
174
+ f"{r['task_id']:<12} {r['steps_taken']:>6} {optimal:>8} "
175
+ f"{r['total_reward']:>8.3f} {r['score']:>7.3f} "
176
+ f"{str(r['success']):>8} {r['final_health_ratio']:>7.2%}"
177
+ )
178
+ print(f"{'-'*70}\n")
179
+ else:
180
+ run_inference(task_id=args.task, verbose=True)
181
+
182
+
183
+ if __name__ == "__main__":
184
+ main()
stress_test.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ stress_test.py — Run 1000 simulations across all tasks with varying seeds.
3
+ Collects stats: success rate, avg score, avg reward, avg steps, failures.
4
+ """
5
+ import sys, os
6
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
7
+
8
+ from env_v2 import RepairEnvV2
9
+ from models_v2 import Action, ActionType, ComponentTarget
10
+ from tasks_v2 import TASKS
11
+
12
+ import random
13
+ import time
14
+ from collections import defaultdict
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # Reuse the same inspect-then-fix agent from inference.py
18
+ # ---------------------------------------------------------------------------
19
+
20
+ def inspect_then_fix_agent(obs, task_config: dict, step: int, rng: random.Random) -> Action:
21
+ root_cause = task_config["root_cause"]
22
+ if isinstance(root_cause, list):
23
+ root_cause = root_cause[0]
24
+
25
+ if step == 0:
26
+ inspect_map = {
27
+ "database": ActionType.inspect_database,
28
+ "api": ActionType.inspect_api,
29
+ "cache": ActionType.inspect_cache,
30
+ "queue": ActionType.inspect_queue,
31
+ }
32
+ return Action(
33
+ action_type=inspect_map.get(root_cause, ActionType.inspect_database),
34
+ target=ComponentTarget(root_cause),
35
+ )
36
+
37
+ worst_comp = max(obs.metrics.items(), key=lambda kv: kv[1].error_rate)[0]
38
+ repair_map = {
39
+ "database": ActionType.repair_database,
40
+ "api": ActionType.restart_service,
41
+ "cache": ActionType.clear_cache,
42
+ "queue": ActionType.restart_service,
43
+ }
44
+ return Action(
45
+ action_type=repair_map.get(worst_comp, ActionType.restart_service),
46
+ target=ComponentTarget(worst_comp),
47
+ )
48
+
49
+
50
+ def random_agent(obs, rng: random.Random) -> Action:
51
+ """Completely random agent for chaos testing."""
52
+ return Action(
53
+ action_type=rng.choice(list(ActionType)),
54
+ target=rng.choice(list(ComponentTarget)),
55
+ )
56
+
57
+
58
+ # ---------------------------------------------------------------------------
59
+ # Run N simulations
60
+ # ---------------------------------------------------------------------------
61
+
62
+ def run_stress_test(n_total: int = 1000):
63
+ task_ids = list(TASKS.keys())
64
+ n_per_task = n_total // len(task_ids)
65
+
66
+ # Stats per task per agent
67
+ stats = {}
68
+ for task_id in task_ids:
69
+ stats[task_id] = {
70
+ "baseline": defaultdict(list),
71
+ "random": defaultdict(list),
72
+ }
73
+
74
+ errors = []
75
+ total_runs = 0
76
+ start = time.time()
77
+
78
+ print(f"\n{'='*60}")
79
+ print(f" Stress Test: {n_total} simulations")
80
+ print(f" Tasks: {task_ids}")
81
+ print(f" {n_per_task} runs per task x 2 agents = {n_per_task * 2 * len(task_ids)} total episodes")
82
+ print(f"{'='*60}\n")
83
+
84
+ for task_id in task_ids:
85
+ config = TASKS[task_id]
86
+ print(f" Running task '{task_id}' ({n_per_task} seeds x 2 agents)...", end="", flush=True)
87
+
88
+ for seed in range(n_per_task):
89
+ for agent_name in ["baseline", "random"]:
90
+ try:
91
+ env = RepairEnvV2()
92
+ obs = env.reset(task_config=config, task_id=task_id)
93
+
94
+ done = False
95
+ step = 0
96
+ rng = random.Random(seed)
97
+
98
+ while not done:
99
+ if agent_name == "baseline":
100
+ action = inspect_then_fix_agent(obs, config, step, rng)
101
+ else:
102
+ action = random_agent(obs, rng)
103
+
104
+ obs, reward, done, info = env.step(action)
105
+ step += 1
106
+
107
+ result = env.get_episode_result()
108
+ s = stats[task_id][agent_name]
109
+ s["scores"].append(result.score)
110
+ s["rewards"].append(result.total_reward)
111
+ s["steps"].append(result.steps_taken)
112
+ s["success"].append(int(result.success))
113
+ s["health"].append(result.final_health_ratio)
114
+ total_runs += 1
115
+
116
+ except Exception as e:
117
+ errors.append({"task": task_id, "seed": seed, "agent": agent_name, "error": str(e)})
118
+
119
+ print(f" done.")
120
+
121
+ elapsed = time.time() - start
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # Print results
125
+ # ---------------------------------------------------------------------------
126
+
127
+ print(f"\n{'='*60}")
128
+ print(f" RESULTS ({total_runs} episodes in {elapsed:.1f}s)")
129
+ print(f"{'='*60}\n")
130
+
131
+ for task_id in task_ids:
132
+ config = TASKS[task_id]
133
+ print(f" Task: {task_id} [{config.get('difficulty','?')}] (optimal={config['optimal_steps']})")
134
+ print(f" {'-'*55}")
135
+
136
+ for agent_name in ["baseline", "random"]:
137
+ s = stats[task_id][agent_name]
138
+ n = len(s["scores"])
139
+ if n == 0:
140
+ print(f" {agent_name:<10}: NO DATA")
141
+ continue
142
+
143
+ avg_score = sum(s["scores"]) / n
144
+ avg_reward = sum(s["rewards"]) / n
145
+ avg_steps = sum(s["steps"]) / n
146
+ success_pct = sum(s["success"]) / n * 100
147
+ avg_health = sum(s["health"]) / n
148
+ min_score = min(s["scores"])
149
+ max_score = max(s["scores"])
150
+
151
+ print(f" {agent_name:<10}: n={n} success={success_pct:5.1f}% "
152
+ f"score={avg_score:.3f} [{min_score:.2f}-{max_score:.2f}] "
153
+ f"reward={avg_reward:+.2f} steps={avg_steps:.1f} health={avg_health:.2%}")
154
+ print()
155
+
156
+ # Summary table
157
+ print(f" {'Task':<12} {'Agent':<10} {'Success%':>9} {'AvgScore':>9} {'AvgReward':>10} {'AvgSteps':>9}")
158
+ print(f" {'-'*58}")
159
+ for task_id in task_ids:
160
+ for agent_name in ["baseline", "random"]:
161
+ s = stats[task_id][agent_name]
162
+ n = len(s["scores"])
163
+ if n == 0: continue
164
+ print(f" {task_id:<12} {agent_name:<10} "
165
+ f"{sum(s['success'])/n*100:>8.1f}% "
166
+ f"{sum(s['scores'])/n:>9.4f} "
167
+ f"{sum(s['rewards'])/n:>10.3f} "
168
+ f"{sum(s['steps'])/n:>9.1f}")
169
+ print(f" {'-'*58}")
170
+
171
+ if errors:
172
+ print(f"\n ERRORS ({len(errors)} total):")
173
+ for e in errors[:10]:
174
+ print(f" [{e['task']} seed={e['seed']} {e['agent']}] {e['error']}")
175
+ if len(errors) > 10:
176
+ print(f" ... and {len(errors) - 10} more")
177
+ else:
178
+ print(f"\n No errors! All {total_runs} episodes completed cleanly.")
179
+
180
+ print(f"\n{'='*60}")
181
+ print(f" Stress test complete. {total_runs}/{n_per_task * 2 * len(task_ids)} episodes ran successfully.")
182
+ print(f"{'='*60}\n")
183
+
184
+ return stats, errors
185
+
186
+
187
+ if __name__ == "__main__":
188
+ import argparse
189
+ parser = argparse.ArgumentParser()
190
+ parser.add_argument("--n", type=int, default=1000, help="Total simulations (split across tasks)")
191
+ args = parser.parse_args()
192
+ run_stress_test(n_total=args.n)