Trong một đêm cao điểm production, hệ thống chatbot của tôi đột nhiên chết hoàn toàn. ConnectionError: timeout exceeded 30s — hàng nghìn người dùng không thể truy cập. Đó là khoảnh khắc tôi nhận ra rằng kiến trúc single-point-of-failure là con dao hai lưỡi. Bài viết này là toàn bộ bài học xương máu của tôi khi xây dựng hệ thống Claude API high-availability với HolySheep AI.

Tại Sao Cần Kiến Trúc High-Availability?

Với HolySheep AI, chi phí chỉ $15/MTok cho Claude Sonnet 4.5 (so với $80+ tại các provider khác), nhưng downtime vẫn có thể gây thiệt hại nghiêm trọng. Một kiến trúc HA đúng cách đảm bảo:

Kiến Trúc Tổng Quan

+------------------+      +-------------------+      +------------------+
|   Load Balancer  |----->|  API Gateway      |----->|  HolySheep API   |
|   (nginx/haproxy)|      |  (Health Check)   |      |  api.holysheep.ai|
+------------------+      +-------------------+      +------------------+
         |                         |
         v                         v
+------------------+      +-------------------+
|  Circuit Breaker |      |  Response Cache   |
|  (Resilience4j)  |      |  (Redis/TTL)       |
+------------------+      +-------------------+
         |
         v
+------------------+      +-------------------+
|  Fallback Models |----->|  Queue System     |
|  (GPT-4.1 $8)    |      |  (Redis Queue)    |
+------------------+      +-------------------+

1. Cấu Hình Connection Pool Và Retry Logic

Đây là lớp nền tảng quan trọng nhất. Tôi đã mất 3 ngày debug một lỗi 401 Unauthorized chỉ vì không config đúng timeout.

# config/holy_sheep_client.py
import httpx
from typing import Optional, Dict, Any
from tenacity import retry, stop_after_attempt, wait_exponential
import asyncio

class HolySheepClaudeClient:
    """Client cấp doanh nghiệp với high-availability"""
    
    def __init__(
        self,
        api_key: str = "YOUR_HOLYSHEEP_API_KEY",
        base_url: str = "https://api.holysheep.ai/v1",
        max_connections: int = 100,
        max_keepalive_connections: int = 20,
        timeout: float = 60.0,
        max_retries: int = 3
    ):
        self.api_key = api_key
        self.base_url = base_url
        
        # Connection pool config — điều này QUAN TRỌNG
        limits = httpx.Limits(
            max_connections=max_connections,
            max_keepalive_connections=max_keepalive_connections,
            keepalive_expiry=30.0
        )
        
        # Timeout strategy phân tầng
        self.timeout = httpx.Timeout(
            timeout,
            connect=10.0,      # Connect timeout
            read=45.0,         # Read timeout  
            write=10.0,        # Write timeout
            pool=5.0           # Pool acquisition timeout
        )
        
        self.client = httpx.AsyncClient(
            limits=limits,
            timeout=self.timeout,
            headers={
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json",
                "X-Request-ID": "",  # Tracing ID
            }
        )
        
        # Circuit breaker state
        self._failure_count = 0
        self._circuit_open = False
        self._last_failure_time = None
        
    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=2, max=10)
    )
    async def chat_completions(
        self,
        messages: list,
        model: str = "claude-sonnet-4-20250514",
        temperature: float = 0.7,
        max_tokens: int = 4096,
        **kwargs
    ) -> Dict[str, Any]:
        """
        Gọi Claude API với retry logic tự động.
        """
        if self._circuit_open:
            raise CircuitBreakerOpenError(
                "Circuit breaker is OPEN. Fallback activated."
            )
        
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature,
            "max_tokens": max_tokens,
            **kwargs
        }
        
        try:
            response = await self.client.post(
                f"{self.base_url}/chat/completions",
                json=payload
            )
            
            # Xử lý HTTP status code
            if response.status_code == 200:
                self._on_success()
                return response.json()
            elif response.status_code == 401:
                # KHÔNG retry 401 — API key lỗi
                raise AuthenticationError(
                    f"Invalid API key: {response.text}"
                )
            elif response.status_code == 429:
                # Rate limit — retry với exponential backoff
                retry_after = int(
                    response.headers.get("Retry-After", 5)
                )
                await asyncio.sleep(retry_after)
                raise RateLimitError("Rate limit exceeded")
            else:
                raise APIError(
                    f"HTTP {response.status_code}: {response.text}"
                )
                
        except httpx.TimeoutException as e:
            self._on_failure()
            raise TimeoutError(f"Request timeout: {e}")
        except httpx.ConnectError as e:
            self._on_failure()
            raise ConnectionError(f"Connection failed: {e}")

    def _on_success(self):
        """Reset circuit breaker khi thành công"""
        self._failure_count = 0
        self._circuit_open = False

    def _on_failure(self):
        """Tăng failure count, open circuit nếu vượt threshold"""
        self._failure_count += 1
        self._last_failure_time = asyncio.get_event_loop().time()
        
        # Open circuit sau 5 failures liên tiếp
        if self._failure_count >= 5:
            self._circuit_open = True
            print(f"[WARNING] Circuit breaker OPENED at {self._failure_count} failures")

