Grafana + LibreChat – Mesurer l’usage IA en entreprise

Vous avez déployé LibreChat en interne pour donner à vos équipes un accès unifié aux LLMs. Excellent choix. Mais savez-vous combien de tokens vos collaborateurs consomment chaque jour ? Quels modèles sont les plus utilisés ? Quels départements génèrent le plus de coûts IA ? Sans monitoring, vous pilotez à l’aveugle.

Dans cet article, nous vous montrons comment déployer un tableau de bord Grafana complet pour mesurer l’usage IA en entreprise via LibreChat – avec toutes les métriques qui comptent pour piloter votre stratégie IA.

Pourquoi monitorer votre usage IA ?

L’observabilité de vos outils IA n’est pas un luxe technique. C’est une nécessité opérationnelle et financière pour plusieurs raisons :

  • Maîtrise des coûts : les tokens coûtent de l’argent. Sans monitoring, les surprises sur la facture API arrivent vite.
  • Optimisation des modèles : peut-être que 80% des usages pourraient être couverts par un modèle 10x moins cher.
  • Adoption et ROI : mesurer qui utilise vraiment l’outil et comment justifie l’investissement auprès du management.
  • Sécurité et conformité : détecter les usages anormaux ou non conformes à vos politiques.

Architecture de la stack de monitoring

Notre stack repose sur trois composants open source :

  • LibreChat : la plateforme de chat IA unifiée, qui expose des métriques via son API interne
  • Prometheus : collecteur et base de données de séries temporelles pour les métriques
  • Grafana : visualisation des métriques avec dashboards configurables

L’architecture est simple : LibreChat expose ses métriques dans un format compatible Prometheus, Prometheus les scrape à intervalles réguliers, et Grafana interroge Prometheus pour afficher les dashboards.

Configuration de LibreChat pour l’observabilité

Activation des métriques Prometheus

Dans votre fichier librechat.yaml, ajoutez la configuration suivante pour activer l’export de métriques :

monitoring:
  enabled: true
  prometheus:
    enabled: true
    port: 9090
    path: /metrics

LibreChat exposera alors des métriques sur le port 9090 au format Prometheus.

Métriques disponibles par défaut

LibreChat expose nativement les métriques suivantes :

  • librechat_tokens_total : total des tokens consommés (prompt + completion)
  • librechat_tokens_by_model : tokens par modèle LLM utilisé
  • librechat_tokens_by_user : tokens par utilisateur
  • librechat_conversations_total : nombre total de conversations
  • librechat_active_users : utilisateurs actifs (daily/weekly/monthly)
  • librechat_api_latency : latence des appels API aux différents LLMs
  • librechat_errors_total : erreurs par type et par modèle

Configuration de Prometheus

Ajoutez la cible LibreChat dans votre prometheus.yml :

scrape_configs:
  - job_name: 'librechat'
    static_configs:
      - targets: ['librechat:9090']
    scrape_interval: 30s
    metrics_path: /metrics

Avec Docker Compose, voici la configuration complète de votre stack :

version: '3.8'
services:
  librechat:
    image: ghcr.io/danny-avila/librechat:latest
    ports:
      - "3080:3080"
      - "9090:9090"
  
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9091:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=votremotdepasse

Création du dashboard Grafana

Connexion à Prometheus comme datasource

