---
title: Link to Traces
sidebarTitle: Link to Traces
description: Link Langfuse Prompts to Traces.
---

# Link Prompts to Traces

Linking prompts to [traces](/docs/observability) enables tracking of metrics and evaluations per prompt version. It's the foundation of improving prompt quality over time.

After linking prompts and traces, navigating to a generation span in Langfuse will highlight the prompt that was used to generate the response. To access the metrics, navigate to your prompt and click on the `Metrics` tab.

## How to Link Prompts to Traces

<LangTabs items={["Python SDK", "JS/TS SDK", "OpenAI SDK (Python)", "OpenAI SDK (JS/TS)", "Langchain (Python)", "Langchain (JS/TS)", "Vercel AI SDK"]}>
<Tab>

For generations created with the Langfuse Python SDK, pass the prompt directly to the generation using the `prompt` keyword argument. This links the prompt only to the intended generation and is the recommended approach.

You can set the prompt on a specific generation with one of the following Langfuse Python SDK methods. For more information, see the [SDK documentation](/docs/observability/sdk/python/instrumentation).

**Decorators**

```python
from langfuse import observe, get_client

langfuse = get_client()

@observe(as_type="generation")
def nested_generation():
    prompt = langfuse.get_prompt("movie-critic")

    langfuse.update_current_generation(
        prompt=prompt,
    )

@observe()
def main():
  nested_generation()

main()
```

**Context Managers**

```python
from langfuse import get_client

langfuse = get_client()

prompt = langfuse.get_prompt("movie-critic")

with langfuse.start_as_current_observation(
    as_type="generation",
    name="movie-generation",
    model="gpt-4o",
    prompt=prompt
) as generation:
    # Your LLM call here
    generation.update(output="LLM response")
```

**Manual observations**

```python
from langfuse import get_client

langfuse = get_client()

prompt = langfuse.get_prompt("movie-critic")

generation = langfuse.start_observation(
    name="movie-generation",
    as_type="generation",
    model="gpt-4o",
    prompt=prompt
)

# Your LLM call here

generation.update(output="LLM response")
generation.end()  # Important: manually end the generation
```

**Propagate a prompt to multiple generations**

Use `propagate_attributes(prompt=prompt)` when multiple generations created within the same context use the same prompt version. This option is available in Python SDK 4.14.0 and later.

```python /propagate_attributes(prompt=prompt)/
from langfuse import get_client, propagate_attributes

langfuse = get_client()
prompt = langfuse.get_prompt("movie-critic")

with propagate_attributes(prompt=prompt):
    with langfuse.start_as_current_observation(
        as_type="generation",
        name="movie-review",
    ) as generation:
        generation.update(
            input=prompt.compile(movie="Dune 2"),
            output="A sweeping, ambitious sequel.",
        )

    with langfuse.start_as_current_observation(
        as_type="generation",
        name="movie-review",
    ) as generation:
        generation.update(
            input=prompt.compile(movie="Arrival"),
            output="A thoughtful and moving science-fiction film.",
        )
```

**Third-party instrumentation**

Propagation is also useful when an instrumentation library creates generations for you and does not expose a Langfuse `prompt` argument. For example, with the [LiteLLM OpenTelemetry integration](/integrations/frameworks/litellm-sdk):

```python /propagate_attributes(prompt=prompt)/
import litellm
from langfuse import get_client, propagate_attributes

langfuse = get_client()
prompt = langfuse.get_prompt("movie-critic")

litellm.callbacks = ["langfuse_otel"]

with propagate_attributes(prompt=prompt):
    response = litellm.completion(
        model="gpt-4o",
        messages=[
            {
                "role": "user",
                "content": prompt.compile(movie="Dune 2"),
            }
        ],
    )
```

This also works with the OpenAI Agents SDK and OpenInference instrumentations exported through the Langfuse Python SDK. Only generation observations are linked to the prompt. If a generation sets its own prompt explicitly, that prompt takes precedence over the propagated prompt.

</Tab>

<Tab>

There are three ways to create traces with the Langfuse JS/TS SDK. For more information, see the [SDK documentation](/docs/observability/sdk/typescript/instrumentation).

**Observe wrapper**

```ts
import { LangfuseClient } from "@langfuse/client";
import { observe, updateActiveObservation } from "@langfuse/tracing";

const langfuse = new LangfuseClient();

const callLLM = async (input: string) => {
  const prompt = await langfuse.prompt.get("my-prompt");

  updateActiveObservation({ prompt }, { asType: "generation" });

  return await invokeLLM(input);
};

export const observedCallLLM = observe(callLLM);
```

**Context manager**

```ts
import { LangfuseClient } from "@langfuse/client";
import { startActiveObservation } from "@langfuse/tracing";

const langfuse = new LangfuseClient();

startActiveObservation(
  "llm",
  async (generation) => {
    const prompt = await langfuse.prompt.get("my-prompt");
    generation.update({ prompt });
  },
  { asType: "generation" },
);
```

**Manual observations**

```ts
import { LangfuseClient } from "@langfuse/client";
import { startObservation } from "@langfuse/tracing";

const prompt = await new LangfuseClient().prompt.get("my-prompt");

startObservation(
  "llm",
  {
    prompt,
  },
  { asType: "generation" },
);
```

</Tab>

<Tab>

```python /langfuse_prompt=prompt/
from langfuse.openai import openai
from langfuse import get_client

langfuse = get_client()

prompt = langfuse.get_prompt("calculator")

openai.chat.completions.create(
  model="gpt-4o",
  messages=[
    {"role": "system", "content": prompt.compile(base=10)},
    {"role": "user", "content": "1 + 1 = "}],
  langfuse_prompt=prompt
)
```

</Tab>

<Tab>
Please make sure you have [OpenTelemetry already set up](/docs/observability/sdk/overview#initialize-tracing) for tracing.

```ts /langfusePrompt,/
import { observeOpenAI } from "@langfuse/openai";
import OpenAI from "openai";

const langfusePrompt = await langfuse.prompt.get("prompt-name"); // Fetch a previously created prompt

const res = await observeOpenAI(new OpenAI(), {
  langfusePrompt,
}).completions.create({
  prompt: langfusePrompt.prompt,
  model: "gpt-4o",
  max_tokens: 300,
});
```

</Tab>

<Tab>

```python
from langfuse import get_client
from langfuse.langchain import CallbackHandler
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_openai import ChatOpenAI, OpenAI

langfuse = get_client()

# Initialize the Langfuse handler
langfuse_handler = CallbackHandler()
```

**Text prompts**

```python /"langfuse_prompt"/
langfuse_text_prompt = langfuse.get_prompt("movie-critic")

## Pass the langfuse_text_prompt to the PromptTemplate as metadata to link it to generations that use it
langchain_text_prompt = PromptTemplate.from_template(
    langfuse_text_prompt.get_langchain_prompt(),
    metadata={"langfuse_prompt": langfuse_text_prompt},
)

## Use the text prompt in a Langchain chain
llm = OpenAI()
completion_chain = langchain_text_prompt | llm

completion_chain.invoke({"movie": "Dune 2", "criticlevel": "expert"}, config={"callbacks": [langfuse_handler]})
```

**Chat prompts**

```python /"langfuse_prompt"/
langfuse_chat_prompt = langfuse.get_prompt("movie-critic-chat", type="chat")

## Manually set the metadata on the langchain_chat_prompt to link it to generations that use it
langchain_chat_prompt = ChatPromptTemplate.from_messages(
    langfuse_chat_prompt.get_langchain_prompt()
)

langchain_chat_prompt.metadata = {"langfuse_prompt": langfuse_chat_prompt}

## or use the ChatPromptTemplate constructor directly.
## Note that using ChatPromptTemplate.from_template led to issues in the past
## See: https://github.com/langfuse/langfuse/issues/5374
langchain_chat_prompt = ChatPromptTemplate(
    langfuse_chat_prompt.get_langchain_prompt(),
    metadata={"langfuse_prompt": langfuse_chat_prompt}
)

## Use the chat prompt in a Langchain chain
chat_llm = ChatOpenAI()
chat_chain = langchain_chat_prompt | chat_llm

chat_chain.invoke({"movie": "Dune 2", "criticlevel": "expert"}, config={"callbacks": [langfuse_handler]})
```

  If you use the `with_config` method on the PromptTemplate to create a new
  Langchain Runnable with updated config, please make sure to pass the
  `langfuse_prompt` in the `metadata` key as well.

  Set the `langfuse_prompt` metadata key only on PromptTemplates and not
  additionally on the LLM calls or elsewhere in your chains.

</Tab>

<Tab>

Please make sure you have [OpenTelemetry already set up](/docs/observability/sdk/overview#initialize-tracing) for tracing.

```ts
import { LangfuseClient } from "@langfuse/client";
import { CallbackHandler } from "@langfuse/langchain";

import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI, OpenAI } from "@langchain/openai";

const langfuseHandler = new CallbackHandler();

const langfuse = new LangfuseClient();
```

**Text prompts**

```ts /metadata: { langfusePrompt:/
const langfuseTextPrompt = await langfuse.prompt.get("movie-critic"); // Fetch a previously created text prompt

// Pass the langfuseTextPrompt to the PromptTemplate as metadata to link it to generations that use it
const langchainTextPrompt = PromptTemplate.fromTemplate(
  langfuseTextPrompt.getLangchainPrompt()
).withConfig({
  metadata: { langfusePrompt: langfuseTextPrompt },
});

const model = new OpenAI();
const chain = langchainTextPrompt.pipe(model);

await chain.invoke({ movie: "Dune 2", criticlevel: "expert" }, { callbacks: [langfuseHandler] });

```

**Chat prompts**

```ts /metadata: { langfusePrompt:/
const langfuseChatPrompt = await langfuse.prompt.get(
  "movie-critic-chat",
  {
    type: "chat",
  }
); // type option infers the prompt type as chat (default is 'text')

const langchainChatPrompt = ChatPromptTemplate.fromMessages(
  langfuseChatPrompt.getLangchainPrompt().map((m) => [m.role, m.content])
).withConfig({
  metadata: { langfusePrompt: langfuseChatPrompt },
});

const chatModel = new ChatOpenAI();
const chatChain = langchainChatPrompt.pipe(chatModel);

await chatChain.invoke({ movie: "Dune 2", criticlevel: "expert" }, { callbacks: [langfuseHandler] });
```

</Tab>

<Tab>

Link Langfuse prompts to Vercel AI SDK generations by setting the `langfusePrompt` property in the `metadata` field:

```typescript /langfusePrompt: fetchedPrompt.toJSON()/
import { generateText } from "ai";
import { LangfuseClient } from "@langfuse/client";

const langfuse = new LangfuseClient();

const fetchedPrompt = await langfuse.prompt.get("my-prompt");

const result = await generateText({
  model: openai("gpt-4o"),
  prompt: fetchedPrompt.prompt,
  experimental_telemetry: {
    isEnabled: true,
    metadata: {
      langfusePrompt: fetchedPrompt.toJSON(),
    },
  },
});
```

</Tab>

</LangTabs>

  If a [fallback
  prompt](/docs/prompt-management/features/guaranteed-availability#fallback) is
  used, no link will be created.

## Metrics Reference

Once prompts are linked to traces, Langfuse automatically aggregates the following metrics per prompt version. You can compare them across prompt versions in the Metrics tab in the Langfuse UI:

- Median generation latency
- Median generation input tokens
- Median generation output tokens
- Median generation costs
- Generation count
- Median [score](/docs/evaluation/scores/data-model#scores) value
- First and last generation timestamp

<!-- agent-instructions -->

---

## Agent Instructions

This page is part of the [Langfuse](https://langfuse.com) documentation, published as plain Markdown for AI agents. Every page is available as Markdown by appending `.md` to its URL, or by sending an `Accept: text/markdown` header. This page: `https://langfuse.com/docs/prompt-management/features/link-to-traces.md`.

### Querying these docs

If the answer is not on this page, query the documentation instead of guessing:

- **Semantic search** across all Langfuse docs, returning an answer with the relevant pages and excerpts. Ask a specific, self-contained question:

  ```bash
  curl -sG "https://langfuse.com/api/search-docs" --data-urlencode "query=How do I trace a LangGraph agent?"
  ```

- **Index of every page**: <https://langfuse.com/llms.txt>, with per-section indexes [llms-docs.txt](https://langfuse.com/llms-docs.txt), [llms-integrations.txt](https://langfuse.com/llms-integrations.txt), and [llms-self-hosting.txt](https://langfuse.com/llms-self-hosting.txt).

### Before writing Langfuse code

- **Install the [Langfuse Agent Skill](https://langfuse.com/docs/api-and-data-platform/features/agent-skill).** It encodes Langfuse's own best practices for instrumentation, prompt management, and evaluation, and materially improves results.
- **Read [What does a good trace look like?](https://langfuse.com/docs/observability/best-practices.md)** before instrumenting an application.
- **Verify endpoints, parameters, and response fields** against the [API reference](https://api.reference.langfuse.com) instead of inferring them from code examples.
- **Use the [Langfuse CLI](https://langfuse.com/docs/api-and-data-platform/features/cli)** (`npx langfuse-cli api <resource> <action>`) to read or write traces, prompts, datasets, and scores from the terminal.

Found an error in these docs? Please open an issue at <https://github.com/langfuse/langfuse-docs/issues>.
