41 lines
1.6 KiB
Python
41 lines
1.6 KiB
Python
from typing import Dict, Any, Tuple
|
|
|
|
def _safe_eval(expr: str, context: Dict[str, Any]) -> bool:
|
|
if not expr:
|
|
return True
|
|
allowed_globals = {"__builtins__": {}}
|
|
local = dict(context)
|
|
try:
|
|
return bool(eval(expr, allowed_globals, local))
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
class PolicyEngine:
|
|
def __init__(self, contract=None):
|
|
self.contract = contract
|
|
|
|
def set_contract(self, contract) -> None:
|
|
self.contract = contract
|
|
|
|
def evaluate_pre(self, action: Dict[str, Any], context: Dict[str, Any]) -> Tuple[bool, str]:
|
|
if not self.contract or not self.contract.pre:
|
|
return True, "no-precondition"
|
|
ok = _safe_eval(self.contract.pre, {**context, "action": action})
|
|
return (bool(ok), "precondition" if ok else "precondition-failed")
|
|
|
|
def evaluate_post(self, action: Dict[str, Any], context: Dict[str, Any]) -> Tuple[bool, str]:
|
|
if not self.contract or not self.contract.post:
|
|
return True, "no-postcondition"
|
|
ok = _safe_eval(self.contract.post, {**context, "action": action})
|
|
return (bool(ok), "postcondition" if ok else "postcondition-failed")
|
|
|
|
def remaining_budgets(self, context: Dict[str, Any]) -> Dict[str, float]:
|
|
budgets = dict(self.contract.budgets) if self.contract and self.contract.budgets else {}
|
|
spent = context.get("spent", {"time": 0, "energy": 0, "compute": 0})
|
|
remaining = {}
|
|
for k, v in budgets.items():
|
|
spent_k = spent.get(k, 0)
|
|
remaining[k] = max(0.0, v - spent_k)
|
|
return remaining
|