"""Cost anomaly detection helpers for Alibaba Cloud.""" from __future__ import annotations import statistics from collections import defaultdict from typing import Dict, Iterable, List, Optional class AnomalyDetector: """Compute baselines and highlight outliers for daily billing data.""" def __init__(self, thresholds: Dict[str, float]) -> None: self.thresholds = thresholds # --------------------------- Baselines --------------------------- def account_baseline(self, historical_records: Iterable[Dict[str, float]]) -> Dict[str, float]: daily_costs = defaultdict(float) for row in historical_records: daily_costs[row["date"]] += float(row["cost"]) costs = list(daily_costs.values()) if not costs: return {"avg": 0.0, "median": 0.0, "std": 0.0, "sample_size": 0} return { "avg": statistics.mean(costs), "median": statistics.median(costs), "std": statistics.stdev(costs) if len(costs) > 1 else 0.0, "sample_size": len(costs), } def service_baselines( self, historical_records: Iterable[Dict[str, float]] ) -> Dict[str, Dict[str, float]]: grouped = defaultdict(lambda: defaultdict(float)) for row in historical_records: key = row["product"] grouped[key][row["date"]] += float(row["cost"]) baselines: Dict[str, Dict[str, float]] = {} for service, daily_costs in grouped.items(): values = list(daily_costs.values()) baselines[service] = { "avg": statistics.mean(values), "median": statistics.median(values), "std": statistics.stdev(values) if len(values) > 1 else 0.0, "sample_size": len(values), } return baselines # --------------------------- Detection --------------------------- def detect_account( self, current_cost: float, baseline: Dict[str, float], ) -> Optional[Dict[str, float]]: if baseline["avg"] <= 0: return None ratio = current_cost / baseline["avg"] thresholds = self.thresholds if ( ratio >= thresholds.get("accountCritical", 2.0) and current_cost >= thresholds.get("minCostThreshold", 1.0) ): return {"severity": "critical", "ratio": ratio, "baseline": baseline["avg"]} if ( ratio >= thresholds.get("accountWarning", 1.5) and current_cost >= thresholds.get("minCostThreshold", 1.0) ): return {"severity": "warning", "ratio": ratio, "baseline": baseline["avg"]} return None def detect_service( self, service: str, current_cost: float, baseline_map: Dict[str, Dict[str, float]], ) -> Optional[Dict[str, float]]: baseline = baseline_map.get(service) if not baseline or baseline["avg"] <= 0: return None ratio = current_cost / baseline["avg"] thresholds = self.thresholds if ( ratio >= thresholds.get("serviceCritical", 3.0) and current_cost >= thresholds.get("minCostThreshold", 1.0) ): return {"severity": "critical", "ratio": ratio, "baseline": baseline["avg"]} if ( ratio >= thresholds.get("serviceWarning", 2.0) and current_cost >= thresholds.get("minCostThreshold", 1.0) ): return {"severity": "warning", "ratio": ratio, "baseline": baseline["avg"]} return None def detect_new_services( self, current_services: Dict[str, float], baseline_map: Dict[str, Dict[str, float]], ) -> List[Dict[str, float]]: findings: List[Dict[str, float]] = [] threshold = self.thresholds.get("newServiceThreshold", 10.0) for service, cost in current_services.items(): if service not in baseline_map and cost >= threshold: findings.append({"service": service, "cost": cost}) return findings