Delete infrastructure
Browse files- infrastructure/__init__.py +0 -23
- infrastructure/azure/__init__.py +0 -4
- infrastructure/azure/azure_models.py +0 -17
- infrastructure/azure/azure_simulator.py +0 -89
- infrastructure/constants.py +0 -21
- infrastructure/cost_estimator.py +0 -102
- infrastructure/healing_intent.py +0 -848
- infrastructure/intents.py +0 -168
- infrastructure/policies.py +0 -171
- infrastructure/risk_engine.py +0 -105
infrastructure/__init__.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
from .intents import (
|
| 2 |
-
ResourceType, PermissionLevel, Environment, ChangeScope,
|
| 3 |
-
ProvisionResourceIntent, DeployConfigurationIntent, GrantAccessIntent,
|
| 4 |
-
InfrastructureIntent
|
| 5 |
-
)
|
| 6 |
-
from .policies import (
|
| 7 |
-
Policy, RegionAllowedPolicy, ResourceTypeRestrictedPolicy,
|
| 8 |
-
MaxPermissionLevelPolicy, CostThresholdPolicy, PolicyEvaluator,
|
| 9 |
-
allow_all, deny_all
|
| 10 |
-
)
|
| 11 |
-
from .cost_estimator import CostEstimator
|
| 12 |
-
from .risk_engine import RiskEngine
|
| 13 |
-
from .healing_intent import HealingIntent, RecommendedAction, IntentSource
|
| 14 |
-
from .azure.azure_simulator import AzureInfrastructureSimulator
|
| 15 |
-
|
| 16 |
-
__all__ = [
|
| 17 |
-
"ResourceType", "PermissionLevel", "Environment", "ChangeScope",
|
| 18 |
-
"ProvisionResourceIntent", "DeployConfigurationIntent", "GrantAccessIntent",
|
| 19 |
-
"InfrastructureIntent", "Policy", "RegionAllowedPolicy",
|
| 20 |
-
"ResourceTypeRestrictedPolicy", "MaxPermissionLevelPolicy", "CostThresholdPolicy",
|
| 21 |
-
"PolicyEvaluator", "allow_all", "deny_all", "CostEstimator", "RiskEngine",
|
| 22 |
-
"HealingIntent", "RecommendedAction", "IntentSource", "AzureInfrastructureSimulator"
|
| 23 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/azure/__init__.py
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
from .azure_simulator import AzureInfrastructureSimulator
|
| 2 |
-
from .azure_models import AzureResource, AzureDeployment
|
| 3 |
-
|
| 4 |
-
__all__ = ["AzureInfrastructureSimulator", "AzureResource", "AzureDeployment"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/azure/azure_models.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Azure-specific models (minimal for simulation).
|
| 3 |
-
"""
|
| 4 |
-
from pydantic import BaseModel
|
| 5 |
-
from typing import Optional, Dict, Any
|
| 6 |
-
|
| 7 |
-
class AzureResource(BaseModel):
|
| 8 |
-
resource_id: str
|
| 9 |
-
resource_type: str
|
| 10 |
-
location: str
|
| 11 |
-
tags: Dict[str, str] = {}
|
| 12 |
-
|
| 13 |
-
class AzureDeployment(BaseModel):
|
| 14 |
-
deployment_id: str
|
| 15 |
-
template: Dict[str, Any]
|
| 16 |
-
parameters: Dict[str, Any]
|
| 17 |
-
resource_group: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/azure/azure_simulator.py
DELETED
|
@@ -1,89 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Azure Infrastructure Simulator – Main orchestration engine.
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
from typing import List, Optional, Dict, Any
|
| 6 |
-
|
| 7 |
-
from ..intents import (
|
| 8 |
-
InfrastructureIntent,
|
| 9 |
-
ProvisionResourceIntent,
|
| 10 |
-
)
|
| 11 |
-
from ..policies import (
|
| 12 |
-
Policy,
|
| 13 |
-
PolicyEvaluator,
|
| 14 |
-
CostThresholdPolicy,
|
| 15 |
-
)
|
| 16 |
-
from ..cost_estimator import CostEstimator
|
| 17 |
-
from ..risk_engine import RiskEngine
|
| 18 |
-
from ..healing_intent import (
|
| 19 |
-
HealingIntent,
|
| 20 |
-
RecommendedAction,
|
| 21 |
-
IntentSource,
|
| 22 |
-
)
|
| 23 |
-
from ..constants import MAX_POLICY_VIOLATIONS
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class AzureInfrastructureSimulator:
|
| 27 |
-
def __init__(
|
| 28 |
-
self,
|
| 29 |
-
policy: Policy,
|
| 30 |
-
pricing_file: Optional[str] = None,
|
| 31 |
-
risk_factors: Optional[List] = None,
|
| 32 |
-
):
|
| 33 |
-
self._policy_evaluator = PolicyEvaluator(policy)
|
| 34 |
-
self._cost_estimator = CostEstimator(pricing_file)
|
| 35 |
-
self._risk_engine = RiskEngine(risk_factors if risk_factors else RiskEngine.DEFAULT_FACTORS)
|
| 36 |
-
|
| 37 |
-
def evaluate(self, intent: InfrastructureIntent) -> HealingIntent:
|
| 38 |
-
cost = None
|
| 39 |
-
if isinstance(intent, ProvisionResourceIntent):
|
| 40 |
-
cost = self._cost_estimator.estimate_monthly_cost(intent)
|
| 41 |
-
|
| 42 |
-
context = {"cost_estimate": cost} if cost is not None else {}
|
| 43 |
-
violations = self._policy_evaluator.evaluate(intent, context)
|
| 44 |
-
|
| 45 |
-
if len(violations) > MAX_POLICY_VIOLATIONS:
|
| 46 |
-
violations = violations[:MAX_POLICY_VIOLATIONS]
|
| 47 |
-
|
| 48 |
-
risk_score, explanation, contributions = self._risk_engine.calculate_risk(
|
| 49 |
-
intent, cost, violations
|
| 50 |
-
)
|
| 51 |
-
|
| 52 |
-
if risk_score > 0.8 or violations:
|
| 53 |
-
recommended_action = RecommendedAction.DENY
|
| 54 |
-
elif risk_score > 0.4:
|
| 55 |
-
recommended_action = RecommendedAction.ESCALATE
|
| 56 |
-
else:
|
| 57 |
-
recommended_action = RecommendedAction.APPROVE
|
| 58 |
-
|
| 59 |
-
justification_parts = [f"Risk score: {risk_score:.2f}."]
|
| 60 |
-
if cost is not None:
|
| 61 |
-
justification_parts.append(f"Estimated monthly cost: ${cost:.2f}.")
|
| 62 |
-
if violations:
|
| 63 |
-
justification_parts.append(f"Policy violations: {'; '.join(violations)}.")
|
| 64 |
-
justification_parts.append(explanation)
|
| 65 |
-
justification = " ".join(justification_parts)
|
| 66 |
-
|
| 67 |
-
intent_summary = f"{intent.intent_type} requested by {intent.requester}"
|
| 68 |
-
|
| 69 |
-
details = {
|
| 70 |
-
"cost_estimate": cost,
|
| 71 |
-
"violations": violations,
|
| 72 |
-
"risk_score": risk_score,
|
| 73 |
-
"factor_contributions": contributions,
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
healing_intent = HealingIntent(
|
| 77 |
-
intent_id=intent.intent_id,
|
| 78 |
-
intent_summary=intent_summary,
|
| 79 |
-
cost_projection=cost,
|
| 80 |
-
risk_score=risk_score,
|
| 81 |
-
policy_violations=violations,
|
| 82 |
-
recommended_action=recommended_action,
|
| 83 |
-
justification=justification,
|
| 84 |
-
confidence_score=0.9,
|
| 85 |
-
evaluation_details=details,
|
| 86 |
-
source=IntentSource.INFRASTRUCTURE_ANALYSIS,
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
return healing_intent.mark_as_oss_advisory()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/constants.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
ARF OSS constants – shared across modules.
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
OSS_EDITION = "OSS"
|
| 6 |
-
OSS_LICENSE = "Apache 2.0"
|
| 7 |
-
ENTERPRISE_UPGRADE_URL = "https://huggingface.co/spaces/petter2025/agentic-reliability-framework?enterprise"
|
| 8 |
-
EXECUTION_ALLOWED = False
|
| 9 |
-
|
| 10 |
-
# Limits
|
| 11 |
-
MAX_SIMILARITY_CACHE = 100
|
| 12 |
-
SIMILARITY_THRESHOLD = 0.7
|
| 13 |
-
MAX_POLICY_VIOLATIONS = 10
|
| 14 |
-
MAX_RISK_FACTORS = 20
|
| 15 |
-
MAX_COST_PROJECTIONS = 5
|
| 16 |
-
MAX_DECISION_TREE_DEPTH = 5
|
| 17 |
-
MAX_ALTERNATIVE_ACTIONS = 5
|
| 18 |
-
|
| 19 |
-
class OSSBoundaryError(Exception):
|
| 20 |
-
"""Raised when OSS edition attempts an enterprise-only operation."""
|
| 21 |
-
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/cost_estimator.py
DELETED
|
@@ -1,102 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Cost Estimation Engine – Deterministic pricing with Bayesian uncertainty.
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
import os
|
| 6 |
-
from functools import lru_cache
|
| 7 |
-
from typing import Dict, Optional, Union, Any
|
| 8 |
-
import yaml
|
| 9 |
-
|
| 10 |
-
from .intents import ProvisionResourceIntent, ResourceType
|
| 11 |
-
|
| 12 |
-
class CostEstimator:
|
| 13 |
-
DEFAULT_PRICING = {
|
| 14 |
-
ResourceType.VM: {
|
| 15 |
-
"Standard_D2s_v3": 70.0,
|
| 16 |
-
"Standard_D4s_v3": 140.0,
|
| 17 |
-
"Standard_D8s_v3": 280.0,
|
| 18 |
-
"Standard_D16s_v3": 560.0,
|
| 19 |
-
},
|
| 20 |
-
ResourceType.STORAGE_ACCOUNT: {
|
| 21 |
-
"50GB": 5.0,
|
| 22 |
-
"100GB": 10.0,
|
| 23 |
-
"1TB": 100.0,
|
| 24 |
-
"10TB": 900.0,
|
| 25 |
-
},
|
| 26 |
-
ResourceType.DATABASE: {
|
| 27 |
-
"Basic": 15.0,
|
| 28 |
-
"Standard": 50.0,
|
| 29 |
-
"Premium": 200.0,
|
| 30 |
-
},
|
| 31 |
-
ResourceType.KUBERNETES_CLUSTER: {
|
| 32 |
-
"Small": 100.0,
|
| 33 |
-
"Medium": 300.0,
|
| 34 |
-
"Large": 600.0,
|
| 35 |
-
},
|
| 36 |
-
ResourceType.FUNCTION_APP: {
|
| 37 |
-
"Consumption": 0.0,
|
| 38 |
-
"Premium": 75.0,
|
| 39 |
-
},
|
| 40 |
-
ResourceType.VIRTUAL_NETWORK: {
|
| 41 |
-
"default": 0.0,
|
| 42 |
-
},
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
def __init__(self, pricing_file: Optional[str] = None):
|
| 46 |
-
if pricing_file and os.path.exists(pricing_file):
|
| 47 |
-
with open(pricing_file, 'r') as f:
|
| 48 |
-
raw = yaml.safe_load(f)
|
| 49 |
-
self._pricing = {}
|
| 50 |
-
for res_str, sizes in raw.items():
|
| 51 |
-
try:
|
| 52 |
-
res_type = ResourceType(res_str)
|
| 53 |
-
except ValueError:
|
| 54 |
-
continue
|
| 55 |
-
self._pricing[res_type] = sizes
|
| 56 |
-
else:
|
| 57 |
-
self._pricing = self.DEFAULT_PRICING.copy()
|
| 58 |
-
|
| 59 |
-
# Cache for estimate_monthly_cost using a hashable key
|
| 60 |
-
self._cost_cache = {}
|
| 61 |
-
|
| 62 |
-
def estimate_monthly_cost(self, intent: ProvisionResourceIntent) -> Optional[float]:
|
| 63 |
-
# Create a hashable key from the intent's immutable fields
|
| 64 |
-
# (resource_type, size, region, environment) – configuration is omitted as it doesn't affect cost in this demo.
|
| 65 |
-
key = (intent.resource_type, intent.size, intent.region, intent.environment)
|
| 66 |
-
if key in self._cost_cache:
|
| 67 |
-
return self._cost_cache[key]
|
| 68 |
-
|
| 69 |
-
resource_pricing = self._pricing.get(intent.resource_type)
|
| 70 |
-
if not resource_pricing:
|
| 71 |
-
self._cost_cache[key] = None
|
| 72 |
-
return None
|
| 73 |
-
cost = resource_pricing.get(intent.size)
|
| 74 |
-
self._cost_cache[key] = cost
|
| 75 |
-
return cost
|
| 76 |
-
|
| 77 |
-
def cost_delta_vs_baseline(
|
| 78 |
-
self,
|
| 79 |
-
intent: ProvisionResourceIntent,
|
| 80 |
-
baseline_intent: Optional[ProvisionResourceIntent] = None,
|
| 81 |
-
) -> Optional[float]:
|
| 82 |
-
proposed = self.estimate_monthly_cost(intent)
|
| 83 |
-
if proposed is None:
|
| 84 |
-
return None
|
| 85 |
-
|
| 86 |
-
if baseline_intent:
|
| 87 |
-
baseline = self.estimate_monthly_cost(baseline_intent)
|
| 88 |
-
if baseline is None:
|
| 89 |
-
return None
|
| 90 |
-
return proposed - baseline
|
| 91 |
-
else:
|
| 92 |
-
resource_pricing = self._pricing.get(intent.resource_type)
|
| 93 |
-
if not resource_pricing:
|
| 94 |
-
return None
|
| 95 |
-
min_cost = min(resource_pricing.values())
|
| 96 |
-
return proposed - min_cost
|
| 97 |
-
|
| 98 |
-
def estimate_cost_distribution(self, intent: ProvisionResourceIntent) -> Dict[str, float]:
|
| 99 |
-
cost = self.estimate_monthly_cost(intent)
|
| 100 |
-
if cost is None:
|
| 101 |
-
return {}
|
| 102 |
-
return {str(cost): 1.0}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/healing_intent.py
DELETED
|
@@ -1,848 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Healing Intent - OSS creates, Enterprise executes
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
from dataclasses import dataclass, field, asdict
|
| 6 |
-
from typing import Dict, Any, Optional, List, ClassVar, Tuple, Union
|
| 7 |
-
from datetime import datetime
|
| 8 |
-
import hashlib
|
| 9 |
-
import json
|
| 10 |
-
import time
|
| 11 |
-
import uuid
|
| 12 |
-
from enum import Enum
|
| 13 |
-
import numpy as np
|
| 14 |
-
|
| 15 |
-
from .intents import InfrastructureIntent
|
| 16 |
-
from .constants import (
|
| 17 |
-
OSS_EDITION,
|
| 18 |
-
OSS_LICENSE,
|
| 19 |
-
ENTERPRISE_UPGRADE_URL,
|
| 20 |
-
EXECUTION_ALLOWED,
|
| 21 |
-
MAX_SIMILARITY_CACHE,
|
| 22 |
-
SIMILARITY_THRESHOLD,
|
| 23 |
-
MAX_POLICY_VIOLATIONS,
|
| 24 |
-
MAX_RISK_FACTORS,
|
| 25 |
-
MAX_COST_PROJECTIONS,
|
| 26 |
-
MAX_DECISION_TREE_DEPTH,
|
| 27 |
-
MAX_ALTERNATIVE_ACTIONS,
|
| 28 |
-
OSSBoundaryError,
|
| 29 |
-
)
|
| 30 |
-
|
| 31 |
-
class HealingIntentError(Exception): pass
|
| 32 |
-
class SerializationError(HealingIntentError): pass
|
| 33 |
-
class ValidationError(HealingIntentError): pass
|
| 34 |
-
|
| 35 |
-
class IntentSource(str, Enum):
|
| 36 |
-
OSS_ANALYSIS = "oss_analysis"
|
| 37 |
-
HUMAN_OVERRIDE = "human_override"
|
| 38 |
-
AUTOMATED_LEARNING = "automated_learning"
|
| 39 |
-
SCHEDULED_ACTION = "scheduled_action"
|
| 40 |
-
RAG_SIMILARITY = "rag_similarity"
|
| 41 |
-
INFRASTRUCTURE_ANALYSIS = "infrastructure_analysis"
|
| 42 |
-
POLICY_VIOLATION = "policy_violation"
|
| 43 |
-
COST_OPTIMIZATION = "cost_optimization"
|
| 44 |
-
|
| 45 |
-
class IntentStatus(str, Enum):
|
| 46 |
-
CREATED = "created"
|
| 47 |
-
PENDING_EXECUTION = "pending_execution"
|
| 48 |
-
EXECUTING = "executing"
|
| 49 |
-
EXECUTING_PARTIAL = "executing_partial"
|
| 50 |
-
COMPLETED = "completed"
|
| 51 |
-
COMPLETED_PARTIAL = "completed_partial"
|
| 52 |
-
FAILED = "failed"
|
| 53 |
-
REJECTED = "rejected"
|
| 54 |
-
CANCELLED = "cancelled"
|
| 55 |
-
ROLLED_BACK = "rolled_back"
|
| 56 |
-
OSS_ADVISORY_ONLY = "oss_advisory_only"
|
| 57 |
-
PENDING_APPROVAL = "pending_approval"
|
| 58 |
-
APPROVED = "approved"
|
| 59 |
-
APPROVED_WITH_OVERRIDES = "approved_with_overrides"
|
| 60 |
-
|
| 61 |
-
class RecommendedAction(str, Enum):
|
| 62 |
-
APPROVE = "approve"
|
| 63 |
-
DENY = "deny"
|
| 64 |
-
ESCALATE = "escalate"
|
| 65 |
-
DEFER = "defer"
|
| 66 |
-
|
| 67 |
-
class ConfidenceDistribution:
|
| 68 |
-
def __init__(self, mean: float, std: float = 0.05, samples: Optional[List[float]] = None):
|
| 69 |
-
self.mean = max(0.0, min(mean, 1.0))
|
| 70 |
-
self.std = max(0.0, min(std, 0.5))
|
| 71 |
-
self._samples = samples or list(np.random.normal(self.mean, self.std, 1000).clip(0, 1))
|
| 72 |
-
|
| 73 |
-
@property
|
| 74 |
-
def p5(self) -> float:
|
| 75 |
-
return float(np.percentile(self._samples, 5))
|
| 76 |
-
|
| 77 |
-
@property
|
| 78 |
-
def p50(self) -> float:
|
| 79 |
-
return float(np.percentile(self._samples, 50))
|
| 80 |
-
|
| 81 |
-
@property
|
| 82 |
-
def p95(self) -> float:
|
| 83 |
-
return float(np.percentile(self._samples, 95))
|
| 84 |
-
|
| 85 |
-
@property
|
| 86 |
-
def confidence_interval(self) -> Tuple[float, float]:
|
| 87 |
-
return (self.p5, self.p95)
|
| 88 |
-
|
| 89 |
-
def to_dict(self) -> Dict[str, float]:
|
| 90 |
-
return {
|
| 91 |
-
"mean": self.mean,
|
| 92 |
-
"std": self.std,
|
| 93 |
-
"p5": self.p5,
|
| 94 |
-
"p50": self.p50,
|
| 95 |
-
"p95": self.p95
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
@classmethod
|
| 99 |
-
def from_dict(cls, data: Dict[str, float]) -> "ConfidenceDistribution":
|
| 100 |
-
return cls(mean=data["mean"], std=data.get("std", 0.05), samples=None)
|
| 101 |
-
|
| 102 |
-
def __repr__(self) -> str:
|
| 103 |
-
return f"ConfidenceDistribution(mean={self.mean:.3f}, 95% CI=[{self.p5:.3f}, {self.p95:.3f}])"
|
| 104 |
-
|
| 105 |
-
@dataclass(frozen=True, slots=True)
|
| 106 |
-
class HealingIntent:
|
| 107 |
-
action: str
|
| 108 |
-
component: str
|
| 109 |
-
parameters: Dict[str, Any] = field(default_factory=dict)
|
| 110 |
-
justification: str = ""
|
| 111 |
-
confidence: float = 0.85
|
| 112 |
-
confidence_distribution: Optional[Dict[str, float]] = None
|
| 113 |
-
incident_id: str = ""
|
| 114 |
-
detected_at: float = field(default_factory=time.time)
|
| 115 |
-
risk_score: Optional[float] = None
|
| 116 |
-
risk_factors: Optional[Dict[str, float]] = None
|
| 117 |
-
cost_projection: Optional[float] = None
|
| 118 |
-
cost_confidence_interval: Optional[Tuple[float, float]] = None
|
| 119 |
-
recommended_action: Optional[RecommendedAction] = None
|
| 120 |
-
decision_tree: Optional[List[Dict[str, Any]]] = None
|
| 121 |
-
alternative_actions: Optional[List[Dict[str, Any]]] = None
|
| 122 |
-
risk_profile: Optional[str] = None
|
| 123 |
-
reasoning_chain: Optional[List[Dict[str, Any]]] = None
|
| 124 |
-
similar_incidents: Optional[List[Dict[str, Any]]] = None
|
| 125 |
-
rag_similarity_score: Optional[float] = None
|
| 126 |
-
source: IntentSource = IntentSource.OSS_ANALYSIS
|
| 127 |
-
intent_id: str = field(default_factory=lambda: f"intent_{uuid.uuid4().hex[:16]}")
|
| 128 |
-
created_at: float = field(default_factory=time.time)
|
| 129 |
-
status: IntentStatus = IntentStatus.CREATED
|
| 130 |
-
execution_id: Optional[str] = None
|
| 131 |
-
executed_at: Optional[float] = None
|
| 132 |
-
execution_result: Optional[Dict[str, Any]] = None
|
| 133 |
-
enterprise_metadata: Dict[str, Any] = field(default_factory=dict)
|
| 134 |
-
human_overrides: List[Dict[str, Any]] = field(default_factory=list)
|
| 135 |
-
approvals: List[Dict[str, Any]] = field(default_factory=list)
|
| 136 |
-
comments: List[Dict[str, Any]] = field(default_factory=list)
|
| 137 |
-
oss_edition: str = OSS_EDITION
|
| 138 |
-
oss_license: str = OSS_LICENSE
|
| 139 |
-
requires_enterprise: bool = True
|
| 140 |
-
execution_allowed: bool = EXECUTION_ALLOWED
|
| 141 |
-
infrastructure_intent_id: Optional[str] = None
|
| 142 |
-
policy_violations: List[str] = field(default_factory=list)
|
| 143 |
-
infrastructure_intent: Optional[Dict[str, Any]] = None
|
| 144 |
-
|
| 145 |
-
MIN_CONFIDENCE: ClassVar[float] = 0.0
|
| 146 |
-
MAX_CONFIDENCE: ClassVar[float] = 1.0
|
| 147 |
-
MAX_JUSTIFICATION_LENGTH: ClassVar[int] = 5000
|
| 148 |
-
MAX_PARAMETERS_SIZE: ClassVar[int] = 100
|
| 149 |
-
MAX_SIMILAR_INCIDENTS: ClassVar[int] = MAX_SIMILARITY_CACHE
|
| 150 |
-
VERSION: ClassVar[str] = "2.0.0"
|
| 151 |
-
|
| 152 |
-
def __post_init__(self) -> None:
|
| 153 |
-
self._validate_oss_boundaries()
|
| 154 |
-
self._validate_risk_integration()
|
| 155 |
-
|
| 156 |
-
def _validate_oss_boundaries(self) -> None:
|
| 157 |
-
errors: List[str] = []
|
| 158 |
-
if not (self.MIN_CONFIDENCE <= self.confidence <= self.MAX_CONFIDENCE):
|
| 159 |
-
errors.append(f"Confidence must be between {self.MIN_CONFIDENCE} and {self.MAX_CONFIDENCE}, got {self.confidence}")
|
| 160 |
-
if len(self.justification) > self.MAX_JUSTIFICATION_LENGTH:
|
| 161 |
-
errors.append(f"Justification exceeds max length {self.MAX_JUSTIFICATION_LENGTH}")
|
| 162 |
-
if not self.action or not self.action.strip():
|
| 163 |
-
errors.append("Action cannot be empty")
|
| 164 |
-
if not self.component or not self.component.strip():
|
| 165 |
-
errors.append("Component cannot be empty")
|
| 166 |
-
if len(self.parameters) > self.MAX_PARAMETERS_SIZE:
|
| 167 |
-
errors.append(f"Too many parameters: {len(self.parameters)} > {self.MAX_PARAMETERS_SIZE}")
|
| 168 |
-
try:
|
| 169 |
-
json.dumps(self.parameters)
|
| 170 |
-
except (TypeError, ValueError) as e:
|
| 171 |
-
errors.append(f"Parameters must be JSON serializable: {e}")
|
| 172 |
-
if self.similar_incidents and len(self.similar_incidents) > self.MAX_SIMILAR_INCIDENTS:
|
| 173 |
-
errors.append(f"Too many similar incidents: {len(self.similar_incidents)} > {self.MAX_SIMILAR_INCIDENTS}")
|
| 174 |
-
if self.oss_edition == OSS_EDITION:
|
| 175 |
-
if self.execution_allowed:
|
| 176 |
-
errors.append("Execution not allowed in OSS edition")
|
| 177 |
-
if self.status == IntentStatus.EXECUTING:
|
| 178 |
-
errors.append("EXECUTING status not allowed in OSS edition")
|
| 179 |
-
if self.executed_at is not None:
|
| 180 |
-
errors.append("executed_at should not be set in OSS edition")
|
| 181 |
-
if self.execution_id is not None:
|
| 182 |
-
errors.append("execution_id should not be set in OSS edition")
|
| 183 |
-
if errors:
|
| 184 |
-
raise ValidationError("HealingIntent validation failed:\n" + "\n".join(f" • {error}" for error in errors))
|
| 185 |
-
|
| 186 |
-
def _validate_risk_integration(self) -> None:
|
| 187 |
-
if self.risk_score is not None and not (0.0 <= self.risk_score <= 1.0):
|
| 188 |
-
raise ValidationError(f"Risk score must be between 0 and 1, got {self.risk_score}")
|
| 189 |
-
if self.cost_projection is not None and self.cost_projection < 0:
|
| 190 |
-
raise ValidationError(f"Cost projection cannot be negative, got {self.cost_projection}")
|
| 191 |
-
if self.cost_confidence_interval is not None:
|
| 192 |
-
low, high = self.cost_confidence_interval
|
| 193 |
-
if low > high:
|
| 194 |
-
raise ValidationError(f"Invalid confidence interval: [{low}, {high}]")
|
| 195 |
-
|
| 196 |
-
@property
|
| 197 |
-
def deterministic_id(self) -> str:
|
| 198 |
-
data = {
|
| 199 |
-
"action": self.action,
|
| 200 |
-
"component": self.component,
|
| 201 |
-
"parameters": self._normalize_parameters(self.parameters),
|
| 202 |
-
"incident_id": self.incident_id,
|
| 203 |
-
"detected_at": int(self.detected_at),
|
| 204 |
-
"oss_edition": self.oss_edition,
|
| 205 |
-
}
|
| 206 |
-
json_str = json.dumps(data, sort_keys=True, default=str)
|
| 207 |
-
hash_digest = hashlib.sha256(json_str.encode()).hexdigest()
|
| 208 |
-
return f"intent_{hash_digest[:16]}"
|
| 209 |
-
|
| 210 |
-
@property
|
| 211 |
-
def age_seconds(self) -> float:
|
| 212 |
-
return time.time() - self.created_at
|
| 213 |
-
|
| 214 |
-
@property
|
| 215 |
-
def is_executable(self) -> bool:
|
| 216 |
-
if self.oss_edition == OSS_EDITION:
|
| 217 |
-
return False
|
| 218 |
-
return self.status in [IntentStatus.CREATED, IntentStatus.PENDING_EXECUTION, IntentStatus.APPROVED]
|
| 219 |
-
|
| 220 |
-
@property
|
| 221 |
-
def is_oss_advisory(self) -> bool:
|
| 222 |
-
return self.oss_edition == OSS_EDITION and not self.execution_allowed
|
| 223 |
-
|
| 224 |
-
@property
|
| 225 |
-
def requires_enterprise_upgrade(self) -> bool:
|
| 226 |
-
return self.requires_enterprise and self.oss_edition == OSS_EDITION
|
| 227 |
-
|
| 228 |
-
@property
|
| 229 |
-
def confidence_interval(self) -> Optional[Tuple[float, float]]:
|
| 230 |
-
if self.confidence_distribution:
|
| 231 |
-
return (self.confidence_distribution.get("p5", self.confidence), self.confidence_distribution.get("p95", self.confidence))
|
| 232 |
-
return None
|
| 233 |
-
|
| 234 |
-
def to_enterprise_request(self) -> Dict[str, Any]:
|
| 235 |
-
return {
|
| 236 |
-
"intent_id": self.deterministic_id,
|
| 237 |
-
"action": self.action,
|
| 238 |
-
"component": self.component,
|
| 239 |
-
"parameters": self.parameters,
|
| 240 |
-
"justification": self.justification,
|
| 241 |
-
"confidence": self.confidence,
|
| 242 |
-
"confidence_interval": self.confidence_interval,
|
| 243 |
-
"risk_score": self.risk_score,
|
| 244 |
-
"cost_projection": self.cost_projection,
|
| 245 |
-
"incident_id": self.incident_id,
|
| 246 |
-
"detected_at": self.detected_at,
|
| 247 |
-
"created_at": self.created_at,
|
| 248 |
-
"source": self.source.value,
|
| 249 |
-
"recommended_action": self.recommended_action.value if self.recommended_action else None,
|
| 250 |
-
"oss_edition": self.oss_edition,
|
| 251 |
-
"oss_license": self.oss_license,
|
| 252 |
-
"requires_enterprise": self.requires_enterprise,
|
| 253 |
-
"execution_allowed": self.execution_allowed,
|
| 254 |
-
"version": self.VERSION,
|
| 255 |
-
"oss_metadata": {
|
| 256 |
-
"similar_incidents_count": len(self.similar_incidents) if self.similar_incidents else 0,
|
| 257 |
-
"rag_similarity_score": self.rag_similarity_score,
|
| 258 |
-
"has_reasoning_chain": self.reasoning_chain is not None,
|
| 259 |
-
"source": self.source.value,
|
| 260 |
-
"is_oss_advisory": self.is_oss_advisory,
|
| 261 |
-
"risk_factors": self.risk_factors,
|
| 262 |
-
"policy_violations_count": len(self.policy_violations) if self.policy_violations else 0,
|
| 263 |
-
"confidence_basis": self._get_confidence_basis(),
|
| 264 |
-
"learning_applied": False,
|
| 265 |
-
"learning_reason": "OSS advisory mode does not persist or learn from outcomes",
|
| 266 |
-
},
|
| 267 |
-
"upgrade_url": ENTERPRISE_UPGRADE_URL,
|
| 268 |
-
"enterprise_features": [
|
| 269 |
-
"autonomous_execution",
|
| 270 |
-
"approval_workflows",
|
| 271 |
-
"persistent_storage",
|
| 272 |
-
"learning_engine",
|
| 273 |
-
"audit_trails",
|
| 274 |
-
"compliance_reports",
|
| 275 |
-
"multi_tenant_support",
|
| 276 |
-
"sso_integration",
|
| 277 |
-
"24_7_support",
|
| 278 |
-
"probabilistic_confidence",
|
| 279 |
-
"risk_analytics",
|
| 280 |
-
"cost_optimization"
|
| 281 |
-
]
|
| 282 |
-
}
|
| 283 |
-
|
| 284 |
-
def _get_confidence_basis(self) -> str:
|
| 285 |
-
if self.recommended_action == RecommendedAction.DENY and self.policy_violations:
|
| 286 |
-
return "policy_violation"
|
| 287 |
-
if self.rag_similarity_score and self.rag_similarity_score > SIMILARITY_THRESHOLD:
|
| 288 |
-
return "historical_similarity"
|
| 289 |
-
if self.risk_score is not None:
|
| 290 |
-
return "risk_based"
|
| 291 |
-
return "policy_only"
|
| 292 |
-
|
| 293 |
-
def to_dict(self, include_oss_context: bool = False) -> Dict[str, Any]:
|
| 294 |
-
data = asdict(self)
|
| 295 |
-
if "source" in data and isinstance(data["source"], IntentSource):
|
| 296 |
-
data["source"] = self.source.value
|
| 297 |
-
if "status" in data and isinstance(data["status"], IntentStatus):
|
| 298 |
-
data["status"] = self.status.value
|
| 299 |
-
if "recommended_action" in data and isinstance(data["recommended_action"], RecommendedAction):
|
| 300 |
-
data["recommended_action"] = self.recommended_action.value if self.recommended_action else None
|
| 301 |
-
if not include_oss_context:
|
| 302 |
-
data.pop("reasoning_chain", None)
|
| 303 |
-
data.pop("similar_incidents", None)
|
| 304 |
-
data.pop("rag_similarity_score", None)
|
| 305 |
-
data.pop("decision_tree", None)
|
| 306 |
-
data.pop("alternative_actions", None)
|
| 307 |
-
data.pop("infrastructure_intent", None)
|
| 308 |
-
data["deterministic_id"] = self.deterministic_id
|
| 309 |
-
data["age_seconds"] = self.age_seconds
|
| 310 |
-
data["is_executable"] = self.is_executable
|
| 311 |
-
data["is_oss_advisory"] = self.is_oss_advisory
|
| 312 |
-
data["requires_enterprise_upgrade"] = self.requires_enterprise_upgrade
|
| 313 |
-
data["version"] = self.VERSION
|
| 314 |
-
data["confidence_interval"] = self.confidence_interval
|
| 315 |
-
return data
|
| 316 |
-
|
| 317 |
-
def with_execution_result(self, execution_id: str, executed_at: float, result: Dict[str, Any],
|
| 318 |
-
status: IntentStatus = IntentStatus.COMPLETED, metadata: Optional[Dict[str, Any]] = None) -> "HealingIntent":
|
| 319 |
-
return HealingIntent(
|
| 320 |
-
action=self.action, component=self.component, parameters=self.parameters, justification=self.justification,
|
| 321 |
-
confidence=self.confidence, confidence_distribution=self.confidence_distribution, incident_id=self.incident_id,
|
| 322 |
-
detected_at=self.detected_at, risk_score=self.risk_score, risk_factors=self.risk_factors,
|
| 323 |
-
cost_projection=self.cost_projection, cost_confidence_interval=self.cost_confidence_interval,
|
| 324 |
-
recommended_action=self.recommended_action, decision_tree=self.decision_tree,
|
| 325 |
-
alternative_actions=self.alternative_actions, risk_profile=self.risk_profile,
|
| 326 |
-
reasoning_chain=self.reasoning_chain, similar_incidents=self.similar_incidents,
|
| 327 |
-
rag_similarity_score=self.rag_similarity_score, source=self.source, intent_id=self.intent_id,
|
| 328 |
-
created_at=self.created_at, oss_edition=self.oss_edition, oss_license=self.oss_license,
|
| 329 |
-
requires_enterprise=self.requires_enterprise, execution_allowed=self.execution_allowed,
|
| 330 |
-
infrastructure_intent_id=self.infrastructure_intent_id, policy_violations=self.policy_violations,
|
| 331 |
-
infrastructure_intent=self.infrastructure_intent, status=status, execution_id=execution_id,
|
| 332 |
-
executed_at=executed_at, execution_result=result,
|
| 333 |
-
enterprise_metadata={**(self.enterprise_metadata or {}), **(metadata or {})},
|
| 334 |
-
human_overrides=self.human_overrides, approvals=self.approvals, comments=self.comments
|
| 335 |
-
)
|
| 336 |
-
|
| 337 |
-
def with_human_approval(self, approver: str, approval_time: float, comments: Optional[str] = None,
|
| 338 |
-
overrides: Optional[Dict[str, Any]] = None) -> "HealingIntent":
|
| 339 |
-
approval_record = {"approver": approver, "timestamp": approval_time, "comments": comments, "overrides": overrides}
|
| 340 |
-
new_overrides = list(self.human_overrides)
|
| 341 |
-
if overrides:
|
| 342 |
-
new_overrides.append({"overrider": approver, "timestamp": approval_time, "overrides": overrides, "reason": comments})
|
| 343 |
-
new_approvals = list(self.approvals) + [approval_record]
|
| 344 |
-
new_comments = list(self.comments)
|
| 345 |
-
if comments:
|
| 346 |
-
new_comments.append({"author": approver, "timestamp": approval_time, "comment": comments})
|
| 347 |
-
return HealingIntent(
|
| 348 |
-
action=self.action, component=self.component, parameters=self.parameters, justification=self.justification,
|
| 349 |
-
confidence=self.confidence, confidence_distribution=self.confidence_distribution, incident_id=self.incident_id,
|
| 350 |
-
detected_at=self.detected_at, risk_score=self.risk_score, risk_factors=self.risk_factors,
|
| 351 |
-
cost_projection=self.cost_projection, cost_confidence_interval=self.cost_confidence_interval,
|
| 352 |
-
recommended_action=self.recommended_action, decision_tree=self.decision_tree,
|
| 353 |
-
alternative_actions=self.alternative_actions, risk_profile=self.risk_profile,
|
| 354 |
-
reasoning_chain=self.reasoning_chain, similar_incidents=self.similar_incidents,
|
| 355 |
-
rag_similarity_score=self.rag_similarity_score, source=self.source, intent_id=self.intent_id,
|
| 356 |
-
created_at=self.created_at, status=IntentStatus.APPROVED_WITH_OVERRIDES if overrides else IntentStatus.APPROVED,
|
| 357 |
-
execution_id=self.execution_id, executed_at=self.executed_at, execution_result=self.execution_result,
|
| 358 |
-
enterprise_metadata=self.enterprise_metadata, human_overrides=new_overrides, approvals=new_approvals,
|
| 359 |
-
comments=new_comments, oss_edition=self.oss_edition, oss_license=self.oss_license,
|
| 360 |
-
requires_enterprise=self.requires_enterprise, execution_allowed=self.execution_allowed,
|
| 361 |
-
infrastructure_intent_id=self.infrastructure_intent_id, policy_violations=self.policy_violations,
|
| 362 |
-
infrastructure_intent=self.infrastructure_intent
|
| 363 |
-
)
|
| 364 |
-
|
| 365 |
-
def mark_as_sent_to_enterprise(self) -> "HealingIntent":
|
| 366 |
-
return HealingIntent(
|
| 367 |
-
action=self.action, component=self.component, parameters=self.parameters, justification=self.justification,
|
| 368 |
-
confidence=self.confidence, confidence_distribution=self.confidence_distribution, incident_id=self.incident_id,
|
| 369 |
-
detected_at=self.detected_at, risk_score=self.risk_score, risk_factors=self.risk_factors,
|
| 370 |
-
cost_projection=self.cost_projection, cost_confidence_interval=self.cost_confidence_interval,
|
| 371 |
-
recommended_action=self.recommended_action, decision_tree=self.decision_tree,
|
| 372 |
-
alternative_actions=self.alternative_actions, risk_profile=self.risk_profile,
|
| 373 |
-
reasoning_chain=self.reasoning_chain, similar_incidents=self.similar_incidents,
|
| 374 |
-
rag_similarity_score=self.rag_similarity_score, source=self.source, intent_id=self.intent_id,
|
| 375 |
-
created_at=self.created_at, status=IntentStatus.PENDING_EXECUTION, execution_id=self.execution_id,
|
| 376 |
-
executed_at=self.executed_at, execution_result=self.execution_result, enterprise_metadata=self.enterprise_metadata,
|
| 377 |
-
human_overrides=self.human_overrides, approvals=self.approvals, comments=self.comments,
|
| 378 |
-
oss_edition=self.oss_edition, oss_license=self.oss_license, requires_enterprise=self.requires_enterprise,
|
| 379 |
-
execution_allowed=self.execution_allowed, infrastructure_intent_id=self.infrastructure_intent_id,
|
| 380 |
-
policy_violations=self.policy_violations, infrastructure_intent=self.infrastructure_intent
|
| 381 |
-
)
|
| 382 |
-
|
| 383 |
-
def mark_as_oss_advisory(self) -> "HealingIntent":
|
| 384 |
-
return HealingIntent(
|
| 385 |
-
action=self.action, component=self.component, parameters=self.parameters, justification=self.justification,
|
| 386 |
-
confidence=self.confidence, confidence_distribution=self.confidence_distribution, incident_id=self.incident_id,
|
| 387 |
-
detected_at=self.detected_at, risk_score=self.risk_score, risk_factors=self.risk_factors,
|
| 388 |
-
cost_projection=self.cost_projection, cost_confidence_interval=self.cost_confidence_interval,
|
| 389 |
-
recommended_action=self.recommended_action, decision_tree=self.decision_tree,
|
| 390 |
-
alternative_actions=self.alternative_actions, risk_profile=self.risk_profile,
|
| 391 |
-
reasoning_chain=self.reasoning_chain, similar_incidents=self.similar_incidents,
|
| 392 |
-
rag_similarity_score=self.rag_similarity_score, source=self.source, intent_id=self.intent_id,
|
| 393 |
-
created_at=self.created_at, status=IntentStatus.OSS_ADVISORY_ONLY, execution_id=self.execution_id,
|
| 394 |
-
executed_at=self.executed_at, execution_result=self.execution_result, enterprise_metadata=self.enterprise_metadata,
|
| 395 |
-
human_overrides=self.human_overrides, approvals=self.approvals, comments=self.comments,
|
| 396 |
-
oss_edition=self.oss_edition, oss_license=self.oss_license, requires_enterprise=self.requires_enterprise,
|
| 397 |
-
execution_allowed=False, infrastructure_intent_id=self.infrastructure_intent_id,
|
| 398 |
-
policy_violations=self.policy_violations, infrastructure_intent=self.infrastructure_intent
|
| 399 |
-
)
|
| 400 |
-
|
| 401 |
-
@classmethod
|
| 402 |
-
def from_infrastructure_intent(cls, infrastructure_intent: Any, action: str, component: str,
|
| 403 |
-
parameters: Dict[str, Any], justification: str, confidence: float = 0.85,
|
| 404 |
-
risk_score: Optional[float] = None, risk_factors: Optional[Dict[str, float]] = None,
|
| 405 |
-
cost_projection: Optional[float] = None, policy_violations: Optional[List[str]] = None,
|
| 406 |
-
recommended_action: Optional[RecommendedAction] = None,
|
| 407 |
-
source: IntentSource = IntentSource.INFRASTRUCTURE_ANALYSIS) -> "HealingIntent":
|
| 408 |
-
infrastructure_intent_id = getattr(infrastructure_intent, 'intent_id', None)
|
| 409 |
-
if hasattr(infrastructure_intent, 'model_dump'):
|
| 410 |
-
intent_dict = infrastructure_intent.model_dump()
|
| 411 |
-
elif hasattr(infrastructure_intent, 'to_dict'):
|
| 412 |
-
intent_dict = infrastructure_intent.to_dict()
|
| 413 |
-
else:
|
| 414 |
-
intent_dict = {"type": str(type(infrastructure_intent))}
|
| 415 |
-
return cls(
|
| 416 |
-
action=action, component=component, parameters=parameters, justification=justification, confidence=confidence,
|
| 417 |
-
risk_score=risk_score, risk_factors=risk_factors, cost_projection=cost_projection,
|
| 418 |
-
policy_violations=policy_violations or [], recommended_action=recommended_action, source=source,
|
| 419 |
-
infrastructure_intent_id=infrastructure_intent_id, infrastructure_intent=intent_dict,
|
| 420 |
-
oss_edition=OSS_EDITION, requires_enterprise=True, execution_allowed=False
|
| 421 |
-
)
|
| 422 |
-
|
| 423 |
-
@classmethod
|
| 424 |
-
def from_analysis(cls, action: str, component: str, parameters: Dict[str, Any], justification: str,
|
| 425 |
-
confidence: float, confidence_std: float = 0.05,
|
| 426 |
-
similar_incidents: Optional[List[Dict[str, Any]]] = None,
|
| 427 |
-
reasoning_chain: Optional[List[Dict[str, Any]]] = None, incident_id: str = "",
|
| 428 |
-
source: IntentSource = IntentSource.OSS_ANALYSIS, rag_similarity_score: Optional[float] = None,
|
| 429 |
-
risk_score: Optional[float] = None, cost_projection: Optional[float] = None) -> "HealingIntent":
|
| 430 |
-
if similar_incidents and len(similar_incidents) > cls.MAX_SIMILAR_INCIDENTS:
|
| 431 |
-
similar_incidents = similar_incidents[:cls.MAX_SIMILAR_INCIDENTS]
|
| 432 |
-
conf_dist = ConfidenceDistribution(confidence, confidence_std)
|
| 433 |
-
enhanced_confidence = confidence
|
| 434 |
-
if similar_incidents:
|
| 435 |
-
similarity_scores = [inc.get("similarity", 0.0) for inc in similar_incidents if "similarity" in inc]
|
| 436 |
-
if similarity_scores:
|
| 437 |
-
avg_similarity = sum(similarity_scores) / len(similarity_scores)
|
| 438 |
-
confidence_boost = min(0.2, avg_similarity * 0.3)
|
| 439 |
-
enhanced_confidence = min(confidence * (1.0 + confidence_boost), cls.MAX_CONFIDENCE)
|
| 440 |
-
final_rag_score = rag_similarity_score
|
| 441 |
-
if final_rag_score is None and similar_incidents and len(similar_incidents) > 0:
|
| 442 |
-
top_similarities = [inc.get("similarity", 0.0) for inc in similar_incidents[:3] if "similarity" in inc]
|
| 443 |
-
if top_similarities:
|
| 444 |
-
final_rag_score = sum(top_similarities) / len(top_similarities)
|
| 445 |
-
return cls(
|
| 446 |
-
action=action, component=component, parameters=parameters, justification=justification,
|
| 447 |
-
confidence=enhanced_confidence, confidence_distribution=conf_dist.to_dict(), incident_id=incident_id,
|
| 448 |
-
similar_incidents=similar_incidents, reasoning_chain=reasoning_chain, rag_similarity_score=final_rag_score,
|
| 449 |
-
source=source, risk_score=risk_score, cost_projection=cost_projection,
|
| 450 |
-
oss_edition=OSS_EDITION, requires_enterprise=True, execution_allowed=False
|
| 451 |
-
)
|
| 452 |
-
|
| 453 |
-
@classmethod
|
| 454 |
-
def from_rag_recommendation(cls, action: str, component: str, parameters: Dict[str, Any],
|
| 455 |
-
rag_similarity_score: float, similar_incidents: List[Dict[str, Any]],
|
| 456 |
-
justification_template: str = "Based on {count} similar historical incidents with {success_rate:.0%} success rate",
|
| 457 |
-
success_rate: Optional[float] = None, risk_score: Optional[float] = None,
|
| 458 |
-
cost_projection: Optional[float] = None) -> "HealingIntent":
|
| 459 |
-
if not similar_incidents:
|
| 460 |
-
raise ValidationError("RAG recommendation requires similar incidents")
|
| 461 |
-
if success_rate is None:
|
| 462 |
-
if len(similar_incidents) == 0:
|
| 463 |
-
success_rate = 0.0
|
| 464 |
-
else:
|
| 465 |
-
successful = sum(1 for inc in similar_incidents if inc.get("success", False))
|
| 466 |
-
success_rate = successful / len(similar_incidents)
|
| 467 |
-
justification = justification_template.format(count=len(similar_incidents), success_rate=success_rate or 0.0,
|
| 468 |
-
action=action, component=component)
|
| 469 |
-
base_confidence = rag_similarity_score * 0.8
|
| 470 |
-
if success_rate:
|
| 471 |
-
base_confidence = base_confidence * (0.7 + success_rate * 0.3)
|
| 472 |
-
return cls.from_analysis(action=action, component=component, parameters=parameters, justification=justification,
|
| 473 |
-
confidence=min(base_confidence, 0.95), similar_incidents=similar_incidents,
|
| 474 |
-
incident_id=similar_incidents[0].get("incident_id", "") if similar_incidents else "",
|
| 475 |
-
source=IntentSource.RAG_SIMILARITY, rag_similarity_score=rag_similarity_score,
|
| 476 |
-
risk_score=risk_score, cost_projection=cost_projection)
|
| 477 |
-
|
| 478 |
-
@classmethod
|
| 479 |
-
def from_dict(cls, data: Dict[str, Any]) -> "HealingIntent":
|
| 480 |
-
version = data.get("version", "1.0.0")
|
| 481 |
-
clean_data = data.copy()
|
| 482 |
-
if "source" in clean_data and isinstance(clean_data["source"], str):
|
| 483 |
-
clean_data["source"] = IntentSource(clean_data["source"])
|
| 484 |
-
if "status" in clean_data and isinstance(clean_data["status"], str):
|
| 485 |
-
clean_data["status"] = IntentStatus(clean_data["status"])
|
| 486 |
-
if "recommended_action" in clean_data and isinstance(clean_data["recommended_action"], str):
|
| 487 |
-
try:
|
| 488 |
-
clean_data["recommended_action"] = RecommendedAction(clean_data["recommended_action"])
|
| 489 |
-
except ValueError:
|
| 490 |
-
clean_data["recommended_action"] = None
|
| 491 |
-
clean_data.pop("deterministic_id", None)
|
| 492 |
-
clean_data.pop("age_seconds", None)
|
| 493 |
-
clean_data.pop("is_executable", None)
|
| 494 |
-
clean_data.pop("is_oss_advisory", None)
|
| 495 |
-
clean_data.pop("requires_enterprise_upgrade", None)
|
| 496 |
-
clean_data.pop("version", None)
|
| 497 |
-
clean_data.pop("confidence_interval", None)
|
| 498 |
-
return cls(**clean_data)
|
| 499 |
-
|
| 500 |
-
def _normalize_parameters(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
| 501 |
-
normalized: Dict[str, Any] = {}
|
| 502 |
-
for key, value in sorted(params.items()):
|
| 503 |
-
normalized[key] = self._normalize_value(value)
|
| 504 |
-
return normalized
|
| 505 |
-
|
| 506 |
-
def _normalize_value(self, value: Any) -> Any:
|
| 507 |
-
if isinstance(value, (int, float, str, bool, type(None))):
|
| 508 |
-
return value
|
| 509 |
-
elif isinstance(value, (list, tuple, set)):
|
| 510 |
-
return tuple(sorted(self._normalize_value(v) for v in value))
|
| 511 |
-
elif isinstance(value, dict):
|
| 512 |
-
return self._normalize_parameters(value)
|
| 513 |
-
elif hasattr(value, '__dict__'):
|
| 514 |
-
return self._normalize_parameters(value.__dict__)
|
| 515 |
-
else:
|
| 516 |
-
try:
|
| 517 |
-
return str(value)
|
| 518 |
-
except Exception:
|
| 519 |
-
return f"<unserializable:{type(value).__name__}>"
|
| 520 |
-
|
| 521 |
-
def get_oss_context(self) -> Dict[str, Any]:
|
| 522 |
-
return {
|
| 523 |
-
"reasoning_chain": self.reasoning_chain,
|
| 524 |
-
"similar_incidents": self.similar_incidents,
|
| 525 |
-
"rag_similarity_score": self.rag_similarity_score,
|
| 526 |
-
"decision_tree": self.decision_tree,
|
| 527 |
-
"alternative_actions": self.alternative_actions,
|
| 528 |
-
"analysis_timestamp": datetime.fromtimestamp(self.detected_at).isoformat(),
|
| 529 |
-
"source": self.source.value,
|
| 530 |
-
"created_at": datetime.fromtimestamp(self.created_at).isoformat(),
|
| 531 |
-
"oss_edition": self.oss_edition,
|
| 532 |
-
"is_oss_advisory": self.is_oss_advisory,
|
| 533 |
-
"infrastructure_intent": self.infrastructure_intent,
|
| 534 |
-
}
|
| 535 |
-
|
| 536 |
-
def get_execution_summary(self) -> Dict[str, Any]:
|
| 537 |
-
summary = {
|
| 538 |
-
"intent_id": self.deterministic_id,
|
| 539 |
-
"action": self.action,
|
| 540 |
-
"component": self.component,
|
| 541 |
-
"confidence": self.confidence,
|
| 542 |
-
"confidence_interval": self.confidence_interval,
|
| 543 |
-
"risk_score": self.risk_score,
|
| 544 |
-
"cost_projection": self.cost_projection,
|
| 545 |
-
"status": self.status.value,
|
| 546 |
-
"created_at": datetime.fromtimestamp(self.created_at).isoformat(),
|
| 547 |
-
"age_seconds": self.age_seconds,
|
| 548 |
-
"oss_edition": self.oss_edition,
|
| 549 |
-
"requires_enterprise": self.requires_enterprise,
|
| 550 |
-
"is_oss_advisory": self.is_oss_advisory,
|
| 551 |
-
"source": self.source.value,
|
| 552 |
-
"policy_violations_count": len(self.policy_violations) if self.policy_violations else 0,
|
| 553 |
-
"confidence_basis": self._get_confidence_basis(),
|
| 554 |
-
}
|
| 555 |
-
if self.executed_at:
|
| 556 |
-
summary["executed_at"] = datetime.fromtimestamp(self.executed_at).isoformat()
|
| 557 |
-
summary["execution_duration_seconds"] = self.executed_at - self.created_at
|
| 558 |
-
if self.execution_result:
|
| 559 |
-
summary["execution_success"] = self.execution_result.get("success", False)
|
| 560 |
-
summary["execution_message"] = self.execution_result.get("message", "")
|
| 561 |
-
if self.rag_similarity_score:
|
| 562 |
-
summary["rag_similarity_score"] = self.rag_similarity_score
|
| 563 |
-
if self.similar_incidents:
|
| 564 |
-
summary["similar_incidents_count"] = len(self.similar_incidents)
|
| 565 |
-
if self.approvals:
|
| 566 |
-
summary["approvals_count"] = len(self.approvals)
|
| 567 |
-
summary["approved_by"] = [a.get("approver") for a in self.approvals if a.get("approver")]
|
| 568 |
-
if self.human_overrides:
|
| 569 |
-
summary["overrides_count"] = len(self.human_overrides)
|
| 570 |
-
return summary
|
| 571 |
-
|
| 572 |
-
def is_immutable(self) -> bool:
|
| 573 |
-
try:
|
| 574 |
-
object.__setattr__(self, '_test_immutable', True)
|
| 575 |
-
return False
|
| 576 |
-
except Exception:
|
| 577 |
-
return True
|
| 578 |
-
|
| 579 |
-
def __repr__(self) -> str:
|
| 580 |
-
return (f"HealingIntent(id={self.deterministic_id[:8]}..., action={self.action}, "
|
| 581 |
-
f"component={self.component}, confidence={self.confidence:.2f}, "
|
| 582 |
-
f"risk={self.risk_score:.2f if self.risk_score else 'N/A'}, status={self.status.value})")
|
| 583 |
-
|
| 584 |
-
class HealingIntentSerializer:
|
| 585 |
-
SCHEMA_VERSION: ClassVar[str] = "2.0.0"
|
| 586 |
-
|
| 587 |
-
@classmethod
|
| 588 |
-
def serialize(cls, intent: HealingIntent, version: str = "2.0.0") -> Dict[str, Any]:
|
| 589 |
-
try:
|
| 590 |
-
if version == "2.0.0":
|
| 591 |
-
return {
|
| 592 |
-
"version": version,
|
| 593 |
-
"schema_version": cls.SCHEMA_VERSION,
|
| 594 |
-
"data": intent.to_dict(include_oss_context=True),
|
| 595 |
-
"metadata": {
|
| 596 |
-
"serialized_at": time.time(),
|
| 597 |
-
"deterministic_id": intent.deterministic_id,
|
| 598 |
-
"is_executable": intent.is_executable,
|
| 599 |
-
"is_oss_advisory": intent.is_oss_advisory,
|
| 600 |
-
"requires_enterprise_upgrade": intent.requires_enterprise_upgrade,
|
| 601 |
-
"oss_edition": intent.oss_edition,
|
| 602 |
-
"has_probabilistic_confidence": intent.confidence_distribution is not None,
|
| 603 |
-
"has_risk_assessment": intent.risk_score is not None,
|
| 604 |
-
"has_cost_projection": intent.cost_projection is not None,
|
| 605 |
-
}
|
| 606 |
-
}
|
| 607 |
-
elif version in ["1.1.0", "1.0.0"]:
|
| 608 |
-
data = intent.to_dict(include_oss_context=True)
|
| 609 |
-
data.pop("confidence_distribution", None)
|
| 610 |
-
data.pop("risk_score", None)
|
| 611 |
-
data.pop("risk_factors", None)
|
| 612 |
-
data.pop("cost_projection", None)
|
| 613 |
-
data.pop("cost_confidence_interval", None)
|
| 614 |
-
data.pop("recommended_action", None)
|
| 615 |
-
data.pop("decision_tree", None)
|
| 616 |
-
data.pop("alternative_actions", None)
|
| 617 |
-
data.pop("risk_profile", None)
|
| 618 |
-
data.pop("human_overrides", None)
|
| 619 |
-
data.pop("approvals", None)
|
| 620 |
-
data.pop("comments", None)
|
| 621 |
-
data.pop("infrastructure_intent_id", None)
|
| 622 |
-
data.pop("policy_violations", None)
|
| 623 |
-
data.pop("infrastructure_intent", None)
|
| 624 |
-
if data.get("status") in [
|
| 625 |
-
IntentStatus.EXECUTING_PARTIAL.value,
|
| 626 |
-
IntentStatus.COMPLETED_PARTIAL.value,
|
| 627 |
-
IntentStatus.ROLLED_BACK.value,
|
| 628 |
-
IntentStatus.PENDING_APPROVAL.value,
|
| 629 |
-
IntentStatus.APPROVED.value,
|
| 630 |
-
IntentStatus.APPROVED_WITH_OVERRIDES.value
|
| 631 |
-
]:
|
| 632 |
-
data["status"] = IntentStatus.PENDING_EXECUTION.value
|
| 633 |
-
return {
|
| 634 |
-
"version": version,
|
| 635 |
-
"schema_version": "1.1.0" if version == "1.1.0" else "1.0.0",
|
| 636 |
-
"data": data,
|
| 637 |
-
"metadata": {
|
| 638 |
-
"serialized_at": time.time(),
|
| 639 |
-
"deterministic_id": intent.deterministic_id,
|
| 640 |
-
"is_executable": intent.is_executable,
|
| 641 |
-
"is_oss_advisory": intent.is_oss_advisory,
|
| 642 |
-
}
|
| 643 |
-
}
|
| 644 |
-
else:
|
| 645 |
-
raise SerializationError(f"Unsupported version: {version}")
|
| 646 |
-
except Exception as e:
|
| 647 |
-
raise SerializationError(f"Failed to serialize HealingIntent: {e}") from e
|
| 648 |
-
|
| 649 |
-
@classmethod
|
| 650 |
-
def deserialize(cls, data: Dict[str, Any]) -> HealingIntent:
|
| 651 |
-
try:
|
| 652 |
-
version = data.get("version", "1.0.0")
|
| 653 |
-
intent_data = data.get("data", data)
|
| 654 |
-
if version in ["2.0.0", "1.1.0", "1.0.0"]:
|
| 655 |
-
if version.startswith("1."):
|
| 656 |
-
intent_data.setdefault("confidence_distribution", None)
|
| 657 |
-
intent_data.setdefault("risk_score", None)
|
| 658 |
-
intent_data.setdefault("risk_factors", None)
|
| 659 |
-
intent_data.setdefault("cost_projection", None)
|
| 660 |
-
intent_data.setdefault("cost_confidence_interval", None)
|
| 661 |
-
intent_data.setdefault("recommended_action", None)
|
| 662 |
-
intent_data.setdefault("decision_tree", None)
|
| 663 |
-
intent_data.setdefault("alternative_actions", None)
|
| 664 |
-
intent_data.setdefault("risk_profile", None)
|
| 665 |
-
intent_data.setdefault("human_overrides", [])
|
| 666 |
-
intent_data.setdefault("approvals", [])
|
| 667 |
-
intent_data.setdefault("comments", [])
|
| 668 |
-
intent_data.setdefault("infrastructure_intent_id", None)
|
| 669 |
-
intent_data.setdefault("policy_violations", [])
|
| 670 |
-
intent_data.setdefault("infrastructure_intent", None)
|
| 671 |
-
return HealingIntent.from_dict(intent_data)
|
| 672 |
-
else:
|
| 673 |
-
raise SerializationError(f"Unsupported version: {version}")
|
| 674 |
-
except KeyError as e:
|
| 675 |
-
raise SerializationError(f"Missing required field in serialized data: {e}") from e
|
| 676 |
-
except Exception as e:
|
| 677 |
-
raise SerializationError(f"Failed to deserialize HealingIntent: {e}") from e
|
| 678 |
-
|
| 679 |
-
@classmethod
|
| 680 |
-
def to_json(cls, intent: HealingIntent, pretty: bool = False) -> str:
|
| 681 |
-
try:
|
| 682 |
-
serialized = cls.serialize(intent)
|
| 683 |
-
return json.dumps(serialized, indent=2 if pretty else None, default=str)
|
| 684 |
-
except Exception as e:
|
| 685 |
-
raise SerializationError(f"Failed to convert to JSON: {e}") from e
|
| 686 |
-
|
| 687 |
-
@classmethod
|
| 688 |
-
def from_json(cls, json_str: str) -> HealingIntent:
|
| 689 |
-
try:
|
| 690 |
-
data = json.loads(json_str)
|
| 691 |
-
return cls.deserialize(data)
|
| 692 |
-
except json.JSONDecodeError as e:
|
| 693 |
-
raise SerializationError(f"Invalid JSON: {e}") from e
|
| 694 |
-
except Exception as e:
|
| 695 |
-
raise SerializationError(f"Failed to parse JSON: {e}") from e
|
| 696 |
-
|
| 697 |
-
@classmethod
|
| 698 |
-
def to_enterprise_json(cls, intent: HealingIntent) -> str:
|
| 699 |
-
try:
|
| 700 |
-
enterprise_request = intent.to_enterprise_request()
|
| 701 |
-
return json.dumps(enterprise_request, default=str)
|
| 702 |
-
except Exception as e:
|
| 703 |
-
raise SerializationError(f"Failed to create Enterprise JSON: {e}") from e
|
| 704 |
-
|
| 705 |
-
@classmethod
|
| 706 |
-
def validate_for_oss(cls, intent: HealingIntent) -> bool:
|
| 707 |
-
try:
|
| 708 |
-
if intent.oss_edition != OSS_EDITION:
|
| 709 |
-
return False
|
| 710 |
-
if intent.execution_allowed:
|
| 711 |
-
return False
|
| 712 |
-
if intent.similar_incidents and len(intent.similar_incidents) > HealingIntent.MAX_SIMILAR_INCIDENTS:
|
| 713 |
-
return False
|
| 714 |
-
if not intent.is_immutable():
|
| 715 |
-
return False
|
| 716 |
-
if intent.executed_at is not None or intent.execution_id is not None:
|
| 717 |
-
return False
|
| 718 |
-
return True
|
| 719 |
-
except Exception:
|
| 720 |
-
return False
|
| 721 |
-
|
| 722 |
-
# Factory functions
|
| 723 |
-
def create_infrastructure_healing_intent(infrastructure_result: Any, action_mapping: Optional[Dict[str, str]] = None) -> HealingIntent:
|
| 724 |
-
if action_mapping is None:
|
| 725 |
-
action_mapping = {"approve": "execute", "deny": "block", "escalate": "escalate", "defer": "defer"}
|
| 726 |
-
recommended_action = getattr(infrastructure_result, 'recommended_action', None)
|
| 727 |
-
if recommended_action and hasattr(recommended_action, 'value'):
|
| 728 |
-
action = action_mapping.get(recommended_action.value, "review")
|
| 729 |
-
else:
|
| 730 |
-
action = "review"
|
| 731 |
-
parameters = {
|
| 732 |
-
"infrastructure_intent_id": getattr(infrastructure_result, 'intent_id', None),
|
| 733 |
-
"risk_score": getattr(infrastructure_result, 'risk_score', None),
|
| 734 |
-
"cost_projection": getattr(infrastructure_result, 'cost_projection', None),
|
| 735 |
-
"policy_violations": getattr(infrastructure_result, 'policy_violations', []),
|
| 736 |
-
"evaluation_details": getattr(infrastructure_result, 'evaluation_details', {})
|
| 737 |
-
}
|
| 738 |
-
justification_parts = [getattr(infrastructure_result, 'justification', "Infrastructure analysis completed")]
|
| 739 |
-
policy_violations = getattr(infrastructure_result, 'policy_violations', [])
|
| 740 |
-
if policy_violations:
|
| 741 |
-
justification_parts.append(f"Policy violations: {'; '.join(policy_violations)}")
|
| 742 |
-
return HealingIntent.from_infrastructure_intent(
|
| 743 |
-
infrastructure_intent=getattr(infrastructure_result, 'infrastructure_intent', None),
|
| 744 |
-
action=action,
|
| 745 |
-
component="infrastructure",
|
| 746 |
-
parameters=parameters,
|
| 747 |
-
justification=" ".join(justification_parts),
|
| 748 |
-
confidence=getattr(infrastructure_result, 'confidence_score', 0.85),
|
| 749 |
-
risk_score=getattr(infrastructure_result, 'risk_score', None),
|
| 750 |
-
policy_violations=policy_violations,
|
| 751 |
-
recommended_action=recommended_action,
|
| 752 |
-
source=IntentSource.INFRASTRUCTURE_ANALYSIS
|
| 753 |
-
).mark_as_oss_advisory()
|
| 754 |
-
|
| 755 |
-
def create_rollback_intent(component: str, revision: str = "previous", justification: str = "",
|
| 756 |
-
incident_id: str = "", similar_incidents: Optional[List[Dict[str, Any]]] = None,
|
| 757 |
-
rag_similarity_score: Optional[float] = None, risk_score: Optional[float] = None,
|
| 758 |
-
cost_projection: Optional[float] = None) -> HealingIntent:
|
| 759 |
-
if not justification:
|
| 760 |
-
justification = f"Rollback {component} to {revision} revision"
|
| 761 |
-
return HealingIntent.from_analysis(
|
| 762 |
-
action="rollback",
|
| 763 |
-
component=component,
|
| 764 |
-
parameters={"revision": revision},
|
| 765 |
-
justification=justification,
|
| 766 |
-
confidence=0.9,
|
| 767 |
-
similar_incidents=similar_incidents,
|
| 768 |
-
incident_id=incident_id,
|
| 769 |
-
rag_similarity_score=rag_similarity_score,
|
| 770 |
-
risk_score=risk_score,
|
| 771 |
-
cost_projection=cost_projection,
|
| 772 |
-
).mark_as_oss_advisory()
|
| 773 |
-
|
| 774 |
-
def create_restart_intent(component: str, container_id: Optional[str] = None, justification: str = "",
|
| 775 |
-
incident_id: str = "", similar_incidents: Optional[List[Dict[str, Any]]] = None,
|
| 776 |
-
rag_similarity_score: Optional[float] = None, risk_score: Optional[float] = None,
|
| 777 |
-
cost_projection: Optional[float] = None) -> HealingIntent:
|
| 778 |
-
parameters = {}
|
| 779 |
-
if container_id:
|
| 780 |
-
parameters["container_id"] = container_id
|
| 781 |
-
if not justification:
|
| 782 |
-
justification = f"Restart container for {component}"
|
| 783 |
-
return HealingIntent.from_analysis(
|
| 784 |
-
action="restart_container",
|
| 785 |
-
component=component,
|
| 786 |
-
parameters=parameters,
|
| 787 |
-
justification=justification,
|
| 788 |
-
confidence=0.85,
|
| 789 |
-
similar_incidents=similar_incidents,
|
| 790 |
-
incident_id=incident_id,
|
| 791 |
-
rag_similarity_score=rag_similarity_score,
|
| 792 |
-
risk_score=risk_score,
|
| 793 |
-
cost_projection=cost_projection,
|
| 794 |
-
).mark_as_oss_advisory()
|
| 795 |
-
|
| 796 |
-
def create_scale_out_intent(component: str, scale_factor: int = 2, justification: str = "",
|
| 797 |
-
incident_id: str = "", similar_incidents: Optional[List[Dict[str, Any]]] = None,
|
| 798 |
-
rag_similarity_score: Optional[float] = None, risk_score: Optional[float] = None,
|
| 799 |
-
cost_projection: Optional[float] = None) -> HealingIntent:
|
| 800 |
-
if not justification:
|
| 801 |
-
justification = f"Scale out {component} by factor {scale_factor}"
|
| 802 |
-
return HealingIntent.from_analysis(
|
| 803 |
-
action="scale_out",
|
| 804 |
-
component=component,
|
| 805 |
-
parameters={"scale_factor": scale_factor},
|
| 806 |
-
justification=justification,
|
| 807 |
-
confidence=0.8,
|
| 808 |
-
similar_incidents=similar_incidents,
|
| 809 |
-
incident_id=incident_id,
|
| 810 |
-
rag_similarity_score=rag_similarity_score,
|
| 811 |
-
risk_score=risk_score,
|
| 812 |
-
cost_projection=cost_projection,
|
| 813 |
-
).mark_as_oss_advisory()
|
| 814 |
-
|
| 815 |
-
def create_oss_advisory_intent(action: str, component: str, parameters: Dict[str, Any], justification: str,
|
| 816 |
-
confidence: float = 0.85, incident_id: str = "", risk_score: Optional[float] = None,
|
| 817 |
-
cost_projection: Optional[float] = None) -> HealingIntent:
|
| 818 |
-
return HealingIntent(
|
| 819 |
-
action=action,
|
| 820 |
-
component=component,
|
| 821 |
-
parameters=parameters,
|
| 822 |
-
justification=justification,
|
| 823 |
-
confidence=confidence,
|
| 824 |
-
incident_id=incident_id,
|
| 825 |
-
risk_score=risk_score,
|
| 826 |
-
cost_projection=cost_projection,
|
| 827 |
-
oss_edition=OSS_EDITION,
|
| 828 |
-
requires_enterprise=True,
|
| 829 |
-
execution_allowed=False,
|
| 830 |
-
status=IntentStatus.OSS_ADVISORY_ONLY,
|
| 831 |
-
)
|
| 832 |
-
|
| 833 |
-
__all__ = [
|
| 834 |
-
"HealingIntent",
|
| 835 |
-
"ConfidenceDistribution",
|
| 836 |
-
"HealingIntentSerializer",
|
| 837 |
-
"IntentSource",
|
| 838 |
-
"IntentStatus",
|
| 839 |
-
"RecommendedAction",
|
| 840 |
-
"HealingIntentError",
|
| 841 |
-
"SerializationError",
|
| 842 |
-
"ValidationError",
|
| 843 |
-
"create_infrastructure_healing_intent",
|
| 844 |
-
"create_rollback_intent",
|
| 845 |
-
"create_restart_intent",
|
| 846 |
-
"create_scale_out_intent",
|
| 847 |
-
"create_oss_advisory_intent",
|
| 848 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/intents.py
DELETED
|
@@ -1,168 +0,0 @@
|
|
| 1 |
-
# agentic_reliability_framework/infrastructure/intents.py
|
| 2 |
-
"""
|
| 3 |
-
Infrastructure Intent Schema – Algebraic Data Types for Change Requests.
|
| 4 |
-
|
| 5 |
-
This module defines a family of intents as a discriminated union. Each intent
|
| 6 |
-
represents a proposed infrastructure action. Intents are immutable, self-validating,
|
| 7 |
-
and carry provenance for auditability.
|
| 8 |
-
|
| 9 |
-
The design follows principles of domain-driven design and knowledge engineering,
|
| 10 |
-
using strong typing and semantic constraints to prevent invalid states.
|
| 11 |
-
"""
|
| 12 |
-
|
| 13 |
-
from __future__ import annotations
|
| 14 |
-
|
| 15 |
-
import uuid
|
| 16 |
-
from datetime import datetime
|
| 17 |
-
from enum import Enum
|
| 18 |
-
from typing import Annotated, Any, Dict, Literal, Optional, Union
|
| 19 |
-
|
| 20 |
-
from pydantic import BaseModel, Field, field_validator
|
| 21 |
-
from pydantic.functional_validators import AfterValidator
|
| 22 |
-
|
| 23 |
-
# -----------------------------------------------------------------------------
|
| 24 |
-
# Domain Primitives (NewTypes for type safety)
|
| 25 |
-
# -----------------------------------------------------------------------------
|
| 26 |
-
# These are simple wrappers that enforce type checks at runtime only if validators are added.
|
| 27 |
-
# Here we use them as markers; actual validation occurs in field validators.
|
| 28 |
-
Region = str
|
| 29 |
-
Size = str
|
| 30 |
-
Principal = str
|
| 31 |
-
ResourceScope = str
|
| 32 |
-
ServiceName = str
|
| 33 |
-
ChangeScope = Literal["single_instance", "canary", "global"]
|
| 34 |
-
Environment = Literal["dev", "staging", "prod", "test"]
|
| 35 |
-
|
| 36 |
-
# -----------------------------------------------------------------------------
|
| 37 |
-
# Enums for fixed sets (but extensible via new variants)
|
| 38 |
-
# -----------------------------------------------------------------------------
|
| 39 |
-
class ResourceType(str, Enum):
|
| 40 |
-
"""Azure resource types with semantic meaning."""
|
| 41 |
-
VM = "vm"
|
| 42 |
-
STORAGE_ACCOUNT = "storage_account"
|
| 43 |
-
DATABASE = "database"
|
| 44 |
-
KUBERNETES_CLUSTER = "kubernetes_cluster"
|
| 45 |
-
FUNCTION_APP = "function_app"
|
| 46 |
-
VIRTUAL_NETWORK = "virtual_network"
|
| 47 |
-
|
| 48 |
-
# We could add methods here to return associated pricing categories, etc.
|
| 49 |
-
|
| 50 |
-
class PermissionLevel(str, Enum):
|
| 51 |
-
"""Access permission levels in increasing order of privilege."""
|
| 52 |
-
READ = "read"
|
| 53 |
-
WRITE = "write"
|
| 54 |
-
ADMIN = "admin"
|
| 55 |
-
|
| 56 |
-
# -----------------------------------------------------------------------------
|
| 57 |
-
# Knowledge Base Stubs (simulated – in production would be loaded from external source)
|
| 58 |
-
# -----------------------------------------------------------------------------
|
| 59 |
-
# These are used for semantic validation. In a real system, they would be fetched
|
| 60 |
-
# from Azure APIs or a configuration service.
|
| 61 |
-
VALID_AZURE_REGIONS = {
|
| 62 |
-
"eastus", "eastus2", "westus", "westeurope", "northeurope",
|
| 63 |
-
"southeastasia", "eastasia", "japaneast", "brazilsouth"
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
# Mapping of resource type to plausible size patterns (simplified)
|
| 67 |
-
RESOURCE_SIZE_PATTERNS = {
|
| 68 |
-
ResourceType.VM: {"Standard_D2s_v3", "Standard_D4s_v3", "Standard_D8s_v3", "Standard_D16s_v3"},
|
| 69 |
-
ResourceType.STORAGE_ACCOUNT: {"50GB", "100GB", "1TB", "10TB"},
|
| 70 |
-
ResourceType.DATABASE: {"Basic", "Standard", "Premium"},
|
| 71 |
-
ResourceType.KUBERNETES_CLUSTER: {"Small", "Medium", "Large"},
|
| 72 |
-
ResourceType.FUNCTION_APP: {"Consumption", "Premium"},
|
| 73 |
-
ResourceType.VIRTUAL_NETWORK: {"default"},
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
# -----------------------------------------------------------------------------
|
| 77 |
-
# Base Intent Class
|
| 78 |
-
# -----------------------------------------------------------------------------
|
| 79 |
-
class Intent(BaseModel):
|
| 80 |
-
"""Abstract base for all intents, providing common fields."""
|
| 81 |
-
intent_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique identifier for this intent")
|
| 82 |
-
timestamp: datetime = Field(default_factory=datetime.utcnow, description="Time the intent was created")
|
| 83 |
-
requester: Principal = Field(..., description="User or service principal requesting the action")
|
| 84 |
-
provenance: Dict[str, Any] = Field(
|
| 85 |
-
default_factory=dict,
|
| 86 |
-
description="Metadata about how the intent was generated (e.g., agent ID, session)"
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
class Config:
|
| 90 |
-
frozen = True # immutable after creation
|
| 91 |
-
extra = "forbid" # no extra fields
|
| 92 |
-
|
| 93 |
-
# -----------------------------------------------------------------------------
|
| 94 |
-
# Specific Intent Types
|
| 95 |
-
# -----------------------------------------------------------------------------
|
| 96 |
-
class ProvisionResourceIntent(Intent):
|
| 97 |
-
"""Request to provision a new Azure resource."""
|
| 98 |
-
intent_type: Literal["provision_resource"] = "provision_resource"
|
| 99 |
-
resource_type: ResourceType
|
| 100 |
-
region: Region
|
| 101 |
-
size: Size
|
| 102 |
-
configuration: Dict[str, Any] = Field(default_factory=dict)
|
| 103 |
-
environment: Environment
|
| 104 |
-
|
| 105 |
-
@field_validator("region")
|
| 106 |
-
def validate_region(cls, v: Region) -> Region:
|
| 107 |
-
if v not in VALID_AZURE_REGIONS:
|
| 108 |
-
raise ValueError(f"Unknown Azure region: {v}")
|
| 109 |
-
return v
|
| 110 |
-
|
| 111 |
-
@field_validator("size")
|
| 112 |
-
def validate_size(cls, v: Size, info) -> Size:
|
| 113 |
-
# info.data contains previously validated fields
|
| 114 |
-
resource_type = info.data.get("resource_type")
|
| 115 |
-
if resource_type and resource_type in RESOURCE_SIZE_PATTERNS:
|
| 116 |
-
if v not in RESOURCE_SIZE_PATTERNS[resource_type]:
|
| 117 |
-
raise ValueError(f"Invalid size '{v}' for resource type {resource_type}")
|
| 118 |
-
return v
|
| 119 |
-
|
| 120 |
-
class DeployConfigurationIntent(Intent):
|
| 121 |
-
"""Request to change configuration of an existing service."""
|
| 122 |
-
intent_type: Literal["deploy_config"] = "deploy_config"
|
| 123 |
-
service_name: ServiceName
|
| 124 |
-
change_scope: ChangeScope
|
| 125 |
-
deployment_target: Environment
|
| 126 |
-
risk_level_hint: Optional[Annotated[float, Field(ge=0, le=1)]] = None
|
| 127 |
-
configuration: Dict[str, Any] = Field(default_factory=dict)
|
| 128 |
-
|
| 129 |
-
# Optional: validate that service_name follows naming conventions
|
| 130 |
-
@field_validator("service_name")
|
| 131 |
-
def validate_service_name(cls, v: ServiceName) -> ServiceName:
|
| 132 |
-
if not v or len(v) < 3:
|
| 133 |
-
raise ValueError("Service name must be at least 3 characters")
|
| 134 |
-
return v
|
| 135 |
-
|
| 136 |
-
class GrantAccessIntent(Intent):
|
| 137 |
-
"""Request to grant a permission to a principal."""
|
| 138 |
-
intent_type: Literal["grant_access"] = "grant_access"
|
| 139 |
-
principal: Principal
|
| 140 |
-
permission_level: PermissionLevel
|
| 141 |
-
resource_scope: ResourceScope
|
| 142 |
-
justification: Optional[str] = None
|
| 143 |
-
|
| 144 |
-
# Validate resource_scope format (simplified)
|
| 145 |
-
@field_validator("resource_scope")
|
| 146 |
-
def validate_resource_scope(cls, v: ResourceScope) -> ResourceScope:
|
| 147 |
-
if not v.startswith("/"):
|
| 148 |
-
raise ValueError("Resource scope must start with '/'")
|
| 149 |
-
return v
|
| 150 |
-
|
| 151 |
-
# -----------------------------------------------------------------------------
|
| 152 |
-
# Discriminated Union of All Intents
|
| 153 |
-
# -----------------------------------------------------------------------------
|
| 154 |
-
InfrastructureIntent = Annotated[
|
| 155 |
-
Union[ProvisionResourceIntent, DeployConfigurationIntent, GrantAccessIntent],
|
| 156 |
-
Field(discriminator="intent_type")
|
| 157 |
-
]
|
| 158 |
-
|
| 159 |
-
__all__ = [
|
| 160 |
-
"ResourceType",
|
| 161 |
-
"PermissionLevel",
|
| 162 |
-
"Environment",
|
| 163 |
-
"ChangeScope",
|
| 164 |
-
"ProvisionResourceIntent",
|
| 165 |
-
"DeployConfigurationIntent",
|
| 166 |
-
"GrantAccessIntent",
|
| 167 |
-
"InfrastructureIntent",
|
| 168 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/policies.py
DELETED
|
@@ -1,171 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Policy Algebra – Composable, typed policies for infrastructure governance.
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
from __future__ import annotations
|
| 6 |
-
|
| 7 |
-
from abc import ABC, abstractmethod
|
| 8 |
-
from dataclasses import dataclass
|
| 9 |
-
from typing import Any, Callable, Dict, List, Optional, Protocol, Set, TypeVar, Union
|
| 10 |
-
|
| 11 |
-
from pydantic import BaseModel
|
| 12 |
-
|
| 13 |
-
# Relative imports for intents
|
| 14 |
-
from .intents import (
|
| 15 |
-
InfrastructureIntent,
|
| 16 |
-
ProvisionResourceIntent,
|
| 17 |
-
GrantAccessIntent,
|
| 18 |
-
ResourceType,
|
| 19 |
-
PermissionLevel,
|
| 20 |
-
Environment,
|
| 21 |
-
)
|
| 22 |
-
|
| 23 |
-
Violation = str
|
| 24 |
-
EvalResult = List[Violation]
|
| 25 |
-
|
| 26 |
-
class Policy(ABC):
|
| 27 |
-
@abstractmethod
|
| 28 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 29 |
-
pass
|
| 30 |
-
|
| 31 |
-
def __and__(self, other: Policy) -> Policy:
|
| 32 |
-
return AndPolicy(self, other)
|
| 33 |
-
|
| 34 |
-
def __or__(self, other: Policy) -> Policy:
|
| 35 |
-
return OrPolicy(self, other)
|
| 36 |
-
|
| 37 |
-
def __invert__(self) -> Policy:
|
| 38 |
-
return NotPolicy(self)
|
| 39 |
-
|
| 40 |
-
class AtomicPolicy(Policy, ABC):
|
| 41 |
-
pass
|
| 42 |
-
|
| 43 |
-
@dataclass(frozen=True)
|
| 44 |
-
class RegionAllowedPolicy(AtomicPolicy):
|
| 45 |
-
allowed_regions: Set[str]
|
| 46 |
-
|
| 47 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 48 |
-
if isinstance(intent, ProvisionResourceIntent):
|
| 49 |
-
if intent.region not in self.allowed_regions:
|
| 50 |
-
return [f"Region '{intent.region}' not allowed. Allowed: {self.allowed_regions}"]
|
| 51 |
-
return []
|
| 52 |
-
|
| 53 |
-
@dataclass(frozen=True)
|
| 54 |
-
class ResourceTypeRestrictedPolicy(AtomicPolicy):
|
| 55 |
-
forbidden_types: Set[ResourceType]
|
| 56 |
-
|
| 57 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 58 |
-
if isinstance(intent, ProvisionResourceIntent):
|
| 59 |
-
if intent.resource_type in self.forbidden_types:
|
| 60 |
-
return [f"Resource type '{intent.resource_type.value}' is forbidden."]
|
| 61 |
-
return []
|
| 62 |
-
|
| 63 |
-
@dataclass(frozen=True)
|
| 64 |
-
class MaxPermissionLevelPolicy(AtomicPolicy):
|
| 65 |
-
max_level: PermissionLevel
|
| 66 |
-
|
| 67 |
-
_LEVEL_ORDER = {
|
| 68 |
-
PermissionLevel.READ: 1,
|
| 69 |
-
PermissionLevel.WRITE: 2,
|
| 70 |
-
PermissionLevel.ADMIN: 3,
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 74 |
-
if isinstance(intent, GrantAccessIntent):
|
| 75 |
-
if self._LEVEL_ORDER[intent.permission_level] > self._LEVEL_ORDER[self.max_level]:
|
| 76 |
-
return [f"Permission level '{intent.permission_level.value}' exceeds max allowed '{self.max_level.value}'."]
|
| 77 |
-
return []
|
| 78 |
-
|
| 79 |
-
@dataclass(frozen=True)
|
| 80 |
-
class CostThresholdPolicy(AtomicPolicy):
|
| 81 |
-
max_cost_usd: float
|
| 82 |
-
|
| 83 |
-
def evaluate(self, intent: InfrastructureIntent, cost: Optional[float] = None) -> EvalResult:
|
| 84 |
-
return []
|
| 85 |
-
|
| 86 |
-
@dataclass(frozen=True)
|
| 87 |
-
class AndPolicy(Policy):
|
| 88 |
-
left: Policy
|
| 89 |
-
right: Policy
|
| 90 |
-
|
| 91 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 92 |
-
return self.left.evaluate(intent) + self.right.evaluate(intent)
|
| 93 |
-
|
| 94 |
-
@dataclass(frozen=True)
|
| 95 |
-
class OrPolicy(Policy):
|
| 96 |
-
left: Policy
|
| 97 |
-
right: Policy
|
| 98 |
-
|
| 99 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 100 |
-
left_violations = self.left.evaluate(intent)
|
| 101 |
-
right_violations = self.right.evaluate(intent)
|
| 102 |
-
if not left_violations or not right_violations:
|
| 103 |
-
return []
|
| 104 |
-
return left_violations + right_violations
|
| 105 |
-
|
| 106 |
-
@dataclass(frozen=True)
|
| 107 |
-
class NotPolicy(Policy):
|
| 108 |
-
inner: Policy
|
| 109 |
-
|
| 110 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 111 |
-
inner_violations = self.inner.evaluate(intent)
|
| 112 |
-
if inner_violations:
|
| 113 |
-
return []
|
| 114 |
-
else:
|
| 115 |
-
return ["Condition not met (NOT policy)"]
|
| 116 |
-
|
| 117 |
-
class PolicyEvaluator:
|
| 118 |
-
def __init__(self, root_policy: Policy):
|
| 119 |
-
self._root = root_policy
|
| 120 |
-
|
| 121 |
-
def evaluate(self, intent: InfrastructureIntent, context: Optional[Dict[str, Any]] = None) -> EvalResult:
|
| 122 |
-
return self._evaluate_recursive(self._root, intent, context or {})
|
| 123 |
-
|
| 124 |
-
def _evaluate_recursive(self, policy: Policy, intent: InfrastructureIntent, context: Dict[str, Any]) -> EvalResult:
|
| 125 |
-
if isinstance(policy, CostThresholdPolicy):
|
| 126 |
-
cost = context.get('cost_estimate')
|
| 127 |
-
if cost is not None and cost > policy.max_cost_usd:
|
| 128 |
-
return [f"Cost ${cost:.2f} exceeds threshold ${policy.max_cost_usd:.2f}"]
|
| 129 |
-
return []
|
| 130 |
-
if isinstance(policy, AtomicPolicy):
|
| 131 |
-
return policy.evaluate(intent)
|
| 132 |
-
if isinstance(policy, AndPolicy):
|
| 133 |
-
return self._evaluate_recursive(policy.left, intent, context) + self._evaluate_recursive(policy.right, intent, context)
|
| 134 |
-
if isinstance(policy, OrPolicy):
|
| 135 |
-
left = self._evaluate_recursive(policy.left, intent, context)
|
| 136 |
-
right = self._evaluate_recursive(policy.right, intent, context)
|
| 137 |
-
if not left or not right:
|
| 138 |
-
return []
|
| 139 |
-
return left + right
|
| 140 |
-
if isinstance(policy, NotPolicy):
|
| 141 |
-
inner = self._evaluate_recursive(policy.inner, intent, context)
|
| 142 |
-
if inner:
|
| 143 |
-
return []
|
| 144 |
-
return ["Condition not met (NOT policy)"]
|
| 145 |
-
return []
|
| 146 |
-
|
| 147 |
-
def allow_all() -> Policy:
|
| 148 |
-
class _AllowAll(AtomicPolicy):
|
| 149 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 150 |
-
return []
|
| 151 |
-
return _AllowAll()
|
| 152 |
-
|
| 153 |
-
def deny_all() -> Policy:
|
| 154 |
-
class _DenyAll(AtomicPolicy):
|
| 155 |
-
def evaluate(self, intent: InfrastructureIntent) -> EvalResult:
|
| 156 |
-
return ["Action denied by default policy"]
|
| 157 |
-
return _DenyAll()
|
| 158 |
-
|
| 159 |
-
__all__ = [
|
| 160 |
-
"Policy",
|
| 161 |
-
"RegionAllowedPolicy",
|
| 162 |
-
"ResourceTypeRestrictedPolicy",
|
| 163 |
-
"MaxPermissionLevelPolicy",
|
| 164 |
-
"CostThresholdPolicy",
|
| 165 |
-
"AndPolicy",
|
| 166 |
-
"OrPolicy",
|
| 167 |
-
"NotPolicy",
|
| 168 |
-
"PolicyEvaluator",
|
| 169 |
-
"allow_all",
|
| 170 |
-
"deny_all",
|
| 171 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
infrastructure/risk_engine.py
DELETED
|
@@ -1,105 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Risk Scoring Engine – Multi-factor probabilistic risk model.
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
from typing import Dict, List, Optional, Tuple, Any, Callable
|
| 6 |
-
from dataclasses import dataclass, field
|
| 7 |
-
|
| 8 |
-
from .intents import (
|
| 9 |
-
InfrastructureIntent,
|
| 10 |
-
ProvisionResourceIntent,
|
| 11 |
-
GrantAccessIntent,
|
| 12 |
-
DeployConfigurationIntent,
|
| 13 |
-
PermissionLevel,
|
| 14 |
-
Environment,
|
| 15 |
-
)
|
| 16 |
-
|
| 17 |
-
@dataclass(frozen=True)
|
| 18 |
-
class RiskFactor:
|
| 19 |
-
name: str
|
| 20 |
-
weight: float
|
| 21 |
-
score_fn: Callable[[InfrastructureIntent, Optional[float], List[str]], float]
|
| 22 |
-
description: str = ""
|
| 23 |
-
|
| 24 |
-
def __call__(self, intent: InfrastructureIntent, cost: Optional[float], violations: List[str]) -> float:
|
| 25 |
-
return self.score_fn(intent, cost, violations)
|
| 26 |
-
|
| 27 |
-
def intent_type_factor(intent: InfrastructureIntent, cost: Optional[float], violations: List[str]) -> float:
|
| 28 |
-
mapping = {
|
| 29 |
-
"provision_resource": 0.1,
|
| 30 |
-
"grant_access": 0.3,
|
| 31 |
-
"deploy_config": 0.2,
|
| 32 |
-
}
|
| 33 |
-
return mapping.get(intent.intent_type, 0.1)
|
| 34 |
-
|
| 35 |
-
def cost_factor(intent: InfrastructureIntent, cost: Optional[float], violations: List[str]) -> float:
|
| 36 |
-
if not isinstance(intent, ProvisionResourceIntent) or cost is None:
|
| 37 |
-
return 0.0
|
| 38 |
-
return min(cost / 5000.0, 1.0)
|
| 39 |
-
|
| 40 |
-
def permission_factor(intent: InfrastructureIntent, cost: Optional[float], violations: List[str]) -> float:
|
| 41 |
-
if not isinstance(intent, GrantAccessIntent):
|
| 42 |
-
return 0.0
|
| 43 |
-
mapping = {
|
| 44 |
-
PermissionLevel.READ: 0.1,
|
| 45 |
-
PermissionLevel.WRITE: 0.4,
|
| 46 |
-
PermissionLevel.ADMIN: 0.8,
|
| 47 |
-
}
|
| 48 |
-
return mapping.get(intent.permission_level, 0.5)
|
| 49 |
-
|
| 50 |
-
def scope_factor(intent: InfrastructureIntent, cost: Optional[float], violations: List[str]) -> float:
|
| 51 |
-
if not isinstance(intent, DeployConfigurationIntent):
|
| 52 |
-
return 0.0
|
| 53 |
-
mapping = {
|
| 54 |
-
"single_instance": 0.1,
|
| 55 |
-
"canary": 0.2,
|
| 56 |
-
"global": 0.6,
|
| 57 |
-
}
|
| 58 |
-
return mapping.get(intent.change_scope, 0.3)
|
| 59 |
-
|
| 60 |
-
def environment_factor(intent: InfrastructureIntent, cost: Optional[float], violations: List[str]) -> float:
|
| 61 |
-
if hasattr(intent, "environment") and intent.environment == Environment.PROD:
|
| 62 |
-
return 0.1
|
| 63 |
-
return 0.0
|
| 64 |
-
|
| 65 |
-
def policy_violation_factor(intent: InfrastructureIntent, cost: Optional[float], violations: List[str]) -> float:
|
| 66 |
-
return min(len(violations) * 0.2, 0.8)
|
| 67 |
-
|
| 68 |
-
class RiskEngine:
|
| 69 |
-
DEFAULT_FACTORS = [
|
| 70 |
-
RiskFactor("intent_type", 1.0, intent_type_factor, "Base risk from intent type"),
|
| 71 |
-
RiskFactor("cost", 0.3, cost_factor, "Normalized cost estimate"),
|
| 72 |
-
RiskFactor("permission", 0.3, permission_factor, "Permission level being granted"),
|
| 73 |
-
RiskFactor("scope", 0.2, scope_factor, "Deployment scope"),
|
| 74 |
-
RiskFactor("environment", 0.1, environment_factor, "Production environment"),
|
| 75 |
-
RiskFactor("policy_violations", 0.2, policy_violation_factor, "Number of policy violations"),
|
| 76 |
-
]
|
| 77 |
-
|
| 78 |
-
def __init__(self, factors: Optional[List[RiskFactor]] = None):
|
| 79 |
-
self.factors = factors if factors is not None else self.DEFAULT_FACTORS
|
| 80 |
-
|
| 81 |
-
def calculate_risk(
|
| 82 |
-
self,
|
| 83 |
-
intent: InfrastructureIntent,
|
| 84 |
-
cost_estimate: Optional[float],
|
| 85 |
-
policy_violations: List[str],
|
| 86 |
-
) -> Tuple[float, str, Dict[str, float]]:
|
| 87 |
-
total = 0.0
|
| 88 |
-
contributions = {}
|
| 89 |
-
|
| 90 |
-
for factor in self.factors:
|
| 91 |
-
raw_score = factor(intent, cost_estimate, policy_violations)
|
| 92 |
-
weighted = raw_score * factor.weight
|
| 93 |
-
contributions[factor.name] = weighted
|
| 94 |
-
total += weighted
|
| 95 |
-
|
| 96 |
-
total = max(0.0, min(total, 1.0))
|
| 97 |
-
|
| 98 |
-
lines = [f"Total risk score: {total:.2f}"]
|
| 99 |
-
for factor in self.factors:
|
| 100 |
-
contrib = contributions[factor.name]
|
| 101 |
-
if contrib > 0.0:
|
| 102 |
-
lines.append(f" - {factor.name}: {contrib:.2f} ({factor.description})")
|
| 103 |
-
explanation = "\n".join(lines)
|
| 104 |
-
|
| 105 |
-
return total, explanation, contributions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|