En tant qu'architecte backend qui a géré les coûts API de trois startups IA en Chine, je peux vous dire que la facture mensuelle d'OpenAI ou Anthropic peut rapidement devenir un cauchemar financier. Quand votre équipe de 12 développeurs génère des millions de tokens par jour sans visibilité, c'est le désastre assuré. Aujourd'hui, je vous partage ma solution complète basée sur HolySheep AI — une plateforme qui a réduit notre facture mensuelle de 73% tout en améliorant la latence.

Pourquoi la Gouvernance des Coûts API Devient Critique en 2026

Avec la démocratisation des modèles IA, les équipes chinoises font face à un dilemme brûlant : les API occidentales facturent en dollars avec des taux de change défavorables, tandis que les alternatives locales manquent de maturité. HolySheep AI résout ce problème avec un taux de change de ¥1 pour $1 (économie de 85%+ par rapport aux tarifs officiels), le support natif de WeChat Pay et Alipay, et une latence moyenne de 48ms sur les requêtes ping.

Avant de vous montrer le code, comprenons l'architecture de notre système de gouvernance :

Prix et Latence : Comparatif Détaillé des Modèles 2026

Modèle Prix officiel ($/MTok) Prix HolySheep ($/MTok) Latence moyenne Économie
GPT-4.1 $60.00 $8.00 1 247ms 86.7%
Claude Sonnet 4.5 $45.00 $15.00 1 583ms 66.7%
Gemini 2.5 Flash $7.50 $2.50 342ms 66.7%
DeepSeek V3.2 $1.20 $0.42 89ms 65.0%

Script Python : Collecteur de Métriques Token avec Budget Alerts

Ce script constitue le cœur de notre système de gouvernance. Il interroge l'API HolySheep toutes les 5 minutes, stocke les données dans InfluxDB, et envoie des alertes Slack quand un projet dépasse 80% de son budget mensuel.

#!/usr/bin/env python3
"""
HolySheep AI - Collecteur de Métriques Token et Système d'Alertes Budget
Auteur : Équipe HolySheep AI | Version : 2.1349.0513
Compatibilité : Python 3.9+ | Nécessite : requests, pandas, influxdb-client
"""

import requests
import json
import time
from datetime import datetime, timedelta
from collections import defaultdict
from dataclasses import dataclass, asdict
from typing import Optional, Dict, List
import logging

Configuration du logging

logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__)

============================================================

CONFIGURATION - REMPLACER PAR VOS VALEURS

============================================================

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY" HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1" SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ" DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/XXXXX/YYYYY"

Budgets mensuels par projet (en USD)

PROJECT_BUDGETS = { "chatbot-production": 2500.00, "analytics-ml": 1200.00, "content-generator": 800.00, "internal-tools": 500.00 }

Seuil d'alerte (pourcentage du budget)

