PDF-Assit_RAG / backend /app /rag /tracing.py
Param20h's picture
deploy: pure backend API with keywords fix
7c46845 unverified
Raw
History Blame Contribute Delete
3.45 kB
"""
Optional LangSmith tracing helpers for the RAG pipeline.
Safe to import even when LangSmith is not installed or configured.
"""
import logging
import os
from functools import wraps
from typing import Any, Callable, Optional
from app.config import get_settings
logger = logging.getLogger(__name__)
settings = get_settings()
try:
from langsmith import traceable as _langsmith_traceable
except Exception: # pragma: no cover - optional dependency safety
_langsmith_traceable = None
def configure_langsmith() -> bool:
"""Configure LangSmith environment variables when tracing is enabled."""
if not settings.LANGSMITH_TRACING:
return False
if not settings.LANGSMITH_API_KEY:
logger.warning("LangSmith tracing enabled but LANGSMITH_API_KEY is not set; tracing disabled.")
return False
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = settings.LANGSMITH_API_KEY
os.environ["LANGSMITH_ENDPOINT"] = settings.LANGSMITH_ENDPOINT
os.environ["LANGSMITH_PROJECT"] = settings.LANGSMITH_PROJECT
return _langsmith_traceable is not None
LANGSMITH_ENABLED = configure_langsmith()
def _sanitize_metadata(metadata: Optional[dict[str, Any]]) -> dict[str, Any]:
return {key: value for key, value in (metadata or {}).items() if value is not None}
def _build_traceable(name: str, run_type: str, metadata: Optional[dict[str, Any]] = None):
"""Build a LangSmith traceable decorator safely across versions."""
if _langsmith_traceable is None:
return None
sanitized = _sanitize_metadata(metadata)
try:
return _langsmith_traceable(
name=name,
run_type=run_type,
metadata=sanitized or None,
)
except TypeError:
return _langsmith_traceable(name=name, run_type=run_type)
def trace_call(
name: str,
fn: Callable[..., Any],
*args: Any,
run_type: str = "chain",
metadata: Optional[dict[str, Any]] = None,
**kwargs: Any,
) -> Any:
"""Execute a callable with LangSmith tracing when available."""
if not LANGSMITH_ENABLED:
return fn(*args, **kwargs)
decorator = _build_traceable(name, run_type, metadata)
if decorator is None:
return fn(*args, **kwargs)
traced_fn = decorator(fn)
return traced_fn(*args, **kwargs)
def trace_function(
name: str,
*,
run_type: str = "chain",
metadata_factory: Optional[Callable[..., dict[str, Any]]] = None,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""Decorator wrapper that becomes a no-op when LangSmith is disabled."""
def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
@wraps(fn)
def wrapped(*args: Any, **kwargs: Any) -> Any:
metadata = None
if metadata_factory:
try:
metadata = metadata_factory(*args, **kwargs)
except Exception:
logger.warning(
"Metadata factory failed for trace %r; continuing without metadata.",
name,
exc_info=True,
)
metadata = {}
return trace_call(
name,
fn,
*args,
run_type=run_type,
metadata=metadata,
**kwargs,
)
return wrapped
return decorator