langfuse

A setup guide for adding LangFuse tracing to FastAPI applications that use LangChain or LangGraph, so requests and model operations can be observed.

In plain words
What is it for?
Use it to install the required dependencies, configure the LangChain callback handler, and troubleshoot the documented auth_check error.
Why use it?
It addresses common tracing setup mistakes, including using the wrong callback class or calling a method that does not exist.

Cursor rule

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add rules/jondoescoding/jondoescoding-coding-rules/langfuse
Clone the repo
git clone --depth 1 https://github.com/jondoescoding/jondoescoding-coding-rules
Per session 0 Nothing until a file matches its globs; then the whole rule loads.
When invoked 3,539 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 1 finding. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.00000 $0.03539
Opus 5 $0.00000 $0.01769
Sonnet 5 $0.00000 $0.00708
Haiku 4.5 $0.00000 $0.00354

Measured yesterday against content hash 02db3c5711c0, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

langfuse scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured yesterday.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Makes network callslowCapability

Not a fault in itself. Listed so you know the mod talks to something, and to what.

curl -X POST "http://localhost:8000/api/v0/chat_with_rag_enabled" \
templates/cursor-rules/python/llm/observability/langfuse.mdc · 496 lines

How it starts

The opening of the file, as written. The whole thing — 496 lines — stays where its author put it; the contents beside it link to each section on GitHub.

LangFuse Tracing Setup Guide for FastAPI + LangChain/LangGraph

This guide provides step-by-step instructions for implementing comprehensive LangFuse tracing in a FastAPI application using LangChain/LangGraph agents.

🎯 Overview

LangFuse provides observability for LLM applications through automatic tracing of LangChain operations. This guide covers the CallbackHandler method which integrates naturally with LangChain's callback system.

🚨 Critical Fix: CallbackHandler Auth Check Error

IMPORTANT: If you encounter 'LangchainCallbackHandler' object has no attribute 'auth_check' error:

# ❌ WRONG - Don't do this with langfuse.langchain import
from langfuse.langchain import CallbackHandler
handler = CallbackHandler()
handler.auth_check()  # This will fail!

# ✅ CORRECT - Remove auth_check() call entirely
from langfuse.langchain import CallbackHandler
handler = CallbackHandler()  # No auth_check needed

Root Cause: The CallbackHandler from langfuse.langchain doesn't have auth_check() method. Only the generic CallbackHandler from langfuse.callback has this method.

📦 Step 1: Install Dependencies

Add to your pyproject.toml:

[project]
dependencies = [
    "langfuse",
    # ... other dependencies
]

⚙️ Step 2: Environment Configuration

Add to your settings class (typically in src/utils/config.py):

# LangFuse Configuration
LANGFUSE_PUBLIC_KEY: str = os.getenv("LANGFUSE_PUBLIC_KEY", "")
LANGFUSE_SECRET_KEY: str = os.getenv("LANGFUSE_SECRET_KEY", "")
LANGFUSE_HOST: str = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")

Add to your .env file:

LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://cloud.langfuse.com

🔧 Step 3: Create LangFuse Configuration Module

Create src/core/langfuse_config.py:

"""
LangFuse tracing configuration and utilities for FastAPI + LangChain applications.
"""

# Python Standard Library
import time
import logging
from typing import Optional, Dict, Any
from contextlib import asynccontextmanager

# Third-Party Packages
from langfuse.langchain import CallbackHandler
from langfuse import Langfuse

# Local Imports
from utils.config import get_settings

logger = logging.getLogger(__name__)

def get_langfuse_client() -> Optional[Langfuse]:
    """Get initialized LangFuse client"""
    try:
        settings = get_settings()
        
        if not settings.LANGFUSE_PUBLIC_KEY or not settings.LANGFUSE_SECRET_KEY:
            logger.warning("LangFuse keys not configured, tracing disabled")
            return None
        
        client = Langfuse(
            public_key=settings.LANGFUSE_PUBLIC_KEY,
            secret_key=settings.LANGFUSE_SECRET_KEY,
            host=settings.LANGFUSE_HOST
        )
        
        # Test connection (client has auth_check, handler doesn't)
        try:
            client.auth_check()
            logger.info("LangFuse connection verified successfully")
            return client
        except Exception as e:
            logger.error(f"LangFuse connection failed: {e}")
            return None
            
    except Exception as e:
        logger.error(f"Failed to initialize LangFuse client: {e}")
        return None

def get_langfuse_handler() -> Optional[CallbackHandler]:
    """Get initialized LangFuse CallbackHandler"""
    try:
        settings = get_settings()
        
        if not settings.LANGFUSE_PUBLIC_KEY or not settings.LANGFUSE_SECRET_KEY:
            logger.warning("LangFuse keys not configured, callback handler disabled")
            return None
        
        # CRITICAL: Don't call auth_check() on CallbackHandler from langfuse.langchain
        handler = CallbackHandler()
        logger.info("LangFuse CallbackHandler initialized successfully")
        
        return handler
        
    except Exception as e:
        logger.error(f"Failed to create LangFuse CallbackHandler: {e}")
        return None

def get_langfuse_config(
    conversation_id: Optional[str] = None,
    endpoint_name: Optional[str] = None,
    trace_name: Optional[str] = None,
    additional_metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
    """
    Get config dict with CallbackHandler for LangChain/LangGraph
    
    Args:
        conversation_id: Unique conversation identifier
        endpoint_name: API endpoint name for tagging
        trace_name: Custom trace name
        additional_metadata: Extra metadata to include
    
    Returns:
        Config dictionary for LangChain/LangGraph agents
    """
    try:
        settings = get_settings()
        handler = get_langfuse_handler()
        
        if not handler:
            return {"callbacks": []}
        
        # Build metadata
        metadata = {
            "environment": settings.ENVIRONMENT.lower(),
            "timestamp": time.time()
        }
        
        if conversation_id:
            metadata["conversation_id"] = conversation_id
        
        if endpoint_name:
            metadata["endpoint"] = endpoint_name
        
        if additional_metadata:
            metadata.update(additional_metadata)
        
        # Build tags
        tags = [
            f"environment:{settings.ENVIRONMENT.lower()}",
        ]
        
        if endpoint_name:
            tags.append(f"endpoint:{endpoint_name}")
        
        # Configure the handler with trace context
        config = {
            "callbacks": [handler],
            "metadata": metadata,
            "tags": tags
        }
        
        # Add trace name if provided
        if trace_name:
            config["run_name"] = trace_name
        
        return config
        
    except Exception as e:
        logger.error(f"Failed to create LangFuse config: {e}")
        return {"callbacks": []}

@asynccontextmanager
async def langfuse_trace_context(
    trace_name: str,
    conversation_id: Optional[str] = None,
    endpoint_name: Optional[str] = None,
    additional_metadata: Optional[Dict[str, Any]] = None
):
    """
    Context manager for LangFuse trace with custom metrics
    
    Usage:
        async with langfuse_trace_context("rag_chat", conversation_id="123") as metrics:
            # Your agent code here
            result = await agent.ainvoke(input, config=metrics.config)
    """
    class Metrics:
        def __init__(self):
            self.start_time = time.time()
            self.config = get_langfuse_config(
                conversation_id=conversation_id,
                endpoint_name=endpoint_name,
                trace_name=trace_name,
                additional_metadata=additional_metadata
            )
        
        def add_tagger_timing(self, tagger_name: str, duration: float):
            """Track individual AI tagger performance"""
            logger.info(f"Tagger {tagger_name} completed in {duration:.2f}s")
        
        def add_error(self, error_type: str, error_message: str):
            """Track errors for failure rate calculation"""
            logger.error(f"Error in trace: {error_type} - {error_message}")
    
    metrics = Metrics()
    
    try:
        yield metrics
    except Exception as e:
        logger.error(f"Error in LangFuse trace context: {e}")
        raise
    finally:
        total_time = time.time() - metrics.start_time
        logger.info(f"Trace '{trace_name}' completed in {total_time:.2f}s")

Read the full file on GitHub · 496 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. yesterday First seen · 496 lines · 0 tokens per session scan A 02db3c5711c0

Subscribe to this mod's changes

langfuse is a cursor rule published in the GitHub repository jondoescoding/jondoescoding-coding-rules (2 stars, last pushed 1mo ago), licensed MIT. It costs nothing until one of its globs matches a file; then it loads 3,539 tokens. A static security scan graded it A with 1 finding (makes network calls). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.