บทนำ: ทำไมต้อง Gray Release?

ในระบบ Production ระดับจริง การ deploy โมเดล AI โดยตรง 100% เสี่ยงเกินไป โดยเฉพาะเมื่อใช้ HolyShehe AI ที่มีโมเดลหลายตัว เช่น GPT-4.1 ($8/MTok), Claude Sonnet 4.5 ($15/MTok), Gemini 2.5 Flash ($2.50/MTok), DeepSeek V3.2 ($0.42/MTok) — การเลือกผิดโมเดลเพียง 1% ของ request ก็กระทบต้นทุนมหาศาลแล้ว Gray Release (Canary Deployment) ช่วยให้เราทดสอบโมเดลใหม่กับผู้ใช้จำนวนน้อยก่อน และค่อยๆ ขยาย ส่วน A/B Testing ช่วยให้เราเปรียบเทียบผลลัพธ์ของโมเดลต่างๆ อย่างเป็นวิทยาศาสตร์
"""
Gray Release Router - Production Ready
Compatible with HolySheep AI API (https://api.holysheep.ai/v1)
"""

import hashlib
import time
import asyncio
from dataclasses import dataclass
from typing import Optional, Dict, List, Callable
from enum import Enum
import httpx

class TrafficStrategy(Enum):
    CANARY = "canary"           # ส่ง % ไปโมเดลใหม่
    AB_TEST = "ab_test"         # แบ่งครึ่งเปรียบเทียบ
    FEATURE_FLAG = "feature"    # flag-based routing
    WEIGHTED = "weighted"       # weighted round-robin

@dataclass
class ModelConfig:
    name: str
    base_url: str = "https://api.holysheep.ai/v1"
    weight: float = 1.0
    max_rpm: int = 1000
    timeout: float = 30.0
    fallback_to: Optional[str] = None
    
    def __post_init__(self):
        self.current_rpm: int = 0
        self.last_reset: float = time.time()
        self.error_count: int = 0
        self.latencies: List[float] = []

class GrayReleaseRouter:
    def __init__(self):
        self.models: Dict[str, ModelConfig] = {}
        self.api_key: str = ""  # YOUR_HOLYSHEEP_API_KEY
        self.strategy: TrafficStrategy = TrafficStrategy.CANARY
        self.canary_percentage: float = 10.0  # 10% ไป canary
        
        # Circuit breaker state
        self.circuit_open: Dict[str, float] = {}
        self.circuit_timeout: float = 60.0
        
    def add_model(self, name: str, config: ModelConfig) -> None:
        self.models[name] = config
        
    def set_api_key(self, key: str) -> None:
        self.api_key = key
        
    def _get_user_hash(self, user_id: str) -> float:
        """สร้าง deterministic hash สำหรับ routing"""
        hash_input = f"{user_id}:{int(time.time() / 3600)}"
        return float(int(hashlib.md5(hash_input.encode()).hexdigest()[:8], 16)) / 0xFFFFFFFF
    
    def _should_use_canary(self, user_id: str) -> bool:
        """ตัดสินใจว่า request นี้ไป canary หรือ stable"""
        if "stable" not in self.models or "canary" not in self.models:
            return False
        return self._get_user_hash(user_id) * 100 < self.canary_percentage
    
    def _get_circuit_state(self, model_name: str) -> bool:
        """ตรวจสอบ circuit breaker (True = open/blocked)"""
        if model_name not in self.circuit_open:
            return False
        if time.time() - self.circuit_open[model_name] > self.circuit_timeout:
            del self.circuit_open[model_name]
            return False
        return True
    
    def _record_success(self, model_name: str, latency: float) -> None:
        """บันทึกความสำเร็จสำหรับ monitoring"""
        if model_name in self.models:
            self.models[model_name].latencies.append(latency)
            if len(self.models[model_name].latencies) > 100:
                self.models[model_name].latencies.pop(0)
            self.models[model_name].error_count = 0
            
    def _record_failure(self, model_name: str) -> None:
        """บันทึก failure และ trigger circuit breaker"""
        if model_name in self.models:
            self.models[model_name].error_count += 1
            if self.models[model_name].error_count >= 5:
                self.circuit_open[model_name] = time.time()
                print(f"[ALERT] Circuit breaker opened for {model_name}")
                
    def _get_next_weighted_model(self) -> str:
        """Weighted round-robin selection"""
        total_weight = sum(m.weight for m in self.models.values())
        rand = time.time() % total_weight
        cumulative = 0
        for name, model in self.models.items():
            cumulative += model.weight
            if rand <= cumulative:
                return name
        return list(self.models.keys())[0]
    
    def get_stats(self) -> Dict:
        """ดึงสถิติสำหรับ monitoring"""
        stats = {}
        for name, model in self.models.items():
            avg_latency = sum(model.latencies) / len(model.latencies) if model.latencies else 0
            stats[name] = {
                "avg_latency_ms": round(avg_latency * 1000, 2),
                "error_count": model.error_count,
                "circuit_open": self._get_circuit_state(name),
                "requests": len(model.latencies)
            }
        return stats

