Dataiku + carbon-llm : tracker chaque LLM call dans vos recipes Python

Guide pas à pas pour mesurer le CO₂e de chaque appel LLM dans vos flows Dataiku DSS. Python recipe carbon-llm-track, hook sur les LLM Mesh recipes, multi-projet + multi-tenant. Prêt CSRD ESRS E1-6.

Pourquoi tracker les LLM calls dans Dataiku ?

Dataiku est l'une des plateformes les plus utilisées dans les grandes équipes data en France (BNP, AXA, Total, Pernod Ricard, Decathlon...). Chaque flow qui appelle un LLM, via le LLM Mesh natif depuis 2024 ou via un client Python custom, produit des inférences qui doivent apparaître dans le poste 'émissions IA' du Scope 3 CSRD.

L'avantage de Dataiku pour le tracking : tout passe par un nombre limité de recipes (versus du code dispersé dans 50 microservices). Une intégration carbon-llm en un seul Python recipe utilitaire couvre l'intégralité de vos flows.

Bonus : le system de variables Dataiku permet de tagger automatiquement les events par projet, par environnement (DEV / PROD) et par utilisateur, ce qui donne un dashboard naturellement multi-tenant côté carbon-llm.

Stocker la clé API en variable Dataiku

Deux niveaux possibles selon votre besoin :

1. Variable d'INSTANCE (Administration → Variables), accessible à tous les projets de l'instance Dataiku. Idéal si vous gérez carbon-llm de manière centrale (DSI/RSE).

2. Variable de PROJECT (Project Settings → Variables), scope projet uniquement. Idéal si chaque équipe data gère sa propre clé.

Dans les deux cas, la valeur est chiffrée au repos et n'apparaît pas dans les logs DSS.

Variables, onglet Settings

# Variable name        Variable value
CARBON_LLM_API_KEY     live_xxxxxxxxxxxxxxxxxxxx
CARBON_LLM_BASE_URL    https://carbon-llm.com
CARBON_LLM_DEFAULT_TENANT_ID    ${dataiku.project_key}
Python recipe utilitaire carbon-llm-track

Créez un Python recipe (n'importe quel input/output, peu importe, c'est juste une bibliothèque utilitaire) qui définit une fonction réutilisable depuis tous vos autres recipes via `from <recipe_name> import track_llm_call` n'est pas possible directement, mais Dataiku permet la création de Python libraries projet (Project libraries) qui exposent du code partageable.

Pattern recommandé : créer un module Python dans la project library, pas un recipe.

Project library → carbon_llm.py

"""
carbon-llm helper, appelable depuis n'importe quel Python recipe ou notebook DSS.
Stockez ce fichier dans le project library : Settings → Code → Libraries → Edit lib.

Usage :
    from carbon_llm import track_llm_call
    track_llm_call(model="gpt-4o", prompt_tokens=1240, completion_tokens=580)
"""

import dataiku
import requests
from typing import Optional

# Lire les variables Dataiku, niveau project en priorité, puis instance
_client = dataiku.api_client()
_proj = dataiku.Project()
_vars = {**_client.get_general_settings().get_raw().get("studioGlobalSettings", {}).get("variables", {}),
         **_proj.get_variables().get("standard", {})}

API_KEY = _vars.get("CARBON_LLM_API_KEY")
BASE_URL = _vars.get("CARBON_LLM_BASE_URL", "https://carbon-llm.com")
DEFAULT_TENANT = _vars.get("CARBON_LLM_DEFAULT_TENANT_ID", _proj.project_key)


def track_llm_call(
    model: str,
    prompt_tokens: int,
    completion_tokens: int,
    tenant_id: Optional[str] = None,
    ingestion_source: str = "dataiku",
) -> None:
    """Fire-and-forget POST vers carbon-llm. Ne lève jamais d'exception,
    les events ratés sont loggés stderr uniquement."""
    if not API_KEY:
        # Pas configuré → no-op silencieux (utile en dev local sans clé)
        return

    try:
        requests.post(
            f"{BASE_URL}/api/v1/track",
            headers={
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": "application/json",
            },
            json={
                "model": model,
                "prompt_tokens": prompt_tokens,
                "completion_tokens": completion_tokens,
                "tenant_id": tenant_id or DEFAULT_TENANT,
                "ingestion_source": ingestion_source,
            },
            timeout=5.0,
        )
    except Exception as e:
        # Best-effort. Pas de raise, un échec carbon-llm ne casse pas votre flow data.
        print(f"[carbon-llm] track failed: {e}")
Cas 1 : Dataiku LLM Mesh (depuis DSS 12.5)

Le LLM Mesh est l'abstraction native Dataiku qui unifie l'accès à OpenAI, Anthropic, Bedrock, Vertex, Azure OpenAI, Mistral, etc. Chaque appel retourne un objet `LLMResponse` avec un attribut `usage` standard.

Wrapper minimal :

Recipe Python utilisant le LLM Mesh

import dataiku
from carbon_llm import track_llm_call

# Récupérer une connexion LLM Mesh configurée dans Administration
client = dataiku.api_client()
project = dataiku.Project()
llm = project.get_llm("openai:my-openai-connection:gpt-4o")

# Appel standard LLM Mesh
response = llm.new_completion()\
    .with_message("Résume ce texte en 3 lignes : ...")\
    .execute()

# Track, l'objet usage est exposé par LLMResponse
track_llm_call(
    model="gpt-4o",
    prompt_tokens=response.usage.prompt_tokens,
    completion_tokens=response.usage.completion_tokens,
    tenant_id=f"{project.project_key}:{dataiku.api_client().get_auth_info()['authIdentifier']}",
)

print(response.text)
Cas 2 : Client Python custom (sans LLM Mesh)

Si votre équipe utilise directement openai, anthropic ou un autre client Python (cas fréquent dans les notebooks exploratoires), le pattern est identique :

Notebook DSS, client OpenAI direct

import os
from openai import OpenAI
from carbon_llm import track_llm_call
import dataiku

# Récupérer la clé OpenAI depuis les variables Dataiku
client = OpenAI(api_key=dataiku.get_custom_variables()["OPENAI_API_KEY"])

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Résume ce CR de réunion : ..."}],
)

# Track immédiatement après la réponse
track_llm_call(
    model="gpt-4o",
    prompt_tokens=response.usage.prompt_tokens,
    completion_tokens=response.usage.completion_tokens,
    tenant_id=dataiku.Project().project_key,
)

print(response.choices[0].message.content)
Cas 3 : Scenarios Dataiku (batch / orchestré)

Les Dataiku Scenarios qui appellent des LLM en batch (résumé en masse, classification, RAG indexing) doivent aussi être tracés.

Pattern : ajoutez un step Python step en fin de scenario qui agrège les usages collectés pendant le run.

Scenario step Python

import dataiku
from carbon_llm import track_llm_call

# Si vos recipes ont logué les usages dans une dataset commune (recommandé pour batch) :
client = dataiku.api_client()
project = dataiku.Project()
usage_ds = dataiku.Dataset("llm_usage_log")  # une dataset DSS qui collecte les rows

df = usage_ds.get_dataframe()
for _, row in df.iterrows():
    track_llm_call(
        model=row["model"],
        prompt_tokens=int(row["prompt_tokens"]),
        completion_tokens=int(row["completion_tokens"]),
        tenant_id=f"{project.project_key}:scenario:{row['scenario_run_id']}",
        ingestion_source="dataiku_scenario",
    )

# Vide la dataset après tracking, évite les double-compte au prochain scenario
usage_ds.write_with_schema(df.iloc[0:0])
Multi-tenant : un dashboard par projet ou par client final

tenant_id est libre, adaptez-le à votre besoin :

• 1 dashboard global par instance Dataiku → tenant_id = nom du projet (ex: 'risque_credit')

• 1 dashboard par utilisateur final (cabinet de conseil) → tenant_id = 'projet:client' (ex: 'risque_credit:bnp_2026')

• 1 dashboard par environnement → tenant_id = 'projet:env' (ex: 'risque_credit:prod')

Côté carbon-llm, chaque tenant_id distinct apparaît dans /dashboard, avec le CO₂ agrégé et un export CSV de l'usage filtrable par période.

Erreurs courantes

1. **Variables non lues côté recipe**, Dataiku Project libraries n'ont pas accès à `dataiku.get_custom_variables()` directement. Utilisez `dataiku.api_client()` + `Project().get_variables()` comme dans le helper ci-dessus.

2. **LLM Mesh sans usage**, certains backends (Bedrock historiquement) ne retournent pas l'objet usage. Vérifiez avec `print(response.usage)` avant track. Solution de secours : passer par l'admin API du provider et utiliser le connecteur Pro carbon-llm.

3. **Usage qui double**, si vous tracez à la fois dans le recipe ET via un connecteur OpenAI Admin Pro, vous comptez deux fois. Choisissez une seule source par environnement.

4. **tenant_id trop long**, limite 64 chars alphanum + tirets. Hashez si vous concaténez plusieurs niveaux.

Prêt à tracer vos flows Dataiku ?

Gratuit pendant la phase d'accès anticipé. Toutes les fonctionnalités débloquées (mesure CO₂, export CSV, déploiement MDM), sans engagement.