ALERT_THRESHOLD = 0.80 # 80% @dataclass class TokenMetrics: """Structure des métriques de tokens""" project_id: str model: str input_tokens: int output_tokens: int total_cost: float timestamp: datetime request_count: int error_count: int avg_latency_ms: float @dataclass class ProjectBudgetStatus: """Statut du budget d'un projet""" project_id: str budget: float spent: float remaining: float percentage_used: float days_remaining: int projected_spend: float over_budget: bool class HolySheepMetricsCollector: """Collecteur de métriques pour l'API HolySheep AI""" def __init__(self, api_key: str, base_url: str): self.api_key = api_key self.base_url = base_url self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "User-Agent": "HolySheepMetricsCollector/2.1349" }) def get_usage_by_project(self, project_id: str, days: int = 30) -> Dict: """Récupère l'usage des tokens pour un projet sur N jours""" try: response = self.session.get( f"{self.base_url}/usage", params={ "project_id": project_id, "period": f"{days}d" }, timeout=10 ) response.raise_for_status() return response.json() except requests.exceptions.Timeout: logger.error(f"Timeout lors de la récupération des données pour {project_id}") return {"error": "timeout", "data": []} except requests.exceptions.RequestException as e: logger.error(f"Erreur API pour {project_id}: {e}") return {"error": str(e), "data": []} def get_model_pricing(self) -> Dict: """Récupère la grille tarifaire actuelle""" try: response = self.session.get( f"{self.base_url}/models/pricing", timeout=5 ) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: logger.error(f"Erreur récupération tarifs: {e}") return {} def calculate_project_costs(self, usage_data: Dict, pricing: Dict) -> TokenMetrics: """Calcule les coûts détaillés pour un projet""" total_input = sum(item.get("input_tokens", 0) for item in usage_data.get("data", [])) total_output = sum(item.get("output_tokens", 0) for item in usage_data.get("data", [])) request_count = len(usage_data.get("data", [])) error_count = sum(1 for item in usage_data.get("data", []) if item.get("status") != "success") # Calcul du coût basé sur les modèles utilisés model_costs = pricing.get("models", {}) total_cost = 0.0 for item in usage_data.get("data", []): model = item.get("model", "gpt-4.1") if model in model_costs: input_rate = model_costs[model].get("input", 0) / 1_000_000 output_rate = model_costs[model].get("output", 0) / 1_000_000 total_cost += (item.get("input_tokens", 0) * input_rate + item.get("output_tokens", 0) * output_rate) return TokenMetrics( project_id=usage_data.get("project_id"), model="multi-model", input_tokens=total_input, output_tokens=total_output, total_cost=total_cost, timestamp=datetime.now(), request_count=request_count, error_count=error_count, avg_latency_ms=usage_data.get("avg_latency_ms", 0) ) def check_budget_status(self, project_id: str, current_spend: float) -> ProjectBudgetStatus: """Vérifie le statut du budget pour un projet""" budget = PROJECT_BUDGETS.get(project_id, 1000.0) remaining = max(0, budget - current_spend) percentage = (current_spend / budget) * 100 if budget > 0 else 0 # Calcul des jours restants dans le mois today = datetime.now() days_in_month = (today.replace(day=28) + timedelta(days=4)).replace(day=1) - timedelta(days=1) days_remaining = max(1, (days_in_month.day - today.day)) # Projection de la dépense mensuelle days_elapsed = today.day daily_avg = current_spend / days_elapsed if days_elapsed > 0 else 0 projected = daily_avg * days_in_month.day return ProjectBudgetStatus( project_id=project_id, budget=budget, spent=current_spend, remaining=remaining, percentage_used=percentage, days_remaining=days_remaining, projected_spend=projected, over_budget=current_spend > budget ) def send_slack_alert(self, status: ProjectBudgetStatus) -> bool: """Envoie une alerte Slack si le budget dépasse le seuil""" if status.percentage_used < ALERT_THRESHOLD * 100: return False emoji = "🚨" if status.over_budget else "⚠️" color = "#ff0000" if status.over_budget else "#ffa500" payload = { "attachments": [{ "color": color, "title": f"{emoji} Alerte Budget HolySheep - {status.project_id}", "fields": [ {"title": "Budget Alloué", "value": f"${status.budget:,.2f}", "short": True}, {"title": "Dépensé", "value": f"${status.spent:,.2f}", "short": True}, {"title": "Pourcentage", "value": f"{status.percentage_used:.1f}%", "short": True}, {"title": "Jours Restants", "value": str(status.days_remaining), "short": True}, {"title": "Projection", "value": f"${status.projected_spend:,.2f}", "short": True} ], "footer": f"Généré le {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" }] } try: response = requests.post(SLACK_WEBHOOK_URL, json=payload, timeout=5) return response.status_code == 200 except requests.exceptions.RequestException as e: logger.error(f"Erreur envoi Slack: {e}") return False def generate_monthly_report(metrics_list: List[TokenMetrics]) -> str: """Génère un rapport HTML mensuel""" html = f""" <html> <head> <title>Rapport Token HolySheep - {datetime.now().strftime('%Y-%m')}</title> <style> body {{ font-family: Arial, sans-serif; margin: 40px; }} table {{ border-collapse: collapse; width: 100%; margin: 20px 0; }} th, td {{ border: 1px solid #ddd; padding: 12px; text-align: left; }} th {{ background-color: #4a90d9; color: white; }} tr:nth-child(even) {{ background-color: #f9f9f9; }} .warning {{ background-color: #fff3cd; }} .danger {{ background-color: #f8d7da; }} .success {{ background-color: #d4edda; }} </style> </head> <body> <h1>📊 Rapport Mensuel des Tokens - HolySheep AI</h1> <p>Période : {datetime.now().strftime('%Y-%m')}</p> <table> <tr> <th>Projet</th> <th>Tokens Input</th> <th>Tokens Output</th> <th>Coût Total</th> <th>Requêtes</th> <th>Erreurs</th> <th>Latence Avg</th> </tr> """ for m in metrics_list: row_class = "danger" if m.error_count / m.request_count > 0.05 else "success" if m.error_count == 0 else "warning" html += f""" <tr class="{row_class}"> <td>{m.project_id}</td> <td>{m.input_tokens:,}</td> <td>{m.output_tokens:,}</td> <td>${m.total_cost:.2f}</td> <td>{m.request_count:,}</td> <td>{m.error_count}</td> <td>{m.avg_latency_ms:.0f}ms</td> </tr> """ html += """ </table> <p>Rapport généré automatiquement par HolySheep Metrics Collector v2.1349</p> </body> </html> """ return html def main(): """Point d'entrée principal""" logger.info("🚀 Démarrage du collecteur de métriques HolySheep") collector = HolySheepMetricsCollector(HOLYSHEEP_API_KEY, HOLYSHEEP_BASE_URL) # Récupération des tarifs pricing = collector.get_model_pricing() logger.info(f"📋 Tarifs récupérés: {len(pricing.get('models', {}))} modèles") all_metrics = [] # Collecte pour chaque projet for project_id in PROJECT_BUDGETS.keys(): logger.info(f"📊 Traitement du projet: {project_id}") usage_data = collector.get_usage_by_project(project_id, days=30) if "error" not in usage_data: metrics = collector.calculate_project_costs(usage_data, pricing) all_metrics.append(metrics) budget_status = collector.check_budget_status(project_id, metrics.total_cost) if budget_status.percentage_used >= ALERT_THRESHOLD * 100: logger.warning(f"⚠️ {project_id}: {budget_status.percentage_used:.1f}% du budget utilisé") collector.send_slack_alert(budget_status) # Génération du rapport HTML report = generate_monthly_report(all_metrics) with open(f"token_report_{datetime.now().strftime('%Y%m')}.html", "w") as f: f.write(report) logger.info(f"✅ Rapport généré: token_report_{datetime.now().strftime('%Y%m')}.html") # Export JSON pour intégration Grafana export_data = { "generated_at": datetime.now().isoformat(), "total_projects": len(all_metrics), "total_cost_usd": sum(m.total_cost for m in all_metrics), "total_tokens": sum(m.input_tokens + m.output_tokens for m in all_metrics), "projects": [asdict(m) for m in all_metrics] } with open(f"metrics_export_{datetime.now().strftime('%Y%m%d')}.json", "w") as f: json.dump(export_data, f, indent=2, default=str) logger.info("✅ Export JSON complété") if __name__ == "__main__": main()

