Spaces:
Running
Running
| """ | |
| 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]: | |
| 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 | |