class CircuitBreakerOpenError(Exception):
    pass

class RateLimitError(Exception):
    pass

class AuthenticationError(Exception):
    pass

class APIError(Exception):
    pass

2. Multi-Endpoint Load Balancer Với Health Check

Tôi triển khai một load balancer thông minh kiểm tra health endpoint mỗi 10 giây và tự động loại bỏ endpoint không khả dụng.

# services/load_balancer.py
import asyncio
import time
from dataclasses import dataclass, field
from typing import List, Optional
import httpx
from collections import deque

@dataclass
class Endpoint:
    url: str
    name: str
    healthy: bool = True
    latency_ms: float = 0.0
    failure_streak: int = 0
    last_check: float = field(default_factory=time.time)
    response_times: deque = field(default_factory=lambda: deque(maxlen=10))
    
    @property
    def avg_latency(self) -> float:
        if not self.response_times:
            return 0.0
        return sum(self.response_times) / len(self.response_times)

class HolySheepLoadBalancer:
    """
    Load balancer với weighted round-robin và health check.
    Ưu tiên endpoint có latency thấp nhất.
    """
    
    def __init__(
        self,
        endpoints: List[str],
        health_check_interval: int = 10,
        failure_threshold: int = 3
    ):
        self.endpoints = [
            Endpoint(url=url, name=f"endpoint-{i}")
            for i, url in enumerate(endpoints)
        ]
        self.health_check_interval = health_check_interval
        self.failure_threshold = failure_threshold
        self._health_check_task = None
        self._lock = asyncio.Lock()
        
        # Primary endpoint index
        self._current_idx = 0
        
    async def start(self):
        """Khởi động background health check"""
        self._health_check_task = asyncio.create_task(
            self._health_check_loop()
        )
        print("[LOAD_BALANCER] Started with health check")
        
    async def stop(self):
        """Dừng health check"""
        if self._health_check_task:
            self._health_check_task.cancel()
            
    async def get_endpoint(self) -> Endpoint:
        """
        Chọn endpoint tốt nhất dựa trên:
        1. Health status
        2. Average latency
        3. Failure streak
        """
        async with self._lock:
            # Lọc endpoint healthy
            healthy = [ep for ep in self.endpoints if ep.healthy]
            
            if not healthy:
                # Fallback: chọn endpoint có ít failure streak nhất
                fallback = min(
                    self.endpoints, 
                    key=lambda x: x.failure_streak
                )
                print(f"[WARNING] No healthy endpoints. Using fallback: {fallback.name}")
                return fallback
            
            # Weighted selection: ưu tiên latency thấp
            # Endpoint có latency < 100ms được ưu tiên 3x
            weights = []
            for ep in healthy:
                if ep.avg_latency < 100:
                    weights.append(3)
                elif ep.avg_latency < 300:
                    weights.append(2)
                else:
                    weights.append(1)
            
            total_weight = sum(weights)
            import random
            rand_val = random.uniform(0, total_weight)
            
            cumulative = 0
            for i, ep in enumerate(healthy):
                cumulative += weights[i]
                if rand_val <= cumulative:
                    return ep
            
            return healthy[0]
            
    async def _health_check_loop(self):
        """Background health check task"""
        while True:
            try:
                await asyncio.sleep(self.health_check_interval)
                await self._check_all_endpoints()
            except asyncio.CancelledError:
                break
            except Exception as e:
                print(f"[HEALTH_CHECK] Error: {e}")
                
    async def _check_all_endpoints(self):
        """Kiểm tra health của tất cả endpoints"""
        tasks = [self._check_endpoint(ep) for ep in self.endpoints]
        await asyncio.gather(*tasks, return_exceptions=True)
        
    async def _check_endpoint(self, endpoint: Endpoint):
        """Health check đơn lẻ"""
        health_url = f"{endpoint.url}/health"
        
        try:
            start = time.perf_counter()
            async with httpx.AsyncClient() as client:
                response = await client.get(
                    health_url,
                    timeout=5.0
                )
            latency = (time.perf_counter() - start) * 1000
            
            endpoint.latency_ms = latency
            endpoint.response_times.append(latency)
            
            if response.status_code == 200:
                endpoint.healthy = True
                endpoint.failure_streak = 0
                print(f"[HEALTH] {endpoint.name}: OK ({latency:.1f}ms)")
            else:
                await self._mark_unhealthy(endpoint)
                
        except Exception as e:
            await self._mark_unhealthy(endpoint)
            
    async def _mark_unhealthy(self, endpoint: Endpoint):
        """Đánh dấu endpoint không khả dụng"""
        endpoint.failure_streak += 1
        endpoint.healthy = endpoint.failure_streak < self.failure_threshold
        
        if not endpoint.healthy:
            print(f"[HEALTH] {endpoint.name}: FAILED ({endpoint.failure_streak} streaks)")