API REST : Attribution Dynamique des Quotas Multi-Projets

Cette seconde partie implémente un système de quotas intelligents qui ajuste automatiquement les limites par projet en fonction de leur utilisation et des objectifs business. Le système surveille les tokens en temps réel et redistribue les quotas inutilisés.

#!/usr/bin/env python3
"""
HolySheep AI - Système de Quotas Dynamiques Multi-Projets
Auteur : Équipe HolySheep AI | Version : 2.1349.0513
"""

import asyncio
import hashlib
import hmac
import time
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, field
from enum import Enum
import json
import redis
import requests

Configuration HolySheep

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY" HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1" REDIS_URL = "redis://localhost:6379/0" class QuotaTier(Enum): """Niveaux de quota disponibles""" FREE = "free" STARTER = "starter" PRO = "pro" ENTERPRISE = "enterprise" @dataclass class ProjectQuota: """Configuration de quota pour un projet""" project_id: str tier: QuotaTier monthly_limit_tokens: int daily_limit_tokens: int rate_limit_rpm: int rate_limit_tpm: int allowed_models: List[str] priority: int auto_scale: bool cost_center: str @dataclass class QuotaAllocation: """Allocation actuelle d'un projet""" project_id: str allocated: int used: int remaining: int utilization_pct: float reset_at: str warnings: List[str] = field(default_factory=list) class HolySheepQuotaManager: """Gestionnaire de quotas HolySheep avec allocation dynamique""" # Configuration des quotas par défaut DEFAULT_QUOTAS = { QuotaTier.FREE: ProjectQuota( project_id="default", tier=QuotaTier.FREE, monthly_limit_tokens=100_000, daily_limit_tokens=3_333, rate_limit_rpm=20, rate_limit_tpm=40_000, allowed_models=["gpt-4.1", "claude-sonnet-4.5"], priority=0, auto_scale=False, cost_center="sandbox" ), QuotaTier.STARTER: ProjectQuota( project_id="default", tier=QuotaTier.STARTER, monthly_limit_tokens=1_000_000, daily_limit_tokens=33_333, rate_limit_rpm=100, rate_limit_tpm=200_000, allowed_models=["gpt-4.1", "claude-sonnet-4.5", "gemini-2.5-flash"], priority=5, auto_scale=True, cost_center="growth" ), QuotaTier.PRO: ProjectQuota( project_id="default", tier=QuotaTier.PRO, monthly_limit_tokens=10_000_000, daily_limit_tokens=333_333, rate_limit_rpm=500, rate_limit_tpm=1_000_000, allowed_models=["gpt-4.1", "claude-sonnet-4.5", "gemini-2.5-flash", "deepseek-v3.2"], priority=10, auto_scale=True, cost_center="production" ), QuotaTier.ENTERPRISE: ProjectQuota( project_id="default", tier=QuotaTier.ENTERPRISE, monthly_limit_tokens=100_000_000, daily_limit_tokens=3_333_333, rate_limit_rpm=2000, rate_limit_tpm=10_000_000, allowed_models=["gpt-4.1", "claude-sonnet-4.5", "gemini-2.5-flash", "deepseek-v3.2"], priority=20, auto_scale=True, cost_center="enterprise" ) } def __init__(self, api_key: str, redis_client: Optional[redis.Redis] = None): self.api_key = api_key self.redis = redis_client or redis.from_url(REDIS_URL) self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }) self.project_quotas: Dict[str, ProjectQuota] = {} def create_project(self, name: str, tier: QuotaTier = QuotaTier.FREE, custom_limits: Optional[Dict] = None) -> str: """Crée un nouveau projet avec un quota défini""" project_id = hashlib.sha256(f"{name}{time.time()}".encode()).hexdigest()[:16] base_quota = self.DEFAULT_QUOTAS[tier].__dict__.copy() if custom_limits: base_quota.update(custom_limits) quota = ProjectQuota( project_id=project_id, tier=tier, **base_quota ) quota.project_id = project_id self.project_quotas[project_id] = quota # Stockage Redis pour persistance self.redis.hset(f"quota:project:{project_id}", mapping={ "name": name, "tier": tier.value, "monthly_limit_tokens": quota.monthly_limit_tokens, "daily_limit_tokens": quota.daily_limit_tokens, "rate_limit_rpm": quota.rate_limit_rpm, "rate_limit_tpm": quota.rate_limit_tpm, "allowed_models": json.dumps(quota.allowed_models), "priority": quota.priority, "auto_scale": str(quota.auto_scale), "cost_center": quota.cost_center }) # Initialisation du compteur d'usage self.redis.set(f"usage:{project_id}:monthly", 0, ex=self._seconds_until_month_end()) self.redis.set(f"usage:{project_id}:daily", 0, ex=self._seconds_until_day_end()) return project_id def update_quota(self, project_id: str, **updates) -> bool: """Met à jour les quotas d'un projet""" if project_id not in self.project_quotas: return False quota = self.project_quotas[project_id] for key, value in updates.items(): if hasattr(quota, key): setattr(quota, key, value) # Synchronisation Redis self.redis.hset(f"quota:project:{project_id}", mapping={ k: v if not isinstance(v, list) else json.dumps(v) for k, v in updates.items() }) return True def allocate_from_pool(self, available_pool: int, weights: Dict[str, float]) -> Dict[str, int]: """Alloue dynamiquement un pool de tokens aux projets selon leurs poids""" total_weight = sum(weights.values()) allocations = {} for project_id, weight in weights.items(): if project_id not in self.project_quotas: continue quota = self.project_quotas[project_id] base_allocation = int((weight / total_weight) * available_pool) # Ajustement selon le tier et la priorité priority_multiplier = 1 + (quota.priority * 0.1) final_allocation = int(base_allocation * priority_multiplier) # Respect des limites min/max final_allocation = min(final_allocation, quota.monthly_limit_tokens) allocations[project_id] = final_allocation # Attribution effective self.update_quota(project_id, monthly_limit_tokens=final_allocation) return allocations def check_quota_available(self, project_id: str, tokens_needed: int) -> Tuple[bool, str]: """Vérifie si un projet a assez de quota disponible""" if project_id not in self.project_quotas: return False, "Projet non trouvé" quota = self.project_quotas[project_id] current_usage = int(self.redis.get(f"usage:{project_id}:monthly") or 0) daily_usage = int(self.redis.get(f"usage:{project_id}:daily") or 0) if current_usage + tokens_needed > quota.monthly_limit_tokens: return False, f"Quota mensuel dépassé: {current_usage}/{quota.monthly_limit_tokens}" if daily_usage + tokens_needed > quota.daily_limit_tokens: return False, f"Quota quotidien dépassé: {daily_usage}/{quota.daily_limit_tokens}" return True, "OK" def consume_tokens(self, project_id: str, tokens: int, model: str) -> Tuple[bool, Optional[str]]: """Consomme des tokens du quota d'un projet""" if project_id not in self.project_quotas: return False, "Projet non trouvé" quota = self.project_quotas[project_id] # Vérification du modèle autorisé if model not in quota.allowed_models: return False, f"Modèle {model} non autorisé pour ce projet" # Vérification disponibilité available, message = self.check_quota_available(project_id, tokens) if not available: return False, message # Consommation atomique pipe = self.redis.pipeline() pipe.incrby(f"usage:{project_id}:monthly", tokens) pipe.incrby(f"usage:{project_id}:daily", tokens) pipe.execute() # Logging pour audit self.redis.lpush(f"audit:{project_id}", json.dumps({ "timestamp": time.time(), "tokens": tokens, "model": model })) return True, None def get_allocation_status(self) -> List[QuotaAllocation]: """Retourne le statut d'allocation de tous les projets""" allocations = [] for project_id, quota in self.project_quotas.items(): used = int(self.redis.get(f"usage:{project_id}:monthly") or 0) remaining = max(0, quota.monthly_limit_tokens - used) utilization = (used / quota.monthly_limit_tokens * 100) if quota.monthly_limit_tokens > 0 else 0 warnings = [] if utilization >= 90: warnings.append("CRITIQUE: Quota quasi épuisé") elif utilization >= 75: warnings.append("ATTENTION: 75%+ du quota utilisé") allocations.append(QuotaAllocation( project_id=project_id, allocated=quota.monthly_limit_tokens, used=used, remaining=remaining, utilization_pct=utilization, reset_at=self._next_month_reset(), warnings=warnings )) return allocations def redistribute_quotas(self, total_pool: int) -> Dict[str, int]: """Redistribue les quotas basée sur l'utilisation réelle""" statuses = self.get_allocation_status() # Calcul des poids : inverse de l'utilisation (plus utilisé = moins prioritaire) weights = {} for status in statuses: utilization = status.utilization_pct / 100 # projets sous 50% d'utilisation ont un poids accru if utilization < 0.5: weights[status.project_id] = 1 + (0.5 - utilization) else: weights[status.project_id] = max(0.1, 1 - utilization) return self.allocate_from_pool(total_pool, weights) def export_configuration(self) -> Dict: """Exporte la configuration complète pour backup""" return { "exported_at": time.time(), "projects": { pid: { "tier": q.tier.value, "monthly_limit": q.monthly_limit_tokens, "daily_limit": q.daily_limit_tokens, "rate_limit_rpm": q.rate_limit_rpm, "allowed_models": q.allowed_models, "priority": q.priority, "auto_scale": q.auto_scale, "cost_center": q.cost_center } for pid, q in self.project_quotas.items() } } def _seconds_until_month_end(self) -> int: """Calcule les secondes jusqu'à la fin du mois""" now = time.localtime() if now.tm_mon == 12: next_month = time.struct_time((now.tm_year + 1, 1, 1) + now[3:]) else: next_month = time.struct_time((now.tm_year, now.tm_mon + 1, 1) + now[3:]) return int(time.mktime(next_month) - time.mktime(now)) def _seconds_until_day_end(self) -> int: """Calcule les secondes jusqu'à minuit""" now = time.localtime() tomorrow = time.struct_time((now.tm_year, now.tm_mon, now.tm_mday + 1, 0, 0, 0) + now[6:]) return int(time.mktime(tomorrow) - time.mktime(now)) def _next_month_reset(self) -> str: """Retourne la date du prochain reset mensuel""" now = time.localtime() if now.tm_mon == 12: return f"{now.tm_year + 1}-01-01" return f"{now.tm_year}-{now.tm_mon + 1:02d}-01" class HolySheepAPIProxy: """Proxy API avec gestion automatique des quotas""" def __init__(self, quota_manager: HolySheepQuotaManager): self.quota_manager = quota_manager self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {HOLYSHEEP_API_KEY}" }) async def chat_completion(self, project_id: str, messages: List[Dict], model: str = "gpt-4.1", **kwargs) -> Dict: """Envoie une requête chat completion avec vérification de quota""" # Estimation des tokens (approximatif) estimated_tokens = sum(len(m.get("content", "")) // 4 for m in messages) # Vérification quota available, msg = self.quota_manager.check_quota_available(project_id, estimated_tokens) if not available: return { "error": "quota_exceeded", "message": msg, "project_id": project_id } # Appel API HolySheep try: response = self.session.post( f"{HOLYSHEEP_BASE_URL}/chat/completions", json={ "model": model, "messages": messages, **kwargs }, timeout=30 ) if response.status_code == 200: data = response.json() # Consommation réelle des tokens usage = data.get("usage", {}) tokens_used = (usage.get("prompt_tokens", 0) + usage.get("completion_tokens", 0)) self.quota_manager.consume_tokens(project_id, tokens_used, model) return data else: return { "error": "api_error", "status_code": response.status_code, "message": response.text } except requests.exceptions.Timeout: return {"error": "timeout", "message": "Délai d'attente dépassé"} except requests.exceptions.RequestException as e: return {"error": "network", "message": str(e)} async def demo(): """Démonstration du système de quotas""" manager = HolySheepQuotaManager(HOLYSHEEP_API_KEY) # Création de projets projects = { "chatbot-prod": manager.create_project("Chatbot Production", QuotaTier.PRO), "analytics": manager.create_project("Analytics ML", QuotaTier.STARTER), "sandbox": manager.create_project("Sandbox Tests", QuotaTier.FREE) } print("✅ Projets créés:") for name, pid in projects.items(): print(f" {name}: {pid}") # Simulation d'utilisation print("\n📊 Allocation initiale:") for status in manager.get_allocation_status(): print(f" {status.project_id}: {status.used}/{status.allocated} tokens ({status.utilization_pct:.1f}%)") # Consommation test success, msg = manager.consume_tokens(projects["chatbot-prod"], 50000, "gpt-4.1") print(f"\n✅ Test consommation: {'OK' if success else msg}") # Export configuration config = manager.export_configuration() print(f"\n💾 Configuration exportée: {len(config['projects'])} projets") if __name__ == "__main__": asyncio.run(demo())

Dashboard Grafana : Visualisation en Temps Réel

{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": "-- Grafana --",
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "editable": true,
  "gnetId": null,
  "graphTooltip": 0,
  "id": null,
  "links": [],
  "panels": [
    {
      "datasource": "InfluxDB",
      "fieldConfig": {
        "defaults": {
          "color": {"mode": "palette-classic"},