ตัวอย่างการตั้งค่า

router = GrayReleaseRouter() router.add_model("stable", ModelConfig(name="gpt-4.1", weight=90)) router.add_model("canary", ModelConfig(name="deepseek-v3.2", weight=10)) router.set_api_key("YOUR_HOLYSHEEP_API_KEY")

การตั้งค่า A/B Testing สำหรับ Multi-Model Comparison

การทำ A/B Testing ที่ดีต้องมีการควบคุม variance และบันทึกผลลัพธ์อย่างเป็นระบบ ด้านล่างคือสถาปัตยกรรมที่ใช้งานจริงใน Production
"""
A/B Testing Framework for AI Model Comparison
Production-grade implementation with statistical significance
"""

import json
import uuid
from datetime import datetime
from typing import Any, Dict, Optional, Tuple
from dataclasses import dataclass, field
import asyncio
import httpx
from scipy import stats  # สำหรับ statistical significance

@dataclass
class Experiment:
    id: str
    name: str
    variants: Dict[str, Dict]  # model_name -> config
    traffic_split: Dict[str, float]  # ต้องรวม = 100
    start_time: datetime = field(default_factory=datetime.now)
    min_sample_size: int = 1000
    metrics: Dict[str, Dict] = field(default_factory=dict)
    is_active: bool = True
    
    def __post_init__(self):
        for variant_name in self.variants:
            self.metrics[variant_name] = {
                "requests": 0,
                "successes": 0,
                "failures": 0,
                "latencies": [],
                "costs": [],
                "user_satisfaction": []
            }