Usage

async def main(): lb = HolySheepLoadBalancer( endpoints=[ "https://api.holysheep.ai/v1", "https://backup.holysheep.ai/v1", # Backup region ], health_check_interval=10, failure_threshold=3 ) await lb.start() # Test lấy endpoint endpoint = await lb.get_endpoint() print(f"Selected endpoint: {endpoint.name} ({endpoint.url})") await lb.stop() if __name__ == "__main__": asyncio.run(main())

3. Circuit Breaker Pattern Với Fallback

Khi HolySheep API gặp sự cố, hệ thống phải tự động chuyển sang model fallback mà không ảnh hưởng trải nghiệm người dùng. Tôi sử dụng GPT-4.1 ($8/MTok) như fallback chính.

# services/circuit_breaker.py
import asyncio
import time
from enum import Enum
from typing import Callable, Any, Optional
from dataclasses import dataclass

class CircuitState(Enum):
    CLOSED = "closed"      # Normal operation
    OPEN = "open"          # Failing, reject requests
    HALF_OPEN = "half_open"  # Testing recovery

@dataclass
class CircuitBreakerConfig:
    failure_threshold: int = 5      # Open after N failures
    recovery_timeout: int = 60      # Try recovery after N seconds
    half_open_max_calls: int = 3    # Max test calls in half-open
    
class ClaudeCircuitBreaker:
    """
    Circuit breaker cho Claude API với multi-model fallback.
    
    Fallback chain:
    Claude Sonnet 4.5 ($15/MTok) 
        → GPT-4.1 ($8/MTok) 
            → DeepSeek V3.2 ($0.42/MTok)
    """
    
    def __init__(self, config: Optional[CircuitBreakerConfig] = None):
        self.config = config or CircuitBreakerConfig()
        self.state = CircuitState.CLOSED
        self.failure_count = 0
        self.success_count = 0
        self.last_failure_time = None
        self.half_open_calls = 0
        
        # Model configs với pricing
        self.models = [
            {
                "name": "claude-sonnet-4-20250514",
                "provider": "holy_sheep",
                "price_per_mtok": 15.0,
                "quality": "high"
            },
            {
                "name": "gpt-4.1",
                "provider": "holy_sheep", 
                "price_per_mtok": 8.0,
                "quality": "high"
            },
            {
                "name": "deepseek-v3.2",
                "provider": "holy_sheep",
                "price_per_mtok": 0.42,
                "quality": "medium"
            }
        ]
        
    def _should_allow_request(self) -> bool:
        """Kiểm tra xem có nên cho request đi qua không"""
        if self.state == CircuitState.CLOSED:
            return True
            
        if self.state == CircuitState.OPEN:
            # Kiểm tra đã đến lúc thử recovery chưa
            if time.time() - self.last_failure_time >= self.config.recovery_timeout:
                self.state = CircuitState.HALF_OPEN
                self.half_open_calls = 0
                print("[CIRCUIT] OPEN → HALF_OPEN (recovery test)")
                return True
            return False
            
        if self.state == CircuitState.HALF_OPEN:
            return self.half_open_calls < self.config.half_open_max_calls
            
        return False
        
    async def call(
        self,
        client: Any,
        messages: list,
        current_model_idx: int = 0
    ) -> Any:
        """
        Gọi API với circuit breaker và fallback tự động.
        """
        if not self._should_allow_request():
            # Chuyển sang model tiếp theo
            return await self._fallback_to_next_model(
                client, messages, current_model_idx
            )
        
        model = self.models[current_model_idx]
        
        try:
            if self.state == CircuitState.HALF_OPEN:
                self.half_open_calls += 1
                
            response = await client.chat_completions(
                messages=messages,
                model=model["name"]
            )
            
            self._on_success()
            return response
            
        except Exception as e:
            print(f"[CIRCUIT] Model {model['name']} failed: {e}")
            self._on_failure()
            
            # Thử model tiếp theo
            return await self._fallback_to_next_model(
                client, messages, current_model_idx
            )
            
    async def _fallback_to_next_model(
        self,
        client: Any,
        messages: list,
        current_idx: int
    ) -> Any:
        """Fallback sang model rẻ hơn"""
        next_idx = current_idx + 1
        
        if next_idx >= len(self.models):
            # Đã thử hết models, raise error
            raise AllModelsFailedError(
                "All Claude models failed. Last resort: queue for retry."
            )
            
        next_model = self.models[next_idx]
        print(f"[FALLBACK] Switching to {next_model['name']} "
              f"(${next_model['price_per_mtok']}/MTok)")
        
        return await self.call(client, messages, next_idx)
        
    def _on_success(self):
        """Xử lý khi call thành công"""
        self.failure_count = 0
        
        if self.state == CircuitState.HALF_OPEN:
            self.success_count += 1
            if self.success_count >= self.config.half_open_max_calls:
                self.state = CircuitState.CLOSED
                self.success_count = 0
                print("[CIRCUIT] HALF_OPEN → CLOSED (recovery successful)")
        else:
            self.state = CircuitState.CLOSED
            
    def _on_failure(self):
        """Xử lý khi call thất bại"""
        self.failure_count += 1
        self.success_count = 0
        self.last_failure_time = time.time()
        
        if self.state == CircuitState.HALF_OPEN:
            # Thất bại trong recovery test → open lại
            self.state = CircuitState.OPEN
            print("[CIRCUIT] HALF_OPEN → OPEN (recovery failed)")
            
        elif self.failure_count >= self.config.failure_threshold:
            self.state = CircuitState.OPEN
            print(f"[CIRCUIT] CLOSED → OPEN (after {self.failure_count} failures)")
            
    def get_status(self) -> dict:
        """Lấy trạng thái circuit breaker"""
        return {
            "state": self.state.value,
            "failure_count": self.failure_count,
            "last_failure": self.last_failure_time,
            "current_primary_model": self.models[0]["name"]
        }

class AllModelsFailedError(Exception):
    pass

Usage trong main application

async def process_with_circuit_breaker(): from config.holy_sheep_client import HolySheepClaudeClient client = HolySheepClaudeClient( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" ) circuit_breaker = ClaudeCircuitBreaker( CircuitBreakerConfig( failure_threshold=5, recovery_timeout=60, half_open_max_calls=3 ) ) messages = [ {"role": "user", "content": "Giải thích high-availability architecture"} ] try: response = await circuit_breaker.call(client, messages) print(f"Response: {response}") print(f"Circuit status: {circuit_breaker.get_status()}") except AllModelsFailedError as e: # Queue request để retry sau print(f"ALL MODELS DOWN: {e}")

4. Caching Layer Với Redis

Với những request có nội dung tương tự, cache có thể tiết kiệm đến 80% chi phí. Tôi implement semantic cache với Redis.

# services/semantic_cache.py
import hashlib
import json
import redis.asyncio as redis
from typing import Optional, Any
import json_repair

class SemanticCache:
    """
    Semantic caching cho Claude API responses.
    Cache key được tạo từ content hash + model + params.
    """
    
    def __init__(
        self,
        redis_url: str = "redis://localhost:6379/0",
        default_ttl: int = 3600,  # 1 hour
        similarity_threshold: float = 0.95
    ):
        self.redis = redis.from_url(redis_url)
        self.default_ttl = default_ttl
        self.similarity_threshold = similarity_threshold
        
    def _generate_cache_key(
        self,
        messages: list,
        model: str,
        temperature: float,
        **kwargs
    ) -> str:
        """Tạo cache key từ request params"""
        # Compact messages to content only
        content_parts = []
        for msg in messages:
            if isinstance(msg, dict):
                role = msg.get("role", "")
                content = msg.get("content", "")
                content_parts.append(f"{role}:{content}")
            else:
                content_parts.append(str(msg))
        
        content_hash = hashlib.sha256(
            "|".join(content_parts).encode()
        ).hexdigest()[:16]
        
        params_hash = hashlib.md5(
            json.dumps({"model": model, "temp": temperature}, sort_keys=True).encode()
        ).hexdigest()
        
        return f"claude_cache:{content_hash}:{params_hash}"
        
    async def get(
        self,
        messages: list,
        model: str,
        temperature: float = 0.7,
        **kwargs
    ) -> Optional[dict]:
        """Lấy cached response nếu có"""
        cache_key = self._generate_cache_key(
            messages, model, temperature, **kwargs
        )
        
        cached = await self.redis.get(cache_key)
        if cached:
            data = json.loads(cached)
            print(f"[CACHE] HIT for {cache_key[:30]}...")
            return data
            
        print(f"[CACHE] MISS for {cache_key[:30]}...")
        return None
        
    async def set(
        self,
        messages: list,
        model: str,
        response: dict,
        temperature: float = 0.7,
        ttl: Optional[int] = None,
        **kwargs
    ):
        """Lưu response vào cache"""
        cache_key = self._generate_cache_key(
            messages, model, temperature, **kwargs
        )
        
        # Metadata để track usage
        cache_data = {
            "response": response,
            "cached_at": self.redis.time()[0],
            "model": model,
            "tokens_used": response.get("usage", {}).get("total_tokens", 0)
        }
        
        await self.redis.setex(
            cache_key,
            ttl or self.default_ttl,
            json.dumps(cache_data)
        )
        
        print(f"[CACHE] Stored response for {cache_key[:30]}... "
              f"(TTL: {ttl or self.default_ttl}s)")
        
    async def invalidate_pattern(self, pattern: str):
        """Xóa cache theo pattern"""
        keys = []
        async for key in self.redis.scan_iter(match=pattern):
            keys.append(key)
            
        if keys:
            await self.redis.delete(*keys)
            print(f"[CACHE] Invalidated {len(keys)} keys matching {pattern}")
            
    async def get_stats(self) -> dict:
        """Lấy cache statistics"""
        info = await self.redis.info("stats")
        keys_count = await self.redis.dbsize()
        
        # Estimate savings
        total_savings = 0
        async for key in self.redis.scan_iter(match="claude_cache:*"):
            cached = await self.redis.get(key)
            if cached:
                data = json.loads(cached)
                # Claude Sonnet: $15/MTok
                # Giả định average 500 tokens per request
                total_savings += (500 / 1_000_000) * 15
                
        return {
            "total_keys": keys_count,
            "cache_hits": info.get("keyspace_hits", 0),
            "cache_misses": info.get("keyspace_misses", 0),
            "estimated_savings_usd": round(total_savings, 2)
        }

Integration với main client

async def cached_claude_call( client: Any, cache: SemanticCache, messages: list, model: str = "claude-sonnet-4-20250514", use_cache: bool = True, **kwargs ): """ Claude call với automatic caching. """ if use_cache: cached_response = await cache.get(messages, model, **kwargs) if cached_response: return { **cached_response["response"], "cached": True, "cache_hit": True } # Gọi API response = await client.chat_completions( messages=messages, model=model, **kwargs ) # Cache response if use_cache: await cache.set(messages, model, response, **kwargs) return {**response, "cached": False}

Demo

async def demo(): cache = SemanticCache( redis_url="redis://localhost:6379/0", default_ttl=1800 ) # Test cache hit/miss messages = [{"role": "user", "content": "What is AI?"}] result1 = await cached_claude_call( client=None, # Skip actual API call cache=cache, messages=messages, model="claude-sonnet-4-20250514" ) stats = await cache.get_stats() print(f"Cache stats: {stats}") if __name__ == "__main__": import asyncio asyncio.run(demo())

5. Monitoring Và Alerting

Đây là phần quan trọng nhất mà nhiều người bỏ qua. Monitoring không chỉ là watchdog mà còn là early warning system.

# services/monitoring.py
import time
import asyncio
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from collections import deque
import statistics

@dataclass
class MetricsSnapshot:
    timestamp: float
    endpoint: str
    latency_ms: float
    status_code: int
    tokens_used: int = 0
    cached: bool = False
    error: Optional[str] = None

class ClaudeAPIMonitor:
    """
    Real-time monitoring cho Claude API calls.
    Track latency, error rates, token usage và cost.
    """
    
    def __init__(
        self,
        window_size: int = 1000,
        alert_thresholds: Optional[Dict] = None
    ):
        self.metrics: deque = deque(maxlen=window_size)
        self.alert_thresholds = alert_thresholds or {
            "error_rate_pct": 5.0,        # Alert if >5% errors
            "p99_latency_ms": 2000,       # Alert if p99 > 2s
            "rate_limit_pct": 10.0,       # Alert if >10% rate limited
        }
        self._alert_history: deque = deque(maxlen=100)
        self._lock = asyncio.Lock()
        
    async def record(self, snapshot: MetricsSnapshot):
        """Ghi nhận một API call"""
        async with self._lock:
            self.metrics.append(snapshot)
            
            # Check alerts
            await self._check_alerts(snapshot)
            
    async def _check_alerts(self, snapshot: MetricsSnapshot):
        """Kiểm tra và trigger alerts"""
        stats = await self.get_stats()
        
        alerts_triggered = []
        
        # Error rate alert
        if stats["error_rate_pct"] > self.alert_thresholds["error_rate_pct"]:
            alerts_triggered.append({
                "type": "high_error_rate",
                "message": f"Error rate {stats['error_rate_pct']:.1f}% "
                           f"exceeds threshold {self.alert_thresholds['error_rate_pct']}%",
                "severity": "critical"
            })
            
        # Latency alert
        if stats["p99_latency_ms"] > self.alert_thresholds["p99_latency_ms"]:
            alerts_triggered.append({
                "type": "high_latency",
                "message": f"P99 latency {stats['p99_latency_ms']:.0f}ms "
                           f"exceeds threshold {self.alert_thresholds['p99_latency_ms']}ms",
                "severity": "warning"
            })
            
        # Rate limit alert
        if stats["rate_limit_pct"] > self.alert_thresholds["rate_limit_pct"]:
            alerts_triggered.append({
                "type": "rate_limiting",
                "message": f"Rate limit % {stats['rate_limit_pct']:.1f}% "
                           f"indicates need for quota increase",
                "severity": "warning"
            })
            
        for alert in alerts_triggered:
            alert["timestamp"] = time.time()
            self._alert_history.append(alert)
            await self._send_alert(alert)
            
    async def _send_alert(self, alert: dict):
        """Gửi alert (Slack, PagerDuty, etc.)"""
        # In production, integrate with Slack/PagerDuty
        emoji = "🔴" if alert["severity"] == "critical" else "🟡"
        print(f"{emoji} ALERT [{alert['type']}]: {alert['message']}")
        
    async def get_stats(self) -> Dict:
        """Lấy statistics hiện tại"""
        if not self.metrics:
            return self._empty_stats()
            
        latencies = [m.latency_ms for m in self.metrics]
        errors = [m for m in self.metrics if m.error or m.status_code >= 400]
        rate_limited = [m for m in self.metrics if m.status_code == 429]
        cached = [m for m in self.metrics if m.cached]
        total_tokens = sum(m.tokens_used for m in self.metrics)
        
        # Cost calculation (Claude Sonnet 4.5: $15/MTok)
        input_cost = (total_tokens * 0.3 / 1_000_000) * 15
        output_cost = (total_tokens * 0.7 / 1_000_000) * 15
        total_cost = input_cost + output_cost
        
        return {
            "total_requests": len(self.metrics),
            "error_count": len(errors),
            "error_rate_pct": len(errors) / len(self.metrics) * 100,
            "rate_limit_count": len(rate_limited),
            "rate_limit_pct": len(rate_limited) / len(self.metrics) * 100,
            "cache_hit_rate": len(cached) / len(self.metrics) * 100 if self.metrics else 0,
            "avg_latency_ms": statistics.mean(latencies),
            "p50_latency_ms": statistics.median(latencies),
            "p95_latency_ms": statistics.quantiles(latencies, n=20)[18] if len(latencies) >= 20 else statistics.mean(latencies),
            "p99_latency_ms": statistics.quantiles(latencies, n=100)[98] if len(latencies) >= 100 else statistics.mean(latencies),
            "total_tokens": total_tokens,
            "estimated_cost_usd": round(total_cost, 4),
            "requests_per_minute": len([m for m in self.metrics if time.time() - m.timestamp < 60])
        }
        
    def _empty_stats(self) -> Dict:
        return {
            "total_requests": 0,
            "error_rate_pct": 0,
            "avg_latency_ms": 0,
            "p99_latency_ms": 0,
            "total_tokens": 0,
            "estimated_cost_usd": 0
        }
        
    async def get_endpoint_health(self) -> Dict[str, Dict]:
        """Health status của từng endpoint"""
        endpoint_stats: Dict[str, Dict] = {}
        
        for m in self.metrics:
            if m.endpoint not in endpoint_stats:
                endpoint_stats[m.endpoint] = {
                    "requests": 0,
                    "errors": 0,
                    "latencies": []
                }
                
            ep = endpoint_stats[m.endpoint]
            ep["requests"] += 1
            ep["latencies"].append(m.latency_ms)
            if m.error:
                ep["errors"] += 1
                
        # Calculate health score
        for ep, stats in endpoint_stats.items():
            error_rate = stats["errors"] / stats["requests"] * 100
            avg_latency = statistics.mean(stats["latencies"])
            
            # Health score: 100 - error_rate*5 - latency_penalty
            latency_penalty = min(30, avg_latency / 100)
            stats["health_score"] = max(0, 100 - error_rate * 5 - latency_penalty)
            stats["error_rate"] = error_rate
            stats["avg_latency"] = avg_latency
            
        return endpoint_stats

Singleton monitor

monitor = ClaudeAPIMonitor()

Usage

async def demo_monitoring(): # Simulate some requests for i in range(100): snapshot = MetricsSnapshot( timestamp=time.time(), endpoint="api.holysheep.ai", latency_ms=100 + (i % 50), # 100-150ms typical status_code=200, tokens_used=500, cached=(i % 5 == 0) # 20% cache hit ) await monitor.record(snapshot) stats = await monitor.get_stats() print("\n📊 Claude API Monitoring Stats:") print(f" Total Requests: {stats['total_requests']}") print(f" Error Rate: {stats['error_rate_pct']:.2f}%") print(f" Avg Latency: {stats['avg_latency_ms']:.1f}ms") print(f" P99 Latency: {stats['p99_latency_ms']:.1f}ms") print(f" Cache Hit Rate: {stats['cache_hit_rate']:.1f}%") print(f" Estimated Cost: ${stats['estimated_cost_usd']:.4f}") health = await monitor.get_endpoint_health() for ep, h in health.items(): print(f"\n Endpoint: {