"""Validation and guardrails for LLM structured outputs.""" from __future__ import annotations from pam_deploy_graph.constants import ALLOWED_ACTIONS from pam_deploy_graph.models import LlmDeployPlan, LlmIntentResult VALID_INTENTS = {"deploy", "show_usage", "preview", "query_node_ips", "rollback"} FORBIDDEN_TEXT = ("bash ", "powershell ", "deploy.sh", "deploy.ps1", "CLIENT_SECRET=") def validate_intent_result(result: LlmIntentResult) -> None: if result.intent not in VALID_INTENTS: raise ValueError(f"Invalid intent: {result.intent}") if not 0 <= result.confidence <= 1: raise ValueError("Intent confidence must be between 0 and 1") def validate_deploy_plan(plan: LlmDeployPlan) -> None: invalid = [action for action in plan.planned_actions if action not in ALLOWED_ACTIONS] if invalid: raise ValueError(f"Plan contains invalid actions: {', '.join(invalid)}") combined_text = "\n".join([plan.summary, *plan.risk_notes]) lowered = combined_text.lower() forbidden = [item for item in FORBIDDEN_TEXT if item.lower() in lowered] if forbidden: raise ValueError(f"Plan contains forbidden executable text: {', '.join(forbidden)}")