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/tenants. PDF mensuel filtrable, export ESRS E1-6 par tenant — exactement ce qu'un commissaire aux comptes attend.

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 l'early access. Toutes les features Pro débloquées (export ESRS E1-6, bundle audit signé) — sans CB, sans engagement.