Intégrations frameworks
Brancher le SDK sur un framework web ne demande plus d'ouvrir un flow à la main : un middleware (composant qui s'intercale dans le traitement des requêtes) enregistre tout le trafic HTTP en une seule ligne.
Une ligne instrumente tout le trafic HTTP
Enregistrez le middleware une fois, et chaque requête HTTP devient un flow NiaOne, sans toucher à vos routes. Le middleware capte automatiquement, en labels/métriques sur le flow :
http.method,http.route(le template/users/<id>quand le framework l'expose, sinon le path → faible cardinalité),http.status_code, etniaone.integration(l'adaptateur :express,flask…)latency_ms,error_count(+ le labelerror.type) sur erreur ou statut 5xx- la trace distribuée : reprise depuis les headers
niaone-*entrants, réinjection sur la réponse
Comme le flow de la requête est lié au contexte, current_flow_from_context(),
@instrument / instrument, trace_llm_call et submit_feedback fonctionnent
directement dans vos handlers, sans passer le flow en argument.
Safe par défaut
Sans NIAONE_API_KEY (client désactivé), le middleware est un no-op (sans effet) et ne lève
jamais : toute erreur d'instrumentation reste isolée de votre requête. Vous pouvez le
laisser branché en local et en CI.
Python — niaone_sdk.integrations.*
Flask — pip install niaone-python-sdk[flask]
from flask import Flaskfrom niaone_sdk.integrations.flask import NiaoneFlaskapp = Flask(__name__)niaone = NiaoneFlask(app) # c'est tout — zéro config (NiaoneSDK.from_env())# App-factory : NiaoneFlask().init_app(app)# Mapper une vue à un flow definition précis :@app.post("/support")@niaone.flow_definition("fd_support")def support(): ...FastAPI / Starlette — pip install niaone-python-sdk[fastapi]
from niaone_sdk.integrations.fastapi import add_niaoneadd_niaone(app) # ou app.add_middleware(NiaoneASGIMiddleware, client=...)Django — ajoutez le middleware à settings.MIDDLEWARE
MIDDLEWARE = [ "niaone_sdk.integrations.django.NiaoneMiddleware", # ... vos autres middlewares ...]# config optionnelle via settings : NIAONE_CLIENT, NIAONE_FLOW_DEFINITION_ID,# NIAONE_FLOW_DEFINITION_RESOLVER, NIAONE_EXCLUDE_PATHS, NIAONE_SESSION_ID /# NIAONE_ACTOR_ID (callables), NIAONE_PROPAGATE_CARRIER, NIAONE_INJECT_CARRIERASGI générique (Quart, ou toute application ASGI)
from niaone_sdk.integrations.asgi import NiaoneASGIMiddlewareapp = NiaoneASGIMiddleware(app)WSGI générique (Bottle, Pyramid, ou toute application WSGI)
from niaone_sdk.integrations.wsgi import NiaoneWSGIMiddlewareapp = NiaoneWSGIMiddleware(app)TypeScript — sous-imports @niaone/sdk/<framework>
Chaque framework est une peer-dependency optionnelle (seuls ses types sont utilisés, donc aucune dépendance runtime ajoutée).
Express — @niaone/sdk/express
import express from 'express';import { niaoneExpress, niaoneExpressError } from '@niaone/sdk/express';const app = express();app.use(niaoneExpress()); // zéro config (NiaoneSDK.fromEnv())// ... vos routes ...app.use(niaoneExpressError()); // optionnel, à enregistrer EN DERNIERFastify — @niaone/sdk/fastify
import { niaoneFastify } from '@niaone/sdk/fastify';await app.register(niaoneFastify, { client });Koa — @niaone/sdk/koa
import { niaoneKoa } from '@niaone/sdk/koa';app.use(niaoneKoa({ client }));Next.js (route handlers App Router) — @niaone/sdk/next
import { withNiaone } from '@niaone/sdk/next';export const GET = withNiaone(async (req) => Response.json({ ok: true }));node:http / connect — @niaone/sdk/node-http
import { niaoneNodeHttp } from '@niaone/sdk/node-http';const trace = niaoneNodeHttp({ client }); // style connect (req, res, next)Accéder au flow de la requête
Le middleware en place, le flow de chaque requête vit dans le contexte. Récupérez-le sans le passer en argument :
from niaone_sdk import current_flow_from_context, submit_feedback@app.post("/chat")def chat(): flow = current_flow_from_context() # le flow de cette requête resp = openai.chat.completions.create(...) submit_feedback(flow, True) return {"trace_id": flow.trace_id}Options
Tous les adaptateurs partagent les mêmes réglages (camelCase en TS, snake_case en Python).
| Option | Rôle | Défaut |
|---|---|---|
| client | Client SDK à utiliser | NiaoneSDK.fromEnv() |
| flowDefinitionId | ID de flow definition fixe | "http.request" |
| flowDefinitionResolver | Résout l’ID par requête : (req) => string | — |
| sessionId | Extrait l’ID de session : (req) => string | — |
| actorId | Extrait l’ID utilisateur : (req) => string | — |
| excludePaths | Chemins ignorés (exact ou préfixe en *) — absent en Next.js | [] |
| propagateCarrier | Reprend la trace depuis les headers niaone-* entrants | true |
| injectCarrier | Réinjecte les headers niaone-* sur la réponse | true |
NiaoneFlask( app, client=my_client, # défaut : NiaoneSDK.from_env() flow_definition_id="fd_support", # ou flow_definition_resolver=lambda req: ... session_id=lambda req: req.headers.get("X-Session-Id"), actor_id=lambda req: req.headers.get("X-User-Id"), exclude_paths=["/health"], # aussi exclude_endpoints / exclude_blueprints)Template de route & cardinalité
Les middlewares protocolaires (node:http, ASGI, WSGI) tournent avant le routing :
ils ne connaissent pas encore le template, d'où le défaut flow_definition_id = "http.request"
et http.route = le path concret. Express, Fastify, Koa (avec router), Flask et Django
exposent le template de route (faible cardinalité). En Flask, le flow_definition_id
retombe par défaut sur le nom d'endpoint de la vue (default_to_endpoint). Passez
flowDefinitionId / flow_definition_id ou un resolver pour une valeur métier. Les route
handlers Next.js sont runtime Node uniquement (ils dépendent d'AsyncLocalStorage).
Référence complète
Tables d'options exhaustives, tracing distribué inter-services, recettes et troubleshooting
par adaptateur : voir sdk/docs/framework-integrations.md.
Cas avancés & sans middleware
Ouvrir un flow manuellement (stack non couverte)
Si aucun middleware ne couvre votre stack, ouvrez le flow dès l'entrée du handler, avant tout traitement, pour que vos spans métier ne soient pas orphelins derrière le span « http request » de l'auto-instrumentation OTel (cf. Instrumenter).
# FastAPI@app.post("/predict")async def predict(req: PredictRequest): with flow(client, flow_definition_id="prediction_api", name="prediction_main", actor_id=req.user_id) as f: return await run_prediction(f, req)Agents — CrewAI
Instrumentez sans toucher à votre logique : branchez les callbacks de CrewAI et mappez chaque agent
à un Asset (rôle + span_kind). Un exemple complet est fourni dans le SDK
(sdk/python/demo/crewai_integration/).
Python
CrewAI est un framework Python : l'exemple ci-dessous est en Python uniquement.
from niaone_sdk import flow, stepfrom niaone_sdk.models import Asset, Flowfrom niaone_sdk.enums import AssetKind, AssetRole, SpanKindROLE_MAP = {"researcher": AssetRole.RETRIEVAL, "analyst": AssetRole.ANALYSIS, "writer": AssetRole.POSTPROCESS}def run_crew(crew, user_input: str): with flow(client, flow_definition_id="research_crew", name="research_crew_main") as f: for task in crew.tasks: asset = Asset(kind=AssetKind.LLM, role=ROLE_MAP[task.agent.role], name=task.agent.role) with step(f, asset=asset, flow=Flow(name="research_crew_main", step=task.agent.role), span_kind=SpanKind.AGENT) as s: result = task.execute() payload(s, "output", out_data, str(result)) return crew.kickoff(inputs={"input": user_input})LangChain — usage des tokens
normalize_llm_usage comprend les formats LangChain (Python et JS), donc vous récupérez les tokens
quel que soit le vendor sous-jacent.
from niaone_sdk import normalize_llm_usageresult = llm.generate([messages])s.add_metrics(normalize_llm_usage(result.llm_output.get("token_usage", {})))Migration depuis OpenTelemetry brut
Sans le SDK, émettez vous-même les attributs attendus : record_type = "run" et un raw_payload
JSON portant payload.flow.* et payload.asset.*.
with tracer.start_as_current_span("answer") as span: span.set_attribute("payload.flow.name", "my_sia_main") span.set_attribute("payload.flow.phase", "ANALYSIS") span.set_attribute("payload.asset.kind", "llm") span.set_attribute("payload.asset.name", "gpt-4o")