class ABTestManager:
    def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"):
        self.api_key = api_key
        self.base_url = base_url
        self.experiments: Dict[str, Experiment] = {}
        self.ongoing_requests: Dict[str, asyncio.Task] = {}
        
    async def create_experiment(
        self,
        name: str,
        variants: Dict[str, Dict],
        traffic_split: Dict[str, float]
    ) -> str:
        """สร้าง experiment ใหม่"""
        exp_id = str(uuid.uuid4())[:8]
        
        # Validate traffic split
        if abs(sum(traffic_split.values()) - 100) > 0.01:
            raise ValueError(f"Traffic split must sum to 100, got {sum(traffic_split.values())}")
            
        self.experiments[exp_id] = Experiment(
            id=exp_id,
            name=name,
            variants=variants,
            traffic_split=traffic_split
        )
        return exp_id
    
    def _select_variant(self, experiment: Experiment, user_id: str) -> str:
        """Deterministic variant selection"""
        # ใช้ consistent hashing เพื่อให้ user เดิมได้ variant เดิมเสมอ
        hash_val = int(hashlib.md5(f"{experiment.id}:{user_id}".encode()).hexdigest()[:8], 16)
        normalized = (hash_val / 0xFFFFFFFF) * 100
        
        cumulative = 0
        for variant_name, percentage in experiment.traffic_split.items():
            cumulative += percentage
            if normalized < cumulative:
                return variant_name
        return list(experiment.traffic_split.keys())[-1]
    
    async def _call_model(
        self,
        model_config: Dict,
        prompt: str,
        timeout: float = 30.0
    ) -> Tuple[bool, float, float, Any]:
        """
        เรียก HolySheep AI API
        Returns: (success, latency, cost, response)
        """
        model_name = model_config.get("model", "gpt-4.1")
        
        async with httpx.AsyncClient(timeout=timeout) as client:
            start = time.time()
            try:
                response = await client.post(
                    f"{self.base_url}/chat/completions",
                    headers={
                        "Authorization": f"Bearer {self.api_key}",
                        "Content-Type": "application/json"
                    },
                    json={
                        "model": model_name,
                        "messages": [{"role": "user", "content": prompt}],
                        "max_tokens": model_config.get("max_tokens", 1024)
                    }
                )
                latency = time.time() - start
                
                if response.status_code == 200:
                    data = response.json()
                    # คำนวณ cost จาก token usage
                    tokens = data.get("usage", {}).get("total_tokens", 0)
                    cost = self._calculate_cost(model_name, tokens)
                    return True, latency, cost, data
                else:
                    return False, latency, 0, None
                    
            except Exception as e:
                return False, time.time() - start, 0, str(e)
    
    def _calculate_cost(self, model_name: str, tokens: int) -> float:
        """คำนวณ cost ตาม HolySheep pricing 2026"""
        pricing = {
            "gpt-4.1": 8.0,           # $8/MTok
            "claude-sonnet-4.5": 15.0, # $15/MTok
            "gemini-2.5-flash": 2.50,  # $2.50/MTok
            "deepseek-v3.2": 0.42     # $0.42/MTok
        }
        rate = pricing.get(model_name, 8.0)
        return (tokens / 1_000_000) * rate
    
    async def run_experiment(
        self,
        exp_id: str,
        user_id: str,
        prompt: str
    ) -> Dict[str, Any]:
        """รัน experiment และบันทึกผล"""
        experiment = self.experiments.get(exp_id)
        if not experiment or not experiment.is_active:
            raise ValueError(f"Experiment {exp_id} not found or inactive")
            
        variant_name = self._select_variant(experiment, user_id)
        model_config = experiment.variants[variant_name]
        
        success, latency, cost, response = await self._call_model(model_config, prompt)
        
        # บันทึก metrics
        m = experiment.metrics[variant_name]
        m["requests"] += 1
        if success:
            m["successes"] += 1
            m["latencies"].append(latency)
        else:
            m["failures"] += 1
        m["costs"].append(cost)
        
        return {
            "experiment_id": exp_id,
            "variant": variant_name,
            "success": success,
            "latency_ms": round(latency * 1000, 2),
            "cost_usd": round(cost, 6),
            "response": response
        }
    
    def get_experiment_results(self, exp_id: str) -> Dict:
        """วิเคราะห์ผล experiment พร้อม statistical significance"""
        experiment = self.experiments.get(exp_id)
        if not experiment:
            return {}
            
        results = {
            "experiment_id": exp_id,
            "name": experiment.name,
            "is_conclusive": False,
            "variants": {}
        }
        
        all_latencies = {}
        
        for variant_name, metrics in experiment.metrics.items():
            total_requests = metrics["requests"]
            success_rate = metrics["successes"] / total_requests if total_requests > 0 else 0
            avg_latency = sum(metrics["latencies"]) / len(metrics["latencies"]) if metrics["latencies"] else 0
            total_cost = sum(metrics["costs"])
            avg_cost_per_request = total_cost / total_requests if total_requests > 0 else 0
            
            all_latencies[variant_name] = metrics["latencies"]
            
            results["variants"][variant_name] = {
                "requests": total_requests,
                "success_rate": round(success_rate * 100, 2),
                "avg_latency_ms": round(avg_latency * 1000, 2),
                "total_cost_usd": round(total_cost, 6),
                "avg_cost_per_request": round(avg_cost_per_request, 6),
                "p50_latency_ms": round(sorted(metrics["latencies"])[len(metrics["latencies"]) // 2] * 1000, 2) if metrics["latencies"] else 0,
                "p99_latency_ms": round(sorted(metrics["latencies"])[int(len(metrics["latencies"]) * 0.99)] * 1000, 2) if metrics["latencies"] else 0
            }
        
        # Statistical significance test (t-test)
        if len(all_latencies) >= 2:
            variants = list(all_latencies.keys())
            if all_latencies[variants[0]] and all_latencies[variants[1]]:
                t_stat, p_value = stats.ttest_ind(
                    all_latencies[variants[0]], 
                    all_latencies[variants[1]]
                )
                results["statistical_significance"] = {
                    "t_statistic": round(t_stat, 4),
                    "p_value": round(p_value, 6),
                    "significant_at_95": p_value < 0.05,
                    "significant_at_99": p_value < 0.01
                }
        
        # ตรวจสอบว่ามี sample size เพียงพอหรือยัง
        min_samples = min(m["requests"] for m in experiment.metrics.values())
        results["is_conclusive"] = min_samples >= experiment.min_sample_size
        
        return results

ตัวอย่างการใช้งาน

async def main(): manager = ABTestManager(api_key="YOUR_HOLYSHEEP_API_KEY") # สร้าง experiment: เปรียบเทียบ GPT-4.1 vs DeepSeek V3.2 exp_id = await manager.create_experiment( name="Model Cost-Performance Comparison", variants={ "gpt-4.1": {"model": "gpt-4.1", "max_tokens": 1024}, "deepseek-v3.2": {"model": "deepseek-v3.2", "max_tokens": 1024} }, traffic_split={"gpt-4.1": 50, "deepseek-v3.2": 50} ) # รัน 1000 requests for i in range(1000): result = await manager.run_experiment( exp_id, user_id=f"user_{i % 100}", prompt="Explain quantum computing in simple terms" ) # ดึงผลวิเคราะห์ analysis = manager.get_experiment_results(exp_id) print(json.dumps(analysis, indent=2)) if __name__ == "__main__": asyncio.run(main())

การตั้งค่า Feature Flag และ Progressive Rollout

Feature Flag ช่วยให้เราควบคุมการเปิด/ปิดฟีเจอร์ต่างๆ ได้อย่างละเอียด รวมถึงการ progressive rollout ที่ปลอดภัย
"""
Feature Flag System with Progressive Rollout
รองรับ gradual percentage rollout และ targeting rules
"""

from typing import Optional, Dict, Any, Callable
from datetime import datetime, timedelta
import json
import redis.asyncio as redis
from dataclasses import dataclass
import hashlib

@dataclass
class FeatureFlag:
    name: str
    enabled: bool
    rollout_percentage: float = 100.0
    targeting_rules: Optional[Dict] = None
    variants: Optional[Dict[str, float]] = None  # variant_name -> percentage
    metadata: Optional[Dict] = None

class FeatureFlagManager:
    def __init__(self, redis_url: str = "redis://localhost:6379"):
        self.redis: Optional[redis.Redis] = None
        self.redis_url = redis_url
        self.local_cache: Dict[str, FeatureFlag] = {}
        self.cache_ttl: int = 30  # seconds
        
    async def connect(self) -> None:
        self.redis = await redis.from_url(self.redis_url)
        
    async def disconnect(self) -> None:
        if self.redis:
            await self.redis.close()
    
    def _get_user_bucket(self, user_id: str, flag_name: str) -> float:
        """Deterministic bucket assignment (0.0 - 1.0)"""
        hash_input = f"{flag_name}:{user_id}"
        hash_val = int(hashlib.sha256(hash_input.encode()).hexdigest()[:8], 16)
        return hash_val / 0xFFFFFFFF
    
    def _check_targeting_rules(
        self, 
        user: Dict, 
        rules: Dict
    ) -> bool:
        """ตรวจสอบ targeting rules"""
        for rule in rules.get("conditions", []):
            attribute = rule.get("attribute")
            operator = rule.get("operator")
            value = rule.get("value")
            
            user_value = user.get(attribute)
            
            if operator == "eq" and user_value != value:
                return False
            elif operator == "neq" and user_value == value:
                return False
            elif operator == "in" and user_value not in value:
                return False
            elif operator == "gt" and (user_value is None or user_value <= value):
                return False
            elif operator == "lt" and (user_value is None or user_value >= value):
                return False
                
        return True
    
    async def is_enabled(
        self,
        flag_name: str,
        user_id: Optional[str] = None,
        user_context: Optional[Dict] = None,
        default: bool = False
    ) -> bool:
        """ตรวจสอบว่า feature flag เปิดอยู่หรือไม่"""
        flag = await self._get_flag(flag_name)
        if not flag:
            return default
            
        if not flag.enabled:
            return False
            
        if flag.rollout_percentage >= 100.0:
            return True
            
        if not user_id:
            return flag.rollout_percentage > 0
            
        # Check targeting rules first
        if flag.targeting_rules and user_context:
            if not self._check_targeting_rules(user_context, flag.targeting_rules):
                return False
                
        # Check rollout percentage
        bucket = self._get_user_bucket(user_id, flag_name)
        return bucket * 100 < flag.rollout_percentage
    
    async def get_variant(
        self,
        flag_name: str,
        user_id: str
    ) -> Optional[str]:
        """รับ variant ที่ user ได้รับ (สำหรับ A/B testing)"""
        flag = await self._get_flag(flag_name)
        if not flag or not flag.variants:
            return None
            
        bucket = self._get_user_bucket(user_id, flag_name)
        cumulative = 0
        
        for variant_name, percentage in flag.variants.items():
            cumulative += percentage
            if bucket * 100 < cumulative:
                return variant_name
                
        return list(flag.variants.keys())[-1]
    
    async def _get_flag(self, flag_name: str) -> Optional[FeatureFlag]:
        """ดึง flag จาก cache หรือ Redis"""
        if flag_name in self.local_cache:
            return self.local_cache[flag_name]
            
        if self.redis:
            cached = await self.redis.get(f"ff:{flag_name}")
            if cached:
                data = json.loads(cached)
                flag = FeatureFlag(**data)
                self.local_cache[flag_name] = flag
                return flag
                
        return None
    
    async def set_flag(
        self,
        flag: FeatureFlag,
        sync_immediately: bool = True
    ) -> None:
        """ตั้งค่า feature flag"""
        self.local_cache[flag.name] = flag
        
        if self.redis:
            await self.redis.set(
                f"ff:{flag.name}",
                json.dumps(flag.__dict__, default=str),
                ex=self.cache_ttl
            )
            
        if sync_immediately:
            await self._sync_to_source(flag)
    
    async def progressive_rollout(
        self,
        flag_name: str,
        target_percentage: float,
        step: float = 5.0,
        interval_seconds: int = 300
    ) -> None:
        """Progressive rollout - เพิ่ม % ทีละน้อย"""
        flag = await self._get_flag(flag_name)
        if not flag:
            return
            
        current = flag.rollout_percentage
        while current < target_percentage:
            current = min(current + step, target_percentage)
            flag.rollout_percentage = current
            await self.set_flag(flag)
            print(f"[Rollout] {flag_name}: {current}%")
            await asyncio.sleep(interval_seconds)
    
    async def _sync_to_source(self, flag: FeatureFlag) -> None:
        """Sync ไปยัง source of truth (e.g., LaunchDarkly, ConfigCat)"""
        # Implementation depends on your flag management service
        pass

ตัวอย่าง Progressive Rollout

async def example_progressive_rollout(): manager = FeatureFlagManager() await manager.connect() # ตั้งค่า flag เริ่มต้น 5% await manager.set_flag(FeatureFlag( name="new-ai-model", enabled=True, rollout_percentage=5.0, variants={ "gpt-4.1": 50, "deepseek-v3.2": 50 } )) # Progressive rollout: 5% -> 25% -> 50% -> 100% (ทีละ 5%, ทุก 5 นาที) await manager.progressive_rollout( "new-ai-model", target_percentage=100.0, step=5.0, interval_seconds=300 ) await manager.disconnect()

ตัวอย่างการใช้งานใน request handling

async def handle_request(user_id: str, user_context: Dict): manager = FeatureFlagManager() # ตรวจสอบ flag หลายตัว use_new_model = await manager.is_enabled( "new-ai-model", user_id=user_id, user_context=user_context ) use_streaming = await manager.is_enabled( "streaming-response", user_id=user_id, default=True # default เป็น True ถ้าไม่มี flag ) # Get variant for A/B test model_variant = await manager.get_variant("model-selection", user_id) return { "use_new_model": use_new_model, "use_streaming": use_streaming, "model_variant": model_variant }

Performance Benchmark และ Cost Optimization

การเลือกโมเดลที่เหมาะสมไม่ใช่แค่เรื่องคุณภาพ แต่ต้องคำนึงถึง latency และต้นทุนด้วย ด้านล่างคือผล benchmark จริงจาก HolySheep AI
"""
Performance Benchmark Tool
เปรียบเทียบ latency และ cost ของโมเดลต่างๆ
"""

import asyncio
import statistics
from datetime import datetime
from typing import List, Dict
import httpx

async def benchmark_model(
    base_url: str,
    api_key: str,
    model: str,
    num_requests: int = 100,
    prompt: str = "What is artificial intelligence?"
) -> Dict:
    """Benchmark โมเดลเดียว"""
    latencies: List[float] = []
    errors: List[str] = []
    total_tokens = 0
    
    async with httpx.AsyncClient(timeout=60.0) as client:
        for i in range(num_requests):
            start = time.time()
            try:
                response = await client.post(
                    f"{base_url}/chat/completions",
                    headers={
                        "Authorization": f"Bearer {api_key}",
                        "Content-Type": "application/json"
                    },
                    json={
                        "model": model,
                        "messages": [{"role": "user", "content": prompt}],
                        "max_tokens": 256
                    }
                )
                latency = time.time() - start
                latencies.append(latency)
                
                if response.status_code == 200:
                    data = response.json()
                    total_tokens += data.get("usage", {}).get("total_tokens", 0)
                else:
                    errors.append(f"HTTP {response.status_code}")
                    
            except Exception as e:
                errors.append(str(e))
                
            if i % 10 == 0:
                await asyncio.sleep(0.1)  # Rate limiting
    
    if not latencies:
        return {"error": "No successful requests"}
        
    # คำนวณต้นทุน
    pricing = {
        "gpt-4.1": 8.0,
        "claude-sonnet-4.5": 15.0,
        "gemini-2.5-flash": 2.50,
        "deepseek-v3.2": 0.42
    }
    cost_per_mtok = pricing.get(model, 8.0)
    total_cost = (total_tokens / 1_000_000) * cost_per_mtok
    
    return {
        "model": model,
        "successful_requests": len(latencies),
        "failed_requests": len(errors),
        "latency": {
            "min_ms": round(min(latencies) * 1000, 2),
            "max_ms": round(max(latencies) * 1000, 2),
            "mean_ms": round(statistics.mean(latencies) * 1000, 2),
            "median_ms": round(statistics.median(latencies) * 1000, 2),
            "p95_ms": round(sorted(latencies)[int(len(latencies) * 0.95)] * 1000, 2),
            "p99_ms": round(sorted(latencies)[int(len(latencies) * 0.99)] * 1000, 2),
            "std_dev_ms": round(statistics.stdev(latencies) * 1000, 2) if len(latencies) > 1 else 0
        },
        "tokens": {
            "total": total_tokens,
            "avg_per_request": round(total_tokens / len(latencies), 1)
        },
        "cost": {
            "total_usd": round(total_cost, 6),
            "per_1k_requests": round((total_cost / len(latencies)) * 1000, 4),
            "per_mtok": cost_per_mtok
        }
    }

async def run_full_benchmark():
    """Run benchmark ทุกโมเดล"""
    base_url = "https://api.holysheep.ai/v1"
    api_key = "YOUR_HOLYSHEEP_API_KEY"  # เปลี่ยนเป็น key จริง
    
    models = [
        "gpt-4.1",
        "claude-sonnet-4.5",
        "gemini-2.5-flash",
        "deepseek-v3.2"
    ]
    
    results = {}
    for model in models:
        print(f"Benchmarking {model}...")
        results[model] = await benchmark_model(
            base_url, api_key, model, num_requests=50
        )
        print(f"  -> Mean latency: {results[model]['latency']['mean_ms']}ms")
        print(f"  -> Cost per 1K: ${results[model]['cost']['per_1k_requests']}")
        await asyncio.sleep(2)
    
    # สรุปผล
    print("\n" + "="*60)
    print("BENCHMARK SUMMARY")
    print("="*60)
    
    sorted_by_speed = sorted(results.items(), 
                             key=lambda x: x[1]["latency"]["mean_ms"])
    sorted_by_cost = sorted(results.items(),
                           key=lambda x: x[1]["cost"]["per