feat: implement LangfuseTracer (#3398)
* feat: implement langfuse tracer class for event monitoring * feat: integrate langfuse trace class into trace service
This commit is contained in:
parent
ee6fc45f68
commit
b5fc559d65
2 changed files with 154 additions and 0 deletions
137
src/backend/base/langflow/services/tracing/langfuse.py
Normal file
137
src/backend/base/langflow/services/tracing/langfuse.py
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
import os
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||||
|
from uuid import UUID
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from langflow.services.tracing.base import BaseTracer
|
||||||
|
from langflow.services.tracing.schema import Log
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from langflow.graph.vertex.base import Vertex
|
||||||
|
from langchain.callbacks.base import BaseCallbackHandler
|
||||||
|
|
||||||
|
|
||||||
|
class LangFuseTracer(BaseTracer):
|
||||||
|
flow_id: str
|
||||||
|
|
||||||
|
def __init__(self, trace_name: str, trace_type: str, project_name: str, trace_id: UUID):
|
||||||
|
self.project_name = project_name
|
||||||
|
self.trace_name = trace_name
|
||||||
|
self.trace_type = trace_type
|
||||||
|
self.trace_id = trace_id
|
||||||
|
self.flow_id = trace_name.split(" - ")[-1]
|
||||||
|
self.last_span = None
|
||||||
|
self.spans: dict = {}
|
||||||
|
self._ready: bool = self.setup_langfuse()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ready(self):
|
||||||
|
return self._ready
|
||||||
|
|
||||||
|
def setup_langfuse(self) -> bool:
|
||||||
|
try:
|
||||||
|
from langfuse import Langfuse
|
||||||
|
from langfuse.callback.langchain import LangchainCallbackHandler
|
||||||
|
|
||||||
|
config = self._get_config()
|
||||||
|
if not all(config.values()):
|
||||||
|
raise ValueError("Missing Langfuse configuration")
|
||||||
|
|
||||||
|
self._client = Langfuse(**config)
|
||||||
|
self.trace = self._client.trace(id=str(self.trace_id), name=self.flow_id)
|
||||||
|
|
||||||
|
config |= {
|
||||||
|
"trace_name": self.flow_id,
|
||||||
|
"stateful_client": self.trace,
|
||||||
|
"update_stateful_client": True,
|
||||||
|
}
|
||||||
|
self._callback = LangchainCallbackHandler(**config)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
logger.error("Could not import langfuse. Please install it with `pip install langfuse`.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Error setting up LangSmith tracer: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_trace(
|
||||||
|
self,
|
||||||
|
trace_id: str,
|
||||||
|
trace_name: str,
|
||||||
|
trace_type: str,
|
||||||
|
inputs: Dict[str, Any],
|
||||||
|
metadata: Dict[str, Any] | None = None,
|
||||||
|
vertex: Optional["Vertex"] = None,
|
||||||
|
):
|
||||||
|
start_time = datetime.utcnow()
|
||||||
|
if not self._ready:
|
||||||
|
return
|
||||||
|
|
||||||
|
_metadata: dict = {}
|
||||||
|
_metadata |= {"trace_type": trace_type} if trace_type else {}
|
||||||
|
_metadata |= metadata if metadata else {}
|
||||||
|
|
||||||
|
_name = trace_name.removesuffix(f" ({trace_id})")
|
||||||
|
content_span = {
|
||||||
|
"name": _name,
|
||||||
|
"input": inputs,
|
||||||
|
"metadata": _metadata,
|
||||||
|
"start_time": start_time,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.last_span:
|
||||||
|
span = self.last_span.span(**content_span)
|
||||||
|
else:
|
||||||
|
span = self.trace.span(**content_span)
|
||||||
|
|
||||||
|
self.last_span = span
|
||||||
|
self.spans[trace_id] = span
|
||||||
|
|
||||||
|
def end_trace(
|
||||||
|
self,
|
||||||
|
trace_id: str,
|
||||||
|
trace_name: str,
|
||||||
|
outputs: Dict[str, Any] | None = None,
|
||||||
|
error: Exception | None = None,
|
||||||
|
logs: list[Log | dict] = [],
|
||||||
|
):
|
||||||
|
end_time = datetime.utcnow()
|
||||||
|
if not self._ready:
|
||||||
|
return
|
||||||
|
|
||||||
|
span = self.spans.get(trace_id, None)
|
||||||
|
if span:
|
||||||
|
_output: dict = {}
|
||||||
|
_output |= outputs if outputs else {}
|
||||||
|
_output |= {"error": str(error)} if error else {}
|
||||||
|
_output |= {"logs": logs} if logs else {}
|
||||||
|
content = {"output": _output, "end_time": end_time}
|
||||||
|
span.update(**content)
|
||||||
|
|
||||||
|
def end(
|
||||||
|
self,
|
||||||
|
inputs: dict[str, Any],
|
||||||
|
outputs: Dict[str, Any],
|
||||||
|
error: Exception | None = None,
|
||||||
|
metadata: dict[str, Any] | None = None,
|
||||||
|
):
|
||||||
|
if not self._ready:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._client.flush()
|
||||||
|
|
||||||
|
def get_langchain_callback(self) -> Optional["BaseCallbackHandler"]:
|
||||||
|
if not self._ready:
|
||||||
|
return None
|
||||||
|
return None # self._callback
|
||||||
|
|
||||||
|
def _get_config(self):
|
||||||
|
secret_key = os.getenv("LANGFUSE_SECRET_KEY", None)
|
||||||
|
public_key = os.getenv("LANGFUSE_PUBLIC_KEY", None)
|
||||||
|
host = os.getenv("LANGFUSE_HOST", None)
|
||||||
|
return {"secret_key": secret_key, "public_key": public_key, "host": host}
|
||||||
|
|
@ -32,6 +32,12 @@ def _get_langwatch_tracer():
|
||||||
return LangWatchTracer
|
return LangWatchTracer
|
||||||
|
|
||||||
|
|
||||||
|
def _get_langfuse_tracer():
|
||||||
|
from langflow.services.tracing.langfuse import LangFuseTracer
|
||||||
|
|
||||||
|
return LangFuseTracer
|
||||||
|
|
||||||
|
|
||||||
class TracingService(Service):
|
class TracingService(Service):
|
||||||
name = "tracing_service"
|
name = "tracing_service"
|
||||||
|
|
||||||
|
|
@ -101,6 +107,7 @@ class TracingService(Service):
|
||||||
await self.start()
|
await self.start()
|
||||||
self._initialize_langsmith_tracer()
|
self._initialize_langsmith_tracer()
|
||||||
self._initialize_langwatch_tracer()
|
self._initialize_langwatch_tracer()
|
||||||
|
self._initialize_langfuse_tracer()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Error initializing tracers: {e}")
|
logger.debug(f"Error initializing tracers: {e}")
|
||||||
|
|
||||||
|
|
@ -127,6 +134,16 @@ class TracingService(Service):
|
||||||
trace_id=self.run_id,
|
trace_id=self.run_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _initialize_langfuse_tracer(self):
|
||||||
|
self.project_name = os.getenv("LANGCHAIN_PROJECT", "Langflow")
|
||||||
|
langfuse_tracer = _get_langfuse_tracer()
|
||||||
|
self._tracers["langfuse"] = langfuse_tracer(
|
||||||
|
trace_name=self.run_name,
|
||||||
|
trace_type="chain",
|
||||||
|
project_name=self.project_name,
|
||||||
|
trace_id=self.run_id,
|
||||||
|
)
|
||||||
|
|
||||||
def set_run_name(self, name: str):
|
def set_run_name(self, name: str):
|
||||||
self.run_name = name
|
self.run_name = name
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue