Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions backend/app/core/langfuse/langfuse.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import threading
import uuid
from collections.abc import Callable
from functools import wraps
Expand All @@ -9,6 +10,7 @@
from langfuse import Langfuse, LangfuseOtelSpanAttributes
from langfuse._client.span import LangfuseGeneration, LangfuseSpan
from langfuse.api.core.api_error import ApiError
from opentelemetry.sdk.trace import TracerProvider

from app.models.llm import (
AudioOutput,
Expand All @@ -20,6 +22,25 @@

logger = logging.getLogger(__name__)

_LANGFUSE_TRACER_PROVIDER: TracerProvider | None = None
_LANGFUSE_TRACER_PROVIDER_LOCK = threading.Lock()


def get_langfuse_tracer_provider() -> TracerProvider:
"""Isolated OTel provider for Langfuse so LLM spans never reach Sentry.

Sentry's SpanProcessor sits on the global provider and exports every span
with no per-project filter; sharing it surfaces each Langfuse span in Sentry
as a duplicate root trace. Langfuse's own processors filter by public_key, so
one shared isolated provider stays multi-tenant safe.
"""
global _LANGFUSE_TRACER_PROVIDER
if _LANGFUSE_TRACER_PROVIDER is None:
with _LANGFUSE_TRACER_PROVIDER_LOCK:
if _LANGFUSE_TRACER_PROVIDER is None:
_LANGFUSE_TRACER_PROVIDER = TracerProvider()
return _LANGFUSE_TRACER_PROVIDER


def format_langfuse_error(exc: Exception) -> str:
"""Return a concise message for a Langfuse SDK exception.
Expand Down Expand Up @@ -153,6 +174,7 @@ def __init__(
secret_key=credentials["secret_key"],
host=credentials["host"],
tracing_enabled=True, # This ensures the client is active
tracer_provider=get_langfuse_tracer_provider(),
)
except Exception as e:
logger.warning(
Expand Down Expand Up @@ -311,6 +333,7 @@ def wrapper(
public_key=credentials.get("public_key"),
secret_key=credentials.get("secret_key"),
host=credentials.get("host"),
tracer_provider=get_langfuse_tracer_provider(),
)
logger.info(
f"[observe_llm_execution] Tracing enabled | session_id={session_id or 'auto'}"
Expand Down
3 changes: 3 additions & 0 deletions backend/app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,14 @@ def get_anthropic_client(session: Session, org_id: int, project_id: int) -> Anth


def _build_langfuse_client(credentials: dict[str, Any]) -> Langfuse:
from app.core.langfuse.langfuse import get_langfuse_tracer_provider

return Langfuse(
public_key=credentials["public_key"],
secret_key=credentials["secret_key"],
host=credentials["host"],
timeout=60,
tracer_provider=get_langfuse_tracer_provider(),
)


Expand Down
Loading