| name | langfuse-sdk-patterns |
| description | Langfuse SDK best practices, patterns, and idiomatic usage.
Use when learning Langfuse SDK patterns, implementing proper tracing,
or following best practices for LLM observability.
Trigger with phrases like "langfuse patterns", "langfuse best practices",
"langfuse SDK guide", "how to use langfuse", "langfuse idioms".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Langfuse SDK Patterns
Overview
Best practices and idiomatic patterns for using the Langfuse SDK effectively.
Prerequisites
- Completed
langfuse-install-auth setup
- Understanding of async/await patterns
- Familiarity with LLM application structure
Core Concepts
Trace Hierarchy
Trace (top-level, represents complete operation)
โโโ Span (child operation, any processing step)
โ โโโ Span (nested spans allowed)
โ โโโ Generation (LLM call)
โโโ Generation (LLM call)
โโโ Event (point-in-time occurrence)
Instructions
Pattern 1: Singleton Client Instance
import { Langfuse } from "langfuse";
let langfuseInstance: Langfuse | null = null;
export function getLangfuse(): Langfuse {
if (!langfuseInstance) {
langfuseInstance = new Langfuse({
publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
secretKey: process.env.LANGFUSE_SECRET_KEY!,
baseUrl: process.env.LANGFUSE_HOST,
});
}
return langfuseInstance;
}
export async function shutdownLangfuse(): Promise<void> {
if (langfuseInstance) {
await langfuseInstance.shutdownAsync();
langfuseInstance = null;
}
}
Pattern 2: Proper Trace Lifecycle
import { getLangfuse } from "./lib/langfuse";
async function handleRequest(request: Request) {
const langfuse = getLangfuse();
const trace = langfuse.trace({
name: "api/chat",
userId: request.userId,
sessionId: request.sessionId,
input: request.body,
metadata: {
endpoint: "/api/chat",
method: "POST",
},
});
try {
const result = await processRequest(trace, request);
trace.update({
output: result,
metadata: { status: "success" },
});
return result;
} catch (error) {
trace.update({
output: { error: String(error) },
level: "ERROR",
statusMessage: String(error),
});
error;
}
}
Pattern 3: Nested Spans for Complex Operations
async function processRequest(
trace: ReturnType<typeof langfuse.trace>,
request: Request
) {
const processSpan = trace.span({
name: "process-request",
input: request.body,
});
const validateSpan = processSpan.span({
name: "validate-input",
input: request.body,
});
const validatedInput = await validateInput(request.body);
validateSpan.end({ output: validatedInput });
const retrieveSpan = processSpan.span({
name: "retrieve-context",
input: { query: validatedInput.query },
});
const context = await retrieveContext(validatedInput.query);
retrieveSpan.end({
output: { documentCount: context.length },
metadata: { source: "vector-db" },
});
generation = processSpan.({
: ,
: ,
: {
: (validatedInput, context),
},
});
response = (validatedInput, context);
generation.({
: response.,
: {
: response..,
: response..,
},
});
processSpan.({ : response });
response;
}
Pattern 4: Decorators for Clean Code (Python)
from langfuse.decorators import observe, langfuse_context
@observe()
def process_request(user_input: str) -> str:
"""Automatically creates a trace with function name."""
langfuse_context.update_current_observation(
metadata={"input_length": len(user_input)}
)
validated = validate_input(user_input)
context = retrieve_context(validated)
response = generate_response(validated, context)
return response
@observe()
def validate_input(user_input: str) -> dict:
"""Creates a child span automatically."""
return {"query": user_input.strip(), "valid": True}
@observe()
def retrieve_context(query: dict) -> list:
"""Another child span."""
langfuse_context.update_current_observation(
metadata={"query": query}
)
return ["context1", "context2"]
@observe(as_type="generation")
def generate_response(query: dict, context: list) -> str:
"""Creates a generation observation."""
langfuse_context.update_current_observation(
model=,
model_parameters={: },
usage={: , : },
)
Pattern 5: Session and User Tracking
function createConversationTrace(
userId: string,
sessionId: string,
turn: number
) {
return langfuse.trace({
name: "conversation-turn",
userId,
sessionId,
metadata: {
turn,
timestamp: new Date().toISOString(),
},
tags: ["conversation"],
});
}
const trace = createConversationTrace(
"user-123",
"session-abc",
3
);
Pattern 6: Scores and Evaluation
const trace = langfuse.trace({ name: "scored-operation" });
langfuse.score({
traceId: trace.id,
name: "accuracy",
value: 0.95,
comment: "High accuracy response",
});
langfuse.score({
traceId: trace.id,
name: "user-feedback",
value: 1,
comment: "User thumbs up",
});
langfuse.score({
traceId: trace.id,
observationId: generation.id,
name: "relevance",
value: 0.8,
});
Output
- Singleton client pattern for consistent tracing
- Proper trace lifecycle management
- Nested spans for complex operations
- Clean decorator-based tracing (Python)
- Session and user tracking
- Evaluation and scoring integration
Error Handling
| Pattern | Issue | Best Practice |
|---|
| Client creation | Multiple instances | Use singleton pattern |
| Trace updates | Missing outputs | Always update on success/error |
| Span nesting | Orphaned spans | Always call .end() |
| Flush timing | Lost data | Use shutdownAsync() on exit |
| Scoring | Invalid values | Use 0-1 range for consistency |
Examples
Complete TypeScript Pattern
import { Langfuse } from "langfuse";
const langfuse = new Langfuse();
interface ChatRequest {
userId: string;
sessionId: string;
message: string;
}
async function chat(request: ChatRequest) {
const trace = langfuse.trace({
name: "chat",
userId: request.userId,
sessionId: request.sessionId,
input: { message: request.message },
});
const span = trace.span({ name: "process" });
try {
const generation = span.generation({
name: "llm-call",
model: "gpt-4",
input: [{ role: "user", content: request.message }],
});
const response = await callOpenAI(request.message);
generation.({
: response.,
: response.,
});
span.({ : { : response. } });
trace.({ : { : response. } });
response.;
} (error) {
span.({ : , : (error) });
trace.({ : , : { : (error) } });
error;
}
}
Resources
Next Steps
For core tracing workflows, see langfuse-core-workflow-a.