| name | langfuse-core-workflow-a |
| description | Execute Langfuse primary workflow: Tracing LLM calls and spans.
Use when implementing LLM tracing, building traced AI features,
or adding observability to existing LLM applications.
Trigger with phrases like "langfuse tracing", "trace LLM calls",
"add langfuse to openai", "langfuse spans", "track llm requests".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Langfuse Core Workflow A: Tracing LLM Calls
Overview
Primary workflow for Langfuse: end-to-end tracing of LLM calls, chains, and agents.
Prerequisites
- Completed
langfuse-install-auth setup
- Understanding of Langfuse trace hierarchy
- LLM provider SDK (OpenAI, Anthropic, etc.)
Instructions
Step 1: Set Up Automatic OpenAI Tracing
import { observeOpenAI } from "langfuse";
import OpenAI from "openai";
const openai = observeOpenAI(new OpenAI());
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is Langfuse?" },
],
});
Step 2: Manual Tracing for Complex Flows
import { Langfuse } from "langfuse";
const langfuse = new Langfuse();
async function ragPipeline(query: string) {
const trace = langfuse.trace({
name: "rag-pipeline",
input: { query },
metadata: { pipeline: "rag-v1" },
});
const embedSpan = trace.span({
name: "embed-query",
input: { text: query },
});
const queryEmbedding = await embedText(query);
embedSpan.end({
output: { dimensions: queryEmbedding.length },
metadata: { model: "text-embedding-ada-002" },
});
const searchSpan = trace.span({
name: "vector-search",
input: { embedding: "vector[1536]" },
});
const documents = await searchVectorDB(queryEmbedding);
searchSpan.({
: {
: documents.,
: documents[]?.,
},
});
generation = trace.({
: ,
: ,
: { : , : },
: {
query,
: documents.( d.),
},
});
answer = (query, documents);
generation.({
: answer.,
: {
: answer..,
: answer..,
: answer..,
},
});
trace.({
: { : answer. },
});
answer.;
}
Step 3: Trace Streaming Responses
async function streamingChat(messages: ChatMessage[]) {
const trace = langfuse.trace({
name: "streaming-chat",
input: { messages },
});
const generation = trace.generation({
name: "stream-response",
model: "gpt-4",
input: messages,
});
const stream = await openai.chat.completions.create({
model: "gpt-4",
messages,
stream: true,
});
let fullContent = "";
let usage = { promptTokens: 0, completionTokens: 0 };
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || "";
fullContent += content;
if (chunk.usage) {
usage = {
promptTokens: chunk.usage.prompt_tokens,
completionTokens: chunk..,
};
}
content;
}
generation.({
: fullContent,
usage,
});
trace.({ : { : fullContent } });
}
Step 4: Link Traces to Parent Operations
async function handleChatRequest(req: Request) {
const trace = langfuse.trace({
name: "api/chat",
userId: req.userId,
sessionId: req.sessionId,
input: req.body,
});
const response = await processChat(trace, req.body.message);
return response;
}
async function processChat(
parentTrace: ReturnType<typeof langfuse.trace>,
message: string
) {
const span = parentTrace.span({
name: "process-chat",
input: { message },
});
const response = await openai.chat.completions.create(
{
model: "gpt-4",
messages: [{ role: , : message }],
},
{ : span }
);
span.({ : response.[]. });
response.[]..;
}
Output
- Automatic OpenAI tracing with zero code changes
- Manual tracing for RAG and complex pipelines
- Streaming response tracking
- Linked parent-child trace hierarchy
Error Handling
| Issue | Cause | Solution |
|---|
| Missing generations | Wrapper not applied | Use observeOpenAI() wrapper |
| Orphaned spans | Missing .end() call | Always end spans in finally block |
| No token usage | Stream without usage | Use stream_options: {include_usage: true} |
| Broken hierarchy | Missing parent link | Pass langfuseParent option |
Examples
Anthropic Claude Tracing
import Anthropic from "@anthropic-ai/sdk";
import { Langfuse } from "langfuse";
const anthropic = new Anthropic();
const langfuse = new Langfuse();
async function callClaude(prompt: string) {
const trace = langfuse.trace({ name: "claude-call", input: { prompt } });
const generation = trace.generation({
name: "claude-response",
model: "claude-3-sonnet-20240229",
input: [{ role: "user", content: prompt }],
});
const response = await anthropic.messages.create({
model: "claude-3-sonnet-20240229",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
});
generation.end({
output: response.content[0].text,
: {
: response..,
: response..,
},
});
response.[].;
}
LangChain Integration
from langchain.callbacks import LangfuseCallbackHandler
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage
langfuse_handler = LangfuseCallbackHandler()
llm = ChatOpenAI(callbacks=[langfuse_handler])
response = llm([HumanMessage(content="Hello!")])
Tool/Function Calling Tracing
async function tracedToolCall(trace: Trace, toolName: string, args: any) {
const span = trace.span({
name: `tool/${toolName}`,
input: args,
metadata: { type: "tool-call" },
});
try {
const result = await executeTool(toolName, args);
span.end({ output: result });
return result;
} catch (error) {
span.end({
level: "ERROR",
statusMessage: String(error),
});
throw error;
}
}
Resources
Next Steps
For evaluation and scoring workflows, see langfuse-core-workflow-b.