diff --git a/content/docs/observability/sdk/upgrade-path/js-v3-to-v4.mdx b/content/docs/observability/sdk/upgrade-path/js-v3-to-v4.mdx
index c247c22c86..568469ac88 100644
--- a/content/docs/observability/sdk/upgrade-path/js-v3-to-v4.mdx
+++ b/content/docs/observability/sdk/upgrade-path/js-v3-to-v4.mdx
@@ -14,10 +14,89 @@ Please follow each section below to upgrade your application from v3 to v4.
If you encounter any questions or issues while upgrading, please raise an [issue](/issues) on GitHub.
+## Overview of changes
+
+The v4 SDK is a major rewrite built on top of [OpenTelemetry](https://opentelemetry.io/). Instead of a single `langfuse` package that both created traces and talked to the API, v4 splits responsibilities across a set of modular packages:
+
+| v3 package / import | v4 package | Used for |
+| ---------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------- |
+| `langfuse` (tracing: `langfuse.trace()`, etc.) | `@langfuse/tracing` + `@langfuse/otel` | Creating traces and observations via OpenTelemetry |
+| `langfuse` (prompts, scores, datasets) | `@langfuse/client` | Non-tracing features (prompt management, scoring, datasets) |
+| `langfuse` (`observeOpenAI`) | `@langfuse/openai` | OpenAI SDK integration |
+| `langfuse-langchain` | `@langfuse/langchain` | LangChain.js integration |
+| `langfuse-vercel` (`LangfuseExporter`) | `@langfuse/otel` (`LangfuseSpanProcessor`) | Vercel AI SDK integration |
+
+The most important conceptual change: **tracing now requires an OpenTelemetry setup**. You register the [`LangfuseSpanProcessor`](https://langfuse-js-git-main-langfuse.vercel.app/classes/_langfuse_otel.LangfuseSpanProcessor.html) with an OpenTelemetry `NodeSDK` once at startup, and then create observations with functions from `@langfuse/tracing` anywhere in your application.
+
+See the [SDK v4 docs](/docs/observability/sdk/overview) for full details on each package.
+
+## Installation
+
+Replace the v3 packages with the modular v4 packages.
+
+**v3:**
+
+```bash
+npm install langfuse langfuse-langchain
+```
+
+**v4:**
+
+```bash
+# Tracing (OpenTelemetry-based)
+npm install @langfuse/tracing @langfuse/otel @opentelemetry/sdk-node
+
+# Add only the packages you use:
+npm install @langfuse/client # prompt management, scoring, datasets
+npm install @langfuse/openai # OpenAI integration
+npm install @langfuse/langchain # LangChain integration
+```
+
## Initialization
The Langfuse base URL environment variable is now `LANGFUSE_BASE_URL` and no longer `LANGFUSE_BASEURL`. For backward compatibility however, the latter will still work in v4 but not in future versions.
+In v3 you instantiated a single `Langfuse` client that handled everything. In v4 you set up OpenTelemetry once by registering the [`LangfuseSpanProcessor`](https://langfuse-js-git-main-langfuse.vercel.app/classes/_langfuse_otel.LangfuseSpanProcessor.html), typically in a dedicated `instrumentation.ts` file that is imported at the very top of your application's entry point.
+
+**v3:**
+
+```typescript
+import { Langfuse } from "langfuse";
+
+const langfuse = new Langfuse({
+ publicKey: process.env.LANGFUSE_PUBLIC_KEY,
+ secretKey: process.env.LANGFUSE_SECRET_KEY,
+ baseUrl: process.env.LANGFUSE_BASEURL,
+});
+```
+
+**v4:**
+
+```typescript filename="instrumentation.ts"
+import { NodeSDK } from "@opentelemetry/sdk-node";
+import { LangfuseSpanProcessor } from "@langfuse/otel";
+
+// Export the processor so you can flush it later (see "Shutdown and flushing")
+export const langfuseSpanProcessor = new LangfuseSpanProcessor();
+
+export const sdk = new NodeSDK({
+ spanProcessors: [langfuseSpanProcessor],
+});
+
+sdk.start();
+```
+
+Import this file at the top of your app's entry point so it runs before any traced code:
+
+```typescript filename="index.ts"
+import "./instrumentation";
+// ... rest of your application
+```
+
+
+**Next.js users:** Use the OpenTelemetry setup via `NodeSDK` shown above rather than `registerOTel` from `@vercel/otel`. The [`@vercel/otel` package does not yet support the OpenTelemetry JS SDK v2](https://github.com/vercel/otel/issues/154) that `@langfuse/tracing` and `@langfuse/otel` are based on. See the [Platform-specific notes](#platform-specific-notes) below.
+
+
## Tracing
The v4 SDK tracing is a major rewrite based on OpenTelemetry and introduces several breaking changes.
@@ -28,7 +107,91 @@ The v4 SDK tracing is a major rewrite based on OpenTelemetry and introduces seve
- The **`@langfuse/tracing`** and **`@langfuse/otel`** packages are for tracing.
- The **`@langfuse/client`** package and the [`LangfuseClient`](https://langfuse-js-git-main-langfuse.vercel.app/classes/_langfuse_client.LangfuseClient.html) class are now only for non-tracing features like scoring, prompt management, and datasets.
-See the [SDK v4 docs](/docs/observability/sdk/overview) for details on each.
+The methods that created traces and observations on the client are replaced by standalone functions:
+
+| v3 | v4 |
+| --------------------------------------------- | ----------------------------------------------------------------------------------- |
+| `langfuse.trace({ name })` | `startObservation("name")` or `startActiveObservation("name", async (span) => ...)` |
+| `trace.span({ name })` | `span.startObservation("name")` |
+| `trace.generation({ name, model })` | `span.startObservation("name", { model }, { asType: "generation" })` |
+| `obj.end({ output })` / `obj.update({ ... })` | `obj.update({ output }).end()` / `obj.update({ ... })` |
+
+**v3:**
+
+```typescript
+import { Langfuse } from "langfuse";
+
+const langfuse = new Langfuse();
+
+const trace = langfuse.trace({ name: "user-request" });
+
+const generation = trace.generation({
+ name: "llm-call",
+ model: "gpt-4o",
+ input: [{ role: "user", content: "What is the capital of France?" }],
+});
+
+generation.end({ output: "Paris is the capital of France." });
+```
+
+**v4:**
+
+```typescript
+import { startActiveObservation, startObservation } from "@langfuse/tracing";
+
+await startActiveObservation("user-request", async (span) => {
+ span.update({ input: { query: "What is the capital of France?" } });
+
+ const generation = startObservation(
+ "llm-call",
+ {
+ model: "gpt-4o",
+ input: [{ role: "user", content: "What is the capital of France?" }],
+ },
+ { asType: "generation" },
+ );
+
+ generation.update({ output: { content: "Paris is the capital of France." } }).end();
+});
+```
+
+See the [instrumentation docs](/docs/observability/sdk/instrumentation) for the three available instrumentation styles ([context manager](/docs/observability/sdk/instrumentation#context-manager), [observe wrapper](/docs/observability/sdk/instrumentation#observe-wrapper), and [manual observations](/docs/observability/sdk/instrumentation#manual-observations)).
+
+## Shutdown and flushing
+
+Because the SDK exports spans in the background, you must flush before short-lived processes (scripts, serverless functions, workers) exit, otherwise traces can be lost. In v3 this was `langfuse.shutdownAsync()` / `langfuse.flushAsync()`. In v4 you call [`shutdown()`](https://langfuse-js-git-main-langfuse.vercel.app/classes/_langfuse_otel.LangfuseSpanProcessor.html) on the OpenTelemetry `NodeSDK`, or [`forceFlush()`](https://langfuse-js-git-main-langfuse.vercel.app/classes/_langfuse_otel.LangfuseSpanProcessor.html#forceflush) on the exported `LangfuseSpanProcessor`.
+
+**v3:**
+
+```typescript
+await langfuse.shutdownAsync();
+// or, to flush without shutting down:
+await langfuse.flushAsync();
+```
+
+**v4:**
+
+```typescript
+import { sdk } from "./instrumentation";
+
+// Graceful shutdown (e.g. at the end of a script)
+await sdk.shutdown();
+```
+
+For serverless functions that should keep running, flush the processor instead of shutting down the whole SDK:
+
+```typescript filename="handler.ts" /forceFlush/
+import { langfuseSpanProcessor } from "./instrumentation";
+
+export async function handler(event, context) {
+ // ... your application logic ...
+
+ // Flush before the function freezes/exits
+ await langfuseSpanProcessor.forceFlush();
+}
+```
+
+See [client lifecycle & flushing](/docs/observability/sdk/instrumentation#client-lifecycle--flushing) for serverless and Vercel Cloud Function patterns.
## [Prompt Management](/docs/prompt-management/overview)
@@ -59,6 +222,40 @@ See the [SDK v4 docs](/docs/observability/sdk/overview) for details on each.
const prompt = await langfuse.prompt.get("my-prompt", { version: "1.0" });
```
+## Scoring
+
+Scoring moves from the tracing client to the [`LangfuseClient`](https://langfuse-js-git-main-langfuse.vercel.app/classes/_langfuse_client.LangfuseClient.html) in `@langfuse/client`, and the method is now namespaced under `langfuse.score.create()`.
+
+**v3:**
+
+```typescript
+import { Langfuse } from "langfuse";
+
+const langfuse = new Langfuse();
+
+langfuse.score({
+ traceId: "trace_id_here",
+ name: "accuracy",
+ value: 0.9,
+});
+```
+
+**v4:**
+
+```typescript
+import { LangfuseClient } from "@langfuse/client";
+
+const langfuse = new LangfuseClient();
+
+await langfuse.score.create({
+ traceId: "trace_id_here",
+ name: "accuracy",
+ value: 0.9,
+});
+```
+
+See [custom scores documentation](/docs/evaluation/evaluation-methods/custom-scores) for new scoring methods.
+
## OpenAI integration
- **Import**: The import of the OpenAI integration is now:
@@ -69,17 +266,36 @@ See the [SDK v4 docs](/docs/observability/sdk/overview) for details on each.
- You can set the `environment` and `release` now via the `LANGFUSE_TRACING_ENVIRONMENT` and `LANGFUSE_TRACING_RELEASE` environment variables.
-## Vercel AI SDK
+Note that `observeOpenAI` relies on the OpenTelemetry setup from the [Initialization](#initialization) section, so make sure your `instrumentation.ts` is imported before any OpenAI calls.
-Works very similarly to v3, but replaces `LangfuseExporter` from `langfuse-vercel` with the regular `LangfuseSpanProcessor` from `@langfuse/otel`.
+## Anthropic SDK
-Please see [full example on usage with the AI SDK](/docs/observability/sdk/instrumentation#framework-third-party-telemetry) for more details.
+There is no dedicated Langfuse package for the Anthropic SDK. In v4 you instrument it with the OpenTelemetry-native [`AnthropicInstrumentation`](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-anthropic) from OpenInference, registered alongside the `LangfuseSpanProcessor`.
-
+```bash
+npm install @anthropic-ai/sdk @arizeai/openinference-instrumentation-anthropic @langfuse/otel @opentelemetry/sdk-node
+```
-Please note that provided tool definitions to the LLM are now mapped to `metadata.tools` and no longer in `input.tools`. This is relevant in case you are running evaluations on your generations.
+```typescript filename="instrumentation.ts"
+import { NodeSDK } from "@opentelemetry/sdk-node";
+import { LangfuseSpanProcessor } from "@langfuse/otel";
+import { AnthropicInstrumentation } from "@arizeai/openinference-instrumentation-anthropic";
+import Anthropic from "@anthropic-ai/sdk";
-
+const instrumentation = new AnthropicInstrumentation();
+instrumentation.manuallyInstrument(Anthropic);
+
+export const langfuseSpanProcessor = new LangfuseSpanProcessor();
+
+export const sdk = new NodeSDK({
+ spanProcessors: [langfuseSpanProcessor],
+ instrumentations: [instrumentation],
+});
+
+sdk.start();
+```
+
+After this setup, all `anthropic.messages.create(...)` calls are traced automatically. See the [Anthropic JS/TS integration](/integrations/model-providers/anthropic-js) for a full example.
## [Langchain integration](/integrations/frameworks/langchain)
@@ -91,6 +307,34 @@ Please note that provided tool definitions to the LLM are now mapped to `metadat
- You can set the `environment` and `release` now via the `LANGFUSE_TRACING_ENVIRONMENT` and `LANGFUSE_TRACING_RELEASE` environment variables.
+The `CallbackHandler` is still passed via the `callbacks` array on invocation, and it relies on the OpenTelemetry setup from the [Initialization](#initialization) section.
+
+```typescript
+import { CallbackHandler } from "@langfuse/langchain";
+
+const langfuseHandler = new CallbackHandler();
+
+await chain.invoke({ input: "..." }, { callbacks: [langfuseHandler] });
+```
+
+## Vercel AI SDK
+
+Works very similarly to v3, but replaces `LangfuseExporter` from `langfuse-vercel` with the regular `LangfuseSpanProcessor` from `@langfuse/otel`.
+
+Please see [full example on usage with the AI SDK](/docs/observability/sdk/instrumentation#framework-third-party-telemetry) for more details.
+
+
+
+Please note that provided tool definitions to the LLM are now mapped to `metadata.tools` and no longer in `input.tools`. This is relevant in case you are running evaluations on your generations.
+
+
+
+## Other framework integrations
+
+Because v4 is built on OpenTelemetry, you can use **any** OpenTelemetry instrumentation for your stack (for example LlamaIndexTS or other OpenInference instrumentations) by registering it on the `NodeSDK` next to the `LangfuseSpanProcessor`, exactly as shown for the [Anthropic SDK](#anthropic-sdk) above. The spans those libraries emit are automatically picked up and sent to Langfuse — no Langfuse-specific wrapper is required.
+
+See the [integrations overview](/integrations) for the list of supported libraries.
+
## `langfuseClient.getTraceUrl`
- method is now asynchronous and returns a promise
@@ -99,28 +343,71 @@ Please note that provided tool definitions to the LLM are now mapped to `metadat
const traceUrl = await langfuseClient.getTraceUrl(traceId);
```
-## Scoring
+## Datasets
-- **Import**: The import of the Langfuse client is now:
+See [datasets documentation](/docs/evaluation/dataset-runs/remote-run#setup--run-via-sdk) for new dataset methods.
- ```typescript
- import { LangfuseClient } from "@langfuse/client";
+## Platform-specific notes [#platform-specific-notes]
+
+### Next.js edge runtime
+
+The v4 tracing setup uses the OpenTelemetry `NodeSDK` and depends on Node.js APIs (such as `crypto`) that are **not available in the Next.js edge runtime**. Run any traced code (and the instrumentation setup) on the **Node.js runtime**.
+
+- In Next.js, register the SDK from `instrumentation.ts` and guard it so it only runs on the Node.js runtime:
+
+ ```typescript filename="instrumentation.ts"
+ export async function register() {
+ if (process.env.NEXT_RUNTIME === "nodejs") {
+ await import("./instrumentation.node");
+ }
+ }
```
-- **Usage**: The usage of the Langfuse client is now:
+ Put the `NodeSDK` / `LangfuseSpanProcessor` setup from the [Initialization](#initialization) section in `instrumentation.node.ts`.
+
+- For route handlers or pages that perform traced LLM calls, opt into the Node.js runtime:
```typescript
- const langfuse = new LangfuseClient();
+ export const runtime = "nodejs";
+ ```
+
+### Cloudflare Workers and other non-Node runtimes
+
+Cloudflare Workers and similar runtimes are not full Node.js environments. To use the v4 SDK there:
+
+- Enable the [`nodejs_compat`](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) compatibility flag so the required Node.js APIs are available.
+- Configure the processor for short-lived execution and flush explicitly before the request finishes, since there is no long-running process to drain the buffer:
+
+ ```typescript /exportMode: "immediate"/ /forceFlush/
+ import { LangfuseSpanProcessor } from "@langfuse/otel";
- await langfuse.score.create({
- traceId: "trace_id_here",
- name: "accuracy",
- value: 0.9,
+ export const langfuseSpanProcessor = new LangfuseSpanProcessor({
+ exportMode: "immediate",
});
+
+ // In your fetch handler, keep the worker alive until the flush completes:
+ // ctx.waitUntil(langfuseSpanProcessor.forceFlush());
```
-See [custom scores documentation](/docs/evaluation/evaluation-methods/custom-scores) for new scoring methods.
+### Self-hosted minimum versions
-## Datasets
+If you self-host Langfuse, the OTel-based v4 SDK requires a recent platform version:
-See [datasets documentation](/docs/evaluation/dataset-runs/remote-run#setup--run-via-sdk) for new dataset methods.
+- **Minimum:** [Langfuse platform ≥ 3.63.0](https://github.com/langfuse/langfuse/releases/tag/v3.63.0) for the OTel-based SDKs.
+- **Recommended:** [Langfuse platform ≥ 3.95.0](https://github.com/langfuse/langfuse/releases/tag/v3.95.0) so all v4 features work correctly.
+
+Upgrade your deployment (e.g. by pulling the latest Docker images and redeploying) before rolling out the v4 SDK. See the [self-hosting upgrade guide](/self-hosting/upgrade) for details.
+
+## Migration checklist
+
+1. Replace the `langfuse` / `langfuse-langchain` / `langfuse-vercel` packages with the modular `@langfuse/*` packages you need.
+2. Add an OpenTelemetry setup (`instrumentation.ts`) that registers the `LangfuseSpanProcessor`, and import it at your app's entry point.
+3. Rename `LANGFUSE_BASEURL` to `LANGFUSE_BASE_URL`.
+4. Replace `langfuse.trace()` / `.span()` / `.generation()` with `startObservation` / `startActiveObservation` from `@langfuse/tracing`.
+5. Move scoring, prompt management, and datasets to `LangfuseClient` from `@langfuse/client`.
+6. Replace `langfuse.shutdownAsync()` / `flushAsync()` with `sdk.shutdown()` or `langfuseSpanProcessor.forceFlush()`.
+7. Update integration imports (`@langfuse/openai`, `@langfuse/langchain`) and swap `LangfuseExporter` for `LangfuseSpanProcessor` for the Vercel AI SDK.
+8. If you run on Next.js edge, Cloudflare Workers, or another non-Node runtime, apply the [platform-specific notes](#platform-specific-notes).
+9. If self-hosting, confirm your platform version meets the [minimum requirements](#self-hosted-minimum-versions).
+
+