Dans Grafana (http://localhost:3000), allez dans Configuration → Data Sources → Add data source, sélectionnez Prometheus et entrez l’URL : http://prometheus:9090.

Les 6 panels essentiels

Panel 1 : Tokens consommés par jour

sum(increase(librechat_tokens_total[24h]))

Ce panel vous donne une vue quotidienne de la consommation totale. Ajoutez une alerte Grafana si le total dépasse votre seuil budgétaire journalier.

Panel 2 : Coût estimé par modèle

sum by (model) (increase(librechat_tokens_total[24h])) * on(model) group_left() librechat_token_cost_per_1k

Ce panel nécessite de configurer les coûts par modèle dans LibreChat. Il vous montre en temps réel le coût engendré par chaque modèle.

Panel 3 : Top 10 utilisateurs par consommation

topk(10, sum by (user) (increase(librechat_tokens_by_user[7d])))

Identifiez vos «super utilisateurs» et comprenez leurs patterns d’usage pour former les autres équipes.

Panel 4 : Utilisateurs actifs (DAU/WAU/MAU)

librechat_active_users{period="daily"}

Le taux d’adoption est votre principal KPI pour mesurer le ROI de votre déploiement LibreChat.

Panel 5 : Distribution des modèles utilisés

sum by (model) (increase(librechat_tokens_by_model[7d]))

Un pie chart qui révèle si vos équipes utilisent des modèles trop chers pour des tâches simples. Très utile pour guider les recommandations de modèle par cas d’usage.

Panel 6 : Latence P95 par modèle

histogram_quantile(0.95, sum by (le, model) (rate(librechat_api_latency_bucket[5m])))

Détectez les modèles qui ralentissent l’expérience utilisateur et anticipez les problèmes de performance.

Métriques avancées : coûts par département

Si vous avez configuré LibreChat avec des groupes d’utilisateurs par département, vous pouvez créer des métriques de coût par équipe :

sum by (department) (increase(librechat_tokens_by_user[30d])) * on(user) group_left(department) librechat_user_info

Cette vue permet aux managers de voir la consommation IA de leur équipe et de justifier ou ajuster les budgets alloués.

Alertes à configurer

Voici les 4 alertes Grafana que nous recommandons à chaque déploiement LibreChat :

  • Dépassement budgétaire : coût journalier > seuil défini (ex: 50€/jour)
  • Pic d’utilisation anormal : consommation horaire > 3x la moyenne sur 7 jours
  • Taux d’erreur élevé : erreurs API > 5% des requêtes sur 15 minutes
  • Latence dégradée : P95 > 30 secondes sur un modèle

Intégration avec Slack pour les alertes

Configurez un webhook Slack dans Grafana pour recevoir les alertes directement dans votre channel #ia-ops :

alerting:
  contact_points:
    - name: Slack IA Ops
      slack:
        url: https://hooks.slack.com/services/VOTRE_WEBHOOK
        channel: "#ia-ops"
        message: "Alerte LibreChat : {{ .GroupLabels.alertname }} - {{ .CommonAnnotations.summary }}"

Conclusion

Monitorer votre usage LibreChat avec Grafana est un investissement de quelques heures qui vous fait économiser des mois d’approximations budgétaires. En quelques dashboards bien configurés, vous passez d’un déploiement IA «à l’aveugle» à un pilotage data-driven avec des KPIs clairs : adoption, coûts, performance et ROI.

Besoin d’aide pour déployer LibreChat avec monitoring Grafana dans votre infrastructure ? L’équipe Digitalizor vous accompagne de l’installation à la configuration des dashboards métier.

🏗️ Architecture : LibreChat → Prometheus → Grafana

Pipeline complet de monitoring IA en entreprise

🤖

LibreChat

Interface IA
Utilisateurs
:3080

métriques
/metrics

📊

Prometheus

Collecte &
stockage TSDB
:9090

PromQL
datasource

📈

Grafana

Dashboards
& Alertes
:3000

alerting
PagerDuty/Slack

🔔

Alertes

Slack / Email
PagerDuty

Node Exporter
CPU / RAM / Disk
:9100
Alertmanager
Routage alertes
:9093
Optionnel mais
recommandé

📋 Les 10 métriques clés à monitorer sur votre stack IA

# Métrique Description Panel Grafana Seuil d’alerte
1 librechat_requests_total Nombre total de requêtes IA envoyées Time series / Counter > 10K/h
2 librechat_tokens_used_total Tokens consommés (input + output) Gauge / Stat panel > 80% budget
3 librechat_active_users Utilisateurs actifs en temps réel Stat / Big number > 500 simultanés
4 librechat_response_latency_p95 Latence au 95e percentile (ms) Heatmap / Histogram > 8 000ms
5 librechat_errors_rate Taux d’erreurs API (timeouts, 4xx, 5xx) Time series / Alert > 2%
6 librechat_cost_usd_total Coût cumulé en $ (calculé via tokens) Stat / Budget tracker > 90% budget
7 librechat_model_usage_ratio Répartition des modèles utilisés (%) Pie chart Informatif
8 librechat_conversations_per_user Conversations moyennes par utilisateur/jour Bar chart Informatif
9 process_cpu_usage CPU utilisé par LibreChat (Node.js) Gauge / Time series > 80%
10 nodejs_heap_used_bytes Mémoire heap Node.js consommée Gauge / Alert > 1.5GB

⚙️ Configuration Prometheus : prometheus.yml pour scraper LibreChat

# prometheus.yml - Configuration pour monitorer LibreChat
global:
  scrape_interval: 15s       # Collecte toutes les 15 secondes
  evaluation_interval: 15s   # Evaluation des regles toutes les 15s
  external_labels:
    monitor: 'librechat-prod'
    environment: 'production'

# Regles d'alerte
rule_files:
  - "alerts/librechat_alerts.yml"

# Alertmanager
alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

# Scrape configs
scrape_configs:
  # LibreChat application metrics
  - job_name: 'librechat'
    static_configs:
      - targets: ['librechat:3080']
    metrics_path: '/metrics'
    scrape_interval: 30s
    scrape_timeout: 10s
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        replacement: 'librechat-prod'

  # Node Exporter - metriques systeme (CPU, RAM, Disk)
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance

  # MongoDB metrics (si utilise par LibreChat)
  - job_name: 'mongodb'
    static_configs:
      - targets: ['mongodb-exporter:9216']

  # Prometheus lui-meme
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

# Exemple de fichier alerts/librechat_alerts.yml :
# groups:
#   - name: librechat
#     rules:
#       - alert: HighErrorRate
#         expr: rate(librechat_errors_total[5m]) > 0.02
#         for: 2m
#         labels:
#           severity: critical
#         annotations:
#           summary: "Taux d'erreur LibreChat eleve"

📊 Dashboard Grafana : JSON importable pour LibreChat

{
  "title": "LibreChat - Monitoring IA",
  "uid": "librechat-monitoring-v1",
  "tags": ["librechat", "ia", "monitoring"],
  "timezone": "Europe/Paris",
  "schemaVersion": 38,
  "refresh": "30s",
  "time": { "from": "now-24h", "to": "now" },
  "panels": [
    {
      "id": 1,
      "title": "Requetes totales (24h)",
      "type": "stat",
      "gridPos": { "x": 0, "y": 0, "w": 4, "h": 4 },
      "targets": [{
        "expr": "increase(librechat_requests_total[24h])",
        "legendFormat": "Requetes"
      }],
      "options": { "colorMode": "background", "graphMode": "area" },
      "fieldConfig": { "defaults": { "color": { "mode": "thresholds" },
        "thresholds": { "steps": [
          {"color": "green", "value": null},
          {"color": "orange", "value": 5000},
          {"color": "red", "value": 10000}
        ]}
      }}
    },
    {
      "id": 2,
      "title": "Tokens consommes (24h)",
      "type": "stat",
      "gridPos": { "x": 4, "y": 0, "w": 4, "h": 4 },
      "targets": [{
        "expr": "increase(librechat_tokens_used_total[24h])",
        "legendFormat": "Tokens"
      }]
    },
    {
      "id": 3,
      "title": "Cout estimé ($)",
      "type": "stat",
      "gridPos": { "x": 8, "y": 0, "w": 4, "h": 4 },
      "targets": [{
        "expr": "increase(librechat_cost_usd_total[24h])",
        "legendFormat": "USD"
      }],
      "fieldConfig": { "defaults": { "unit": "currencyUSD" } }
    },
    {
      "id": 4,
      "title": "Latence p95 (ms)",
      "type": "timeseries",
      "gridPos": { "x": 0, "y": 4, "w": 12, "h": 6 },
      "targets": [{
        "expr": "histogram_quantile(0.95, rate(librechat_response_latency_bucket[5m])) * 1000",
        "legendFormat": "p95 latency"
      }],
      "fieldConfig": { "defaults": { "unit": "ms",
        "thresholds": { "steps": [
          {"color": "green", "value": null},
          {"color": "orange", "value": 5000},
          {"color": "red", "value": 8000}
        ]}
      }}
    }
  ]
}

🐍 Script Python : Export des métriques mensuelles vers CSV

"""
Export mensuel des metriques LibreChat depuis Prometheus vers CSV
Usage: python export_metrics.py --month 2026-05
"""
import requests
import pandas as pd
from datetime import datetime, timedelta
import argparse
import os

PROMETHEUS_URL = os.getenv("PROMETHEUS_URL", "http://localhost:9090")

def query_prometheus(query: str, start: datetime, end: datetime, step: str = "1h") -> list:
    """Interroge Prometheus avec une requete PromQL sur une plage temporelle."""
    url = f"{PROMETHEUS_URL}/api/v1/query_range"
    params = {
        "query": query,
        "start": start.isoformat() + "Z",
        "end":   end.isoformat() + "Z",
        "step":  step
    }
    resp = requests.get(url, params=params, timeout=30)
    resp.raise_for_status()
    return resp.json()["data"]["result"]

def export_monthly_metrics(year: int, month: int) -> pd.DataFrame:
    """Exporte les metriques cles pour un mois donne."""
    start = datetime(year, month, 1)
    if month == 12:
        end = datetime(year + 1, 1, 1) - timedelta(seconds=1)
    else:
        end = datetime(year, month + 1, 1) - timedelta(seconds=1)

    print(f"Export metriques : {start.strftime('%Y-%m')} ...")

    metrics_queries = {
        "requests_total":    "increase(librechat_requests_total[1h])",
        "tokens_used":       "increase(librechat_tokens_used_total[1h])",
        "cost_usd":          "increase(librechat_cost_usd_total[1h])",
        "active_users":      "librechat_active_users",
        "latency_p95_ms":    "histogram_quantile(0.95, librechat_response_latency_bucket) * 1000",
        "error_rate_pct":    "rate(librechat_errors_total[1h]) * 100",
    }

    dfs = []
    for metric_name, query in metrics_queries.items():
        results = query_prometheus(query, start, end)
        for series in results:
            for timestamp, value in series["values"]:
                dfs.append({
                    "timestamp": datetime.utcfromtimestamp(float(timestamp)),
                    "metric": metric_name,
                    "value": float(value) if value != "NaN" else None,
                    "labels": str(series.get("metric", {}))
                })

    df = pd.DataFrame(dfs)
    if df.empty:
        print("Aucune donnee trouvee.")
        return df

    # Pivot pour avoir une colonne par metrique
    df_pivot = df.pivot_table(index="timestamp", columns="metric", values="value", aggfunc="sum")
    df_pivot.reset_index(inplace=True)

    # Totaux mensuels
    print("\nTotaux mensuels :")
    for col in ["requests_total", "tokens_used", "cost_usd"]:
        if col in df_pivot.columns:
            print(f"  {col}: {df_pivot[col].sum():,.0f}")

    return df_pivot

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--month", default=datetime.now().strftime("%Y-%m"), help="Format: YYYY-MM")
    args = parser.parse_args()

    year, month = map(int, args.month.split("-"))
    df = export_monthly_metrics(year, month)

    if not df.empty:
        filename = f"librechat_metrics_{args.month}.csv"
        df.to_csv(filename, index=False)
        print(f"\nExporte dans : {filename}")