| """ |
| Agent Proxy Safety Sandbox |
| |
| Enforces limits on agent interactions: step counts, session timeouts, |
| rate limits, and request timeouts. Prevents runaway or abusive sessions. |
| """ |
|
|
| import time |
| import threading |
| import logging |
| from collections import defaultdict |
| from typing import Dict, List |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| class SandboxViolation(Exception): |
| """Raised when a safety limit is exceeded.""" |
| pass |
|
|
|
|
| class SafetySandbox: |
| """Enforces safety limits on agent interactions.""" |
|
|
| def __init__(self, config: dict): |
| sandbox_config = config.get("sandbox", {}) |
| self.max_steps = sandbox_config.get("max_steps", 20) |
| self.max_session_seconds = sandbox_config.get("max_session_seconds", 600) |
| self.rate_limit_per_minute = sandbox_config.get("rate_limit_per_minute", 10) |
| self.request_timeout = sandbox_config.get("request_timeout_seconds", 60) |
|
|
| |
| self._rate_windows: Dict[str, List[float]] = defaultdict(list) |
| self._lock = threading.Lock() |
|
|
| def check_step_limit(self, current_steps: int): |
| """Raise SandboxViolation if step limit reached.""" |
| if current_steps >= self.max_steps: |
| raise SandboxViolation( |
| f"Step limit reached ({self.max_steps}). " |
| f"Please finish the conversation." |
| ) |
|
|
| def check_session_timeout(self, session_start: float): |
| """Raise SandboxViolation if session has timed out.""" |
| elapsed = time.time() - session_start |
| if elapsed > self.max_session_seconds: |
| raise SandboxViolation( |
| f"Session timeout ({self.max_session_seconds}s). " |
| f"Please finish the conversation." |
| ) |
|
|
| def check_rate_limit(self, user_id: str): |
| """Raise SandboxViolation if user is sending too fast.""" |
| now = time.time() |
| window_start = now - 60.0 |
|
|
| with self._lock: |
| |
| timestamps = self._rate_windows[user_id] |
| self._rate_windows[user_id] = [ |
| t for t in timestamps if t > window_start |
| ] |
|
|
| if len(self._rate_windows[user_id]) >= self.rate_limit_per_minute: |
| raise SandboxViolation( |
| f"Rate limit exceeded ({self.rate_limit_per_minute}/min). " |
| f"Please wait before sending another message." |
| ) |
|
|
| |
| self._rate_windows[user_id].append(now) |
|
|
| def get_request_timeout(self) -> float: |
| """Get the timeout in seconds for proxy HTTP requests.""" |
| return self.request_timeout |
|
|