AI Framework (example)

AI frameworks like LangChain, LlamaIndex or Haystack call the underlying provider SDK (OpenAI, Anthropic, …) so the OpenTelemetry instrumentation for that provider catches every call your chain makes, transparently. You don’t need a framework-specific instrumentation.

The example below uses LangChain, but the same approach works with any framework that calls an instrumented provider.

This example also turns on exportMode: 'immediate', suitable for short-lived runtimes (Lambda, edge functions, scripts) where you don’t want to risk losing buffered spans on exit.

Install

npm install weflayr langchain @langchain/openai @traceloop/instrumentation-openai
pip install weflayr langchain langchain-openai opentelemetry-instrumentation-openai

Setup

const weflayr = require('weflayr');
const { ChatOpenAI } = require('@langchain/openai');

await weflayr.autoInstrument({
  aiSdks: 'openai',
  defaultTags: { app: 'rag-pipeline', env: 'production', version: '2.4.1' },
  exportMode: 'immediate',
});

const llm = new ChatOpenAI({
  model: 'gpt-5.1',
  apiKey: process.env.OPENAI_API_KEY,
});
import os
import weflayr
from langchain_openai import ChatOpenAI

weflayr.auto_instrument(
    ai_sdks=weflayr.AiSdk.OPENAI,
    default_tags={"app": "rag-pipeline"},
    export_mode="immediate",
)

llm = ChatOpenAI(model="gpt-5.1", api_key=os.environ["OPENAI_API_KEY"])

Use

A simple RAG-style chain: retrieve context, then ask the LLM. Decorate the feature function with propagateMetadata / propagate_metadata: every instrumented call it makes (here the OpenAI call inside .invoke(...)) is stamped with the metadata, and each invocation gets a fresh correlation id.

const { ChatPromptTemplate } = require('@langchain/core/prompts');

const prompt = ChatPromptTemplate.fromMessages([
  ['system', 'Answer using only the provided context.'],
  ['user', 'Context:\n{context}\n\nQuestion: {question}'],
]);
const chain = prompt.pipe(llm);

const answer = weflayr.propagateMetadata({
  featureName: 'rag-answer',
  customerName: 'Acme Corp',
  extraTags: { retrieval_strategy: 'hybrid', top_k: 5 },
})((question, context) => chain.invoke({ question, context }));
from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "Answer using only the provided context."),
    ("user", "Context:\n{context}\n\nQuestion: {question}"),
])
chain = prompt | llm


@weflayr.propagate_metadata(
    feature_name="rag-answer",
    customer_name="Acme Corp",
    extra_tags={"retrieval_strategy": "hybrid", "top_k": 5},
)
def answer(question: str, context: str):
    return chain.invoke({"question": question, "context": context})

The same pattern works for any LangChain construct that ultimately calls an instrumented provider: chains, agents, runnables, LCEL pipelines. If the chain makes several LLM calls (e.g. a tool-calling agent loop), every call inside the decorated function carries the same customer_name and feature_name, so the dashboard can correlate them as one logical request. The decorator fixes its metadata at decoration time; when a value varies per request (a per-call customer_name, say), use the inline form instead (weflayr.propagateMetadata(options, fn) in JS, or with weflayr.propagate_metadata(...): in Python).