Switch from Langfuse V1 to Confident AI
Move your traces from Langfuse SDK v1 to Confident AI with confident-trace. Auto instrumentation replaces your trace and generation objects.
Overview
Langfuse SDK v1 sends traces through its own ingestion API, not OpenTelemetry. You build each trace by hand: a Langfuse client, a trace, then a span or a generation for every step. confident-trace replaces those objects with OpenTelemetry spans. init() instruments your provider clients, span marks your own steps, and the trace fields move onto update_trace() in Python and updateTrace() in TypeScript.
In this guide, you will:
- Pick a migration method: one copyable prompt for your coding agent, or manual.
- Install confident-trace and replace the Langfuse keys with one
CONFIDENT_API_KEY. - Replace the traced OpenAI calls with
init()so auto instrumentation emits the model spans. - Replace
langfuse.trace()withspanandupdate_trace()so every trace keeps its name, user, session and metadata. - Drop the manual generations that auto instrumentation now records for you, and keep an LLM span for the model calls it does not cover.
- See what changes once the traces land in Confident AI: evals on every trace, datasets curated from production, alerts when quality drops.
Prerequisites
- A Project API Key,
CONFIDENT_API_KEY(for exampleconfident_us_proj_...). Retrieve yours here. - Python 3.10+ for the Python SDK, or Node.js 22+ for the TypeScript SDK.
- Your existing Langfuse setup on SDK v1: Python
langfuse<2(runpip show langfuse) or TypeScriptlangfuse@1(runnpm ls langfuse). The before snippets below follow the Langfuse v1 SDKs, last released as Python 1.14.0 and TypeScript 1.3.0.
Choose a Migration Method
| Method | What you do | Langfuse SDK v1 |
|---|---|---|
| Coding-agent migration | Give your coding assistant one prompt and review its changes | Supported. See Switch in One Prompt below |
| Manual migration | Make the SDK changes yourself, step by step | Supported. See Switch Manually below |
| OpenTelemetry migration | Add a Confident AI exporter and keep your Langfuse instrumentation | Not available. Langfuse SDK v1 sends traces through the Langfuse ingestion API, not OpenTelemetry, so there is no span to redirect. It is available from Langfuse Python SDK v3 and TypeScript SDK v4 |
Switch in One Prompt
Give the prompt below to your coding assistant, such as Claude Code, Cursor or Codex, from the root of your repository. It finds your Langfuse integration, confirms the SDK version, makes the changes from this guide, and tells you what still needs your hands. Review the diff before you merge it.
Migrate this codebase from Langfuse to Confident AI using confident-trace.
1. Find every Langfuse integration: imports of langfuse, Langfuse() and new Langfuse() clients, langfuse.trace(), trace.span(), trace.generation(), generation.end(), langfuse.openai, the Langfuse CallbackHandler, and LANGFUSE_* environment variables or keys passed in code.
2. Confirm the installed Langfuse SDK version with pip show langfuse or npm ls langfuse. This migration is for Langfuse SDK v1. If the major version is different, stop and tell me which version you found.
3. Make the changes from https://www.confident-ai.com/docs/guides/migrate-to-langfuse-alternative-v1:
- install confident-trace and call init() once at startup
- replace langfuse.openai, and generations around OpenAI calls, with the plain OpenAI client
- replace langfuse.trace() with span on the traced function and update_trace() (updateTrace() in TypeScript) for name, user, session and metadata, with sessionId as thread_id (threadId)
- replace trace.span() with span, or with span(...) / withSpan()
- keep generations for model calls auto instrumentation does not cover as LLM spans with update_span() (updateSpan())
- remove the Langfuse CallbackHandler from LangChain callbacks
- replace the Langfuse keys with CONFIDENT_API_KEY, and langfuse.flush() / shutdownAsync() with flush() / shutdown()
4. Preserve existing tracing behavior where possible: the same span names, nesting, inputs, outputs, users, sessions and metadata.
5. Do not delete Langfuse prompt, dataset or score calls. List them for me instead.
6. When you are done, summarize every change and list anything that needs manual configuration, such as setting CONFIDENT_API_KEY, the EU endpoint, or model calls you could not map.Switch Manually
Install confident-trace
One package. Provider SDKs stay optional peers.
CONFIDENT_API_KEYreplaces the Langfuse public key, secret key and host. For the EU region, also setCONFIDENT_OTEL_ENDPOINT=https://eu.otel.confident-ai.com/v1/traces.pip uninstall langfuse pip install confident-trace export CONFIDENT_API_KEY=... # replaces LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_HOSTnpm uninstall langfuse npm install confident-trace export CONFIDENT_API_KEY=... # replaces the publicKey, secretKey and baseUrl you passed to new Langfuse() node --import confident-trace/register dist/index.jsReplace the traced OpenAI calls with init()
In Python, drop the Langfuse import of OpenAI. In TypeScript, Langfuse v1 has no OpenAI integration, so you wrapped each call in a generation. Either way, auto instrumentation now emits the span from the plain client, with the model, messages and token usage.
from langfuse.openai import openai response = openai.chat.completions.create( model="gpt-4.1-mini", messages=[{"role": "user", "content": "Hello"}], )from confident_trace import init from openai import OpenAI init() client = OpenAI() response = client.chat.completions.create( model="gpt-4.1-mini", messages=[{"role": "user", "content": "Hello"}], )import { Langfuse } from "langfuse"; import OpenAI from "openai"; const langfuse = new Langfuse({ publicKey: "pk-lf-...", secretKey: "sk-lf-..." }); const openai = new OpenAI(); const messages = [{ role: "user" as const, content: "Hello" }]; const generation = langfuse.generation({ name: "hello", model: "gpt-4.1-mini", prompt: messages, }); const response = await openai.chat.completions.create({ model: "gpt-4.1-mini", messages, }); generation.end({ completion: response.choices[0].message, usage: { promptTokens: response.usage?.prompt_tokens, completionTokens: response.usage?.completion_tokens, }, });import { init } from "confident-trace"; import OpenAI from "openai"; const tracing = init(); const client = new OpenAI(); const response = await client.chat.completions.create({ model: "gpt-4.1-mini", messages: [{ role: "user", content: "Hello" }], });Replace langfuse.trace() with span and update_trace()
The function you wrapped in a trace becomes a span. Its return value becomes the trace output. The trace fields you passed to
langfuse.trace()move ontoupdate_trace()(updateTrace()in TypeScript), and the session ID becomes the thread ID.from langfuse import Langfuse from langfuse.model import CreateTrace langfuse = Langfuse() def support_chat(question: str) -> str: trace = langfuse.trace( CreateTrace( name="support-chat", userId="user-7", sessionId="session-42", input=question, metadata={"plan": "enterprise"}, ) ) return openai.chat.completions.create( model="gpt-4.1-mini", messages=[{"role": "user", "content": question}], ).choices[0].message.contentfrom confident_trace import span, update_trace @span(name="support-chat") def support_chat(question: str) -> str: update_trace( user_id="user-7", thread_id="session-42", metadata={"plan": "enterprise"}, ) return client.chat.completions.create( model="gpt-4.1-mini", messages=[{"role": "user", "content": question}], ).choices[0].message.contentasync function supportChat(question: string) { const trace = langfuse.trace({ name: "support-chat", userId: "user-7", sessionId: "session-42", input: question, metadata: { plan: "enterprise" }, }); const answer = await answerQuestion(question); trace.update({ output: answer }); return answer; }import { span, updateTrace } from "confident-trace"; const supportChat = span({ name: "support-chat" }, async (question: string) => { updateTrace({ userId: "user-7", threadId: "session-42", metadata: { plan: "enterprise" }, }); return answerQuestion(question); });Drop the manual generations
A generation you created around an OpenAI call is now redundant: auto instrumentation records the same model span. Keep an LLM span with
update_span()(updateSpan()in TypeScript) for model calls it does not cover, such as a self-hosted model behind your own HTTP client.from langfuse.model import CreateGeneration, UpdateGeneration, Usage generation = trace.generation( CreateGeneration(name="answer", model="my-model", prompt=messages) ) completion, prompt_tokens, completion_tokens = call_my_model(messages) generation.end( UpdateGeneration( completion=completion, usage=Usage(promptTokens=prompt_tokens, completionTokens=completion_tokens), ) )from confident_trace import span, update_span with span("answer", type="llm"): completion, prompt_tokens, completion_tokens = call_my_model(messages) update_span( model="my-model", input=messages, output=completion, input_token_count=prompt_tokens, output_token_count=completion_tokens, )const generation = trace.generation({ name: "answer", model: "my-model", prompt: messages, }); const { completion, promptTokens, completionTokens } = await callMyModel(messages); generation.end({ completion, usage: { promptTokens, completionTokens } });import { updateSpan, withSpan } from "confident-trace"; await withSpan({ name: "answer", type: "llm" }, async () => { const { completion, promptTokens, completionTokens } = await callMyModel(messages); updateSpan({ model: "my-model", input: messages, output: completion, inputTokenCount: promptTokens, outputTokenCount: completionTokens, }); });trace.span()works the same way: replace it withspanor awith span("name"):block (withSpan()in TypeScript). For LangChain, remove the LangfuseCallbackHandlerfrom your callbacks.init()instruments LangChain directly.
Run your app once. The trace appears in your project's Observatory within seconds. If it does not, check that CONFIDENT_API_KEY is set in the same process and that the app flushes before it exits. flush() from confident-trace replaces langfuse.flush() in Python, and await tracing.shutdown() replaces await langfuse.shutdownAsync() in TypeScript.
Field Mapping
| Langfuse v1 | confident-trace |
|---|---|
Langfuse() | init() |
CreateTrace(name=...) | @span(name=...) or update_trace(name=...) |
CreateTrace(userId=...) | update_trace(user_id=...) |
CreateTrace(sessionId=...) | update_trace(thread_id=...) |
CreateTrace(input=..., output=...) | update_trace(input=..., output=...) |
CreateTrace(metadata=...) | update_trace(metadata=...) |
CreateSpan(name=...) | @span or with span("name"): |
CreateGeneration(model=..., prompt=...) | span(type="llm") with update_span(model=..., input=...) |
UpdateGeneration(completion=...) | update_span(output=...) |
Usage(promptTokens=..., completionTokens=...) | update_span(input_token_count=..., output_token_count=...) |
langfuse.flush() | flush() |
| Langfuse v1 | confident-trace |
|---|---|
new Langfuse({ ... }) | init() |
langfuse.trace({ name }) | span({ name }, fn) or updateTrace({ name }) |
langfuse.trace({ userId }) | updateTrace({ userId }) |
langfuse.trace({ sessionId }) | updateTrace({ threadId }) |
trace.update({ input, output }) | updateTrace({ input, output }) |
langfuse.trace({ metadata }) | updateTrace({ metadata }) |
trace.span({ name }) | span({ name }, fn) or withSpan({ name }, fn) |
trace.generation({ model, prompt }) | withSpan({ type: "llm" }, fn) with updateSpan({ model, input }) |
generation.end({ completion }) | updateSpan({ output }) |
usage: { promptTokens, completionTokens } | updateSpan({ inputTokenCount, outputTokenCount }) |
await langfuse.shutdownAsync() | await tracing.shutdown() |
What Changes
Tracing is where you start. Evals are where it pays off. Every trace gets a score. Low scores become dataset rows. Dataset rows become regression tests. Your next release ships against real production failures.
| Capability | Langfuse | Confident AI |
|---|---|---|
| OpenTelemetry ingestion | Yes | OTLP over HTTP and gRPC |
| Auto-instrumented providers | OpenAI wrapper and LangChain callback | OpenAI, Anthropic, Google GenAI, Bedrock, 5 gateways, 16 frameworks |
| Built-in eval metrics | Managed evaluators, custom scoring | 50+ research-backed metrics through DeepEval |
| Online evals on traces, spans and threads | Traces only | Yes |
| Quality-aware alerting on eval scores | Slack, webhook, GitHub Actions | Email, Slack, Discord, Teams |
| Prompt and use case drift detection | No | Yes |
| Automatic dataset curation from production | Manual add to dataset | Ingestion tasks, filtered and tagged |
| Multi-turn simulation | No | Yes |
| Git-based prompt management | No | Branches, PRs, approvals, eval actions |
| Cross-functional workflows, no code | No | PMs and QA run evals over HTTP |
| Regression testing and CI gates | No | Yes |
| Safety monitoring | No | Toxicity, bias, PII on production traffic |
Migration Questions
Do I have to upgrade Langfuse first?
No. You move straight from Langfuse SDK v1 to confident-trace. There is no need to step through Langfuse v2, v3 or v4 on the way.
Can I run both during the switch?
Yes. Langfuse v1 and confident-trace do not share a client, so both can trace the same app while you compare. Remove the Langfuse calls once you are ready to cut over.
What about my Langfuse datasets and prompts?
We import them on the migration call. Langfuse exports through its public API and scheduled blob storage export. We map the result to Confident AI datasets and prompt versions.
What about my scores?
Scores you sent with langfuse.score() do not move over as traces. Confident AI runs evals on every trace instead, with 50+ metrics through DeepEval. We can import your historical scores on the migration call.
How do I get the $200?
Book the call. After it, we apply a $200 coupon to your organization's first paid invoice. One reward per organization.
Is the TypeScript SDK ready?
The Python SDK is stable on PyPI. The TypeScript SDK is an alpha release on npm. The TypeScript snippets in this guide follow the alpha API.
Related
Trace context
Create spans, update traces and spans, and set trace properties like users and threads.
Build test runs from traces
Turn your production traces into an evaluated test run before you ship.
LLM Observability
Evals on every trace, drift detection, alerts, and datasets curated from production.
confident-trace on GitHub
The OpenTelemetry-native tracing SDK with 25 integrations for Python and TypeScript.
Last updated on