Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Upgrade Langfuse SDK versions and migrate between API changes. Use when upgrading Langfuse SDK, handling breaking changes, or migrating between Langfuse versions. Trigger with phrases like "upgrade langfuse", "langfuse migration", "update langfuse SDK", "langfuse breaking changes", "langfuse version".
.claude/skills/jeremylongshore-langfuse-upgrade-migration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -6% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 199% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 74% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 3% | 0% |
!npm list langfuse @langfuse/client @langfuse/tracing @langfuse/otel 2>/dev/null | head -10 || echo 'No langfuse packages found' !pip show langfuse 2>/dev/null | grep -E "Name|Version" || echo 'Python langfuse not installed'
Step-by-step guide for upgrading the Langfuse SDK across major versions. Covers v3 to v4 (OTel rewrite), v4 to v5, breaking changes, and automated codemods.
| SDK | Package | Architecture | Status | |-----|---------|-------------|--------| | v3 | langfuse (single) | Custom, Langfuse class | Legacy | | v4 | @langfuse/client, @langfuse/tracing, @langfuse/otel | OpenTelemetry-based | Stable | | v5 | @langfuse/client, @langfuse/tracing, @langfuse/otel | OpenTelemetry + improvements | Latest |
bashset -euo pipefail # Check what you have npm list langfuse @langfuse/client @langfuse/tracing 2>/dev/null # Check latest available npm info @langfuse/client version npm info @langfuse/tracing version npm info langfuse version # Python pip show langfuse 2>/dev/null | grep Version pip index versions langfuse 2>/dev/null | head -3
This is the biggest migration -- v4 rewrites tracing on OpenTelemetry.
2a. Install new packages:
bashset -euo pipefail # Install v4+ packages npm install @langfuse/client @langfuse/tracing @langfuse/otel @opentelemetry/sdk-node # Keep langfuse v3 temporarily for comparison # Remove after migration: npm uninstall langfuse
2b. Update initialization:
typescript// BEFORE (v3): import { Langfuse } from "langfuse"; const langfuse = new Langfuse({ publicKey: process.env.LANGFUSE_PUBLIC_KEY, secretKey: process.env.LANGFUSE_SECRET_KEY, baseUrl: process.env.LANGFUSE_HOST, }); // AFTER (v4+): import { LangfuseClient } from "@langfuse/client"; import { LangfuseSpanProcessor } from "@langfuse/otel"; import { NodeSDK } from "@opentelemetry/sdk-node"; // OTel setup (once at entry point) const sdk = new NodeSDK({ spanProcessors: [new LangfuseSpanProcessor()], }); sdk.start(); // Client for prompts, datasets, scores const langfuse = new LangfuseClient();
2c. Update tracing calls:
typescript// BEFORE (v3): Manual trace/span/generation const trace = langfuse.trace({ name: "my-op", input: data }); const span = trace.span({ name: "step-1", input: data }); await doWork(); span.end({ output: result }); const gen = trace.generation({ name: "llm", model: "gpt-4o" }); gen.end({ output: response, usage: { promptTokens: 10 } }); await langfuse.flushAsync(); // AFTER (v4+): startActiveObservation with auto-nesting import { startActiveObservation, updateActiveObservation } from "@langfuse/tracing"; await startActiveObservation("my-op", async () => { updateActiveObservation({ input: data }); await startActiveObservation("step-1", async () => { updateActiveObservation({ input: data }); const result = await doWork(); updateActiveObservation({ output: result }); }); await startActiveObservation({ name: "llm", asType: "generation" }, async () => { updateActiveObservation({ model: "gpt-4o" }); const response = await callLLM(); updateActiveObservation({ output: response, usage: { promptTokens: 10 } }); }); });
2d. Update OpenAI wrapper:
typescript// BEFORE (v3): import { observeOpenAI } from "langfuse"; // AFTER (v4+): import { observeOpenAI } from "@langfuse/openai"; // npm install @langfuse/openai
2e. Update environment variable:
bash# BEFORE: LANGFUSE_HOST or LANGFUSE_BASEURL # AFTER: LANGFUSE_BASE_URL (LANGFUSE_BASEURL still works in v4 but not v5)
2f. Update prompt management:
typescript// BEFORE (v3): const prompt = await langfuse.getPrompt("my-prompt", 2); // version as positional arg // AFTER (v4+): const prompt = await langfuse.prompt.get("my-prompt", { version: 2, // version in options object type: "text", // explicit type });
2g. Update shutdown:
typescript// BEFORE (v3): await langfuse.shutdownAsync(); // AFTER (v4+): await sdk.shutdown(); // Shuts down OTel SDK + flushes spans
python# BEFORE (v2): from langfuse import Langfuse langfuse = Langfuse() @langfuse.observe() def my_function(): pass # AFTER (v3): from langfuse.decorators import observe, langfuse_context @observe() def my_function(): langfuse_context.update_current_observation( metadata={"key": "value"} )
bashset -euo pipefail # Run existing test suite npm test # Verify traces appear in dashboard node -e " const { startActiveObservation, updateActiveObservation } = require('@langfuse/tracing'); startActiveObservation('upgrade-verify', async () => { updateActiveObservation({ input: { test: true }, output: { migrated: true } }); }).then(() => console.log('Migration verified')); "
bashset -euo pipefail # After all tests pass npm uninstall langfuse # Verify no lingering imports grep -rn "from ['\"]langfuse['\"]" src/ || echo "No old imports found"
| Change | v3 | v4+ | |--------|-------|-------| | Package | langfuse | @langfuse/client + @langfuse/tracing + @langfuse/otel | | Client class | Langfuse | LangfuseClient | | Base URL env | LANGFUSE_HOST | LANGFUSE_BASE_URL | | Tracing | langfuse.trace() / .span() / .generation() | startActiveObservation() / observe() | | Flush | langfuse.flushAsync() | sdk.shutdown() | | Prompt version | getPrompt(name, version) | prompt.get(name, { version }) | | OpenAI | import { observeOpenAI } from "langfuse" | import { observeOpenAI } from "@langfuse/openai" |
| Error | Cause | Solution | |-------|-------|----------| | Cannot find module '@langfuse/tracing' | Package not installed | npm install @langfuse/tracing @langfuse/otel @opentelemetry/sdk-node | | langfuse.trace is not a function | Using v4 LangfuseClient for tracing | Use startActiveObservation from @langfuse/tracing | | Flat traces (no nesting) | OTel SDK not started | Register LangfuseSpanProcessor with NodeSDK | | LANGFUSE_HOST ignored | v5 dropped legacy env var | Rename to LANGFUSE_BASE_URL |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 25,884 | 14,444 | -44% | 1 | 1 | 0% | 4,367 | 4,116 | -6% | 0 | 0 | — |
case-02 | fail→pass | 10,532 | 10,932 | +4% | 1 | 1 | 0% | 1,069 | 3,191 | +199% | 0 | 0 | — |
case-03 | fail→pass | 20,461 | 19,386 | -5% | 1 | 1 | 0% | 2,707 | 4,713 | +74% | 0 | 0 | — |
case-04 | fail→pass | 19,545 | 10,453 | -47% | 1 | 1 | 0% | 2,106 | 3,155 | +50% | 0 | 0 | — |
case-05 | fail→pass | 24,165 | 11,913 | -51% | 1 | 1 | 0% | 3,347 | 3,459 | +3% | 0 | 0 | — |
case-06 | fail→pass | 23,107 | 10,977 | -52% | 1 | 1 | 0% | 3,239 | 3,174 | -2% | 0 | 0 | — |
case-07 | fail→pass | 15,626 | 14,010 | -10% | 1 | 1 | 0% | 2,799 | 3,741 | +34% | 0 | 0 | — |
case-08 | pass→pass | 19,396 | 10,758 | -45% | 1 | 1 | 0% | 2,072 | 3,255 | +57% | 0 | 0 | — |
case-09 | pass→pass | 14,973 | 4,267 | -72% | 1 | 1 | 0% | 1,945 | 2,911 | +50% | 0 | 0 | — |
case-10 | fail→fail | 26,022 | 4,159 | -84% | 1 | 1 | 0% | 3,213 | 2,630 | -18% | 0 | 0 | — |
case-11 | fail→pass | 12,823 | 14,651 | +14% | 1 | 1 | 0% | 2,368 | 4,285 | +81% | 0 | 0 | — |
case-12 | fail→pass | 12,552 | 10,518 | -16% | 1 | 1 | 0% | 2,200 | 3,014 | +37% | 0 | 0 | — |
case-13 | fail→pass | 14,156 | 8,989 | -37% | 1 | 1 | 0% | 1,619 | 2,728 | +68% | 0 | 0 | — |
case-14 | pass→pass | 10,364 | 12,063 | +16% | 1 | 1 | 0% | 1,959 | 3,086 | +58% | 0 | 0 | — |
case-15 | fail→pass | 15,388 | 3,964 | -74% | 1 | 1 | 0% | 1,832 | 2,782 | +52% | 0 | 0 | — |
case-16 | fail→pass | 27,400 | 15,669 | -43% | 1 | 1 | 0% | 3,343 | 3,655 | +9% | 0 | 0 | — |
case-17 | fail→pass | 16,321 | 13,746 | -16% | 1 | 1 | 0% | 1,792 | 3,300 | +84% | 0 | 0 | — |
case-18 | pass→pass | 19,772 | 15,675 | -21% | 1 | 1 | 0% | 2,099 | 3,637 | +73% | 0 | 0 | — |
case-19 | fail→pass | 20,202 | 5,396 | -73% | 1 | 1 | 0% | 2,326 | 3,146 | +35% | 0 | 0 | — |
case-20 | pass→pass | 13,545 | 14,854 | +10% | 1 | 1 | 0% | 2,615 | 4,015 | +54% | 0 | 0 | — |
case-21 | pass→pass | 22,615 | 19,757 | -13% | 1 | 1 | 0% | 2,633 | 4,896 | +86% | 0 | 0 | — |
case-22 | pass→pass | 17,030 | 16,357 | -4% | 1 | 1 | 0% | 1,888 | 4,178 | +121% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +64 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.