DevOps_Debugger / scenarios /registry.py
printf-sourav's picture
Initial commit
27cdb3e
Raw
History Blame Contribute Delete
4.85 kB
"""
Scenario Registry — Central registry for all DevOps troubleshooting scenarios.
Each scenario defines a broken environment state, success conditions,
and optimal fix sequences for reward shaping.
"""
from __future__ import annotations
import random
from dataclasses import dataclass, field
from typing import Callable, Dict, List, Optional
@dataclass
class Scenario:
"""A single DevOps troubleshooting scenario.
Attributes:
id: Unique string identifier (e.g., 'missing_flask').
level: Difficulty level (1=single-step, 2=two-step, 3=multi-step).
description: Human-readable description of the broken state.
initial_state: Dict defining files, env vars, and processes to set up.
success_condition: Callable that takes last output and returns True if fixed.
hint_commands: Optimal fix sequence (used for reward shaping).
error_fingerprint: Regex or keyword to classify the error type.
setup_commands: Shell commands to create the broken state in the sandbox.
initial_error_log: The error output the agent sees on reset.
verification_command: Command to run to check if the fix worked.
"""
id: str
level: int
description: str
initial_state: Dict
success_condition: Callable[[str], bool]
hint_commands: List[str]
error_fingerprint: str
setup_commands: List[str] = field(default_factory=list)
initial_error_log: str = ""
verification_command: str = ""
class ScenarioRegistry:
"""Central registry holding all scenarios, queryable by level and ID.
Usage:
registry = ScenarioRegistry()
registry.register_defaults()
scenario = registry.get_random(level=1)
"""
def __init__(self) -> None:
"""Initialize an empty registry."""
self._scenarios: Dict[str, Scenario] = {}
def register(self, scenario: Scenario) -> None:
"""Register a scenario in the registry.
Args:
scenario: The Scenario to add.
Raises:
ValueError: If a scenario with the same ID already exists.
"""
if scenario.id in self._scenarios:
raise ValueError(f"Scenario '{scenario.id}' already registered")
self._scenarios[scenario.id] = scenario
def get(self, scenario_id: str) -> Scenario:
"""Get a scenario by its ID.
Args:
scenario_id: The unique scenario identifier.
Returns:
The matching Scenario.
Raises:
KeyError: If the scenario ID is not found.
"""
if scenario_id not in self._scenarios:
raise KeyError(f"Scenario '{scenario_id}' not found in registry")
return self._scenarios[scenario_id]
def get_by_level(self, level: int) -> List[Scenario]:
"""Get all scenarios at a given difficulty level.
Args:
level: Difficulty level (1, 2, or 3).
Returns:
List of scenarios at the specified level.
"""
return [s for s in self._scenarios.values() if s.level == level]
def get_random(self, level: Optional[int] = None) -> Scenario:
"""Get a random scenario, optionally filtered by level.
Args:
level: If provided, filter to this level only.
Returns:
A randomly selected Scenario.
Raises:
ValueError: If no scenarios match the criteria.
"""
candidates = self.get_by_level(level) if level else list(self._scenarios.values())
if not candidates:
raise ValueError(f"No scenarios available for level={level}")
return random.choice(candidates)
def get_all(self) -> List[Scenario]:
"""Get all registered scenarios.
Returns:
List of all scenarios in the registry.
"""
return list(self._scenarios.values())
def list_ids(self) -> List[str]:
"""Get all registered scenario IDs.
Returns:
List of scenario ID strings.
"""
return list(self._scenarios.keys())
def register_defaults(self) -> None:
"""Register all built-in scenarios (Levels 1-3).
This loads all default scenarios from the level modules.
"""
from scenarios.level1.scenarios import get_level1_scenarios
from scenarios.level2.scenarios import get_level2_scenarios
from scenarios.level3.scenarios import get_level3_scenarios
for scenario in get_level1_scenarios():
self.register(scenario)
for scenario in get_level2_scenarios():
self.register(scenario)
for scenario in get_level3_scenarios():
self.register(scenario)
@property
def count(self) -> int:
"""Total number of registered scenarios."""
return len(self._scenarios)