Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
processor.py244 linesDownload Raw Back to otel
1"""OpenTelemetry span processor and exporter for LangSmith."""2 3import logging4import warnings5from typing import Any, Optional6from urllib.parse import urljoin7 8from langsmith import utils as ls_utils9 10try:11    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter12    from opentelemetry.sdk.trace.export import BatchSpanProcessor13 14    OTEL_AVAILABLE = True15except ImportError:16    warnings.warn(17        "OpenTelemetry packages are not installed. "18        "Install optional OpenTelemetry dependencies with: "19        "pip install langsmith[otel]",20        UserWarning,21        stacklevel=2,22    )23 24    class OTLPSpanExporter:  # type: ignore[no-redef]25        """Mock otlp span exporter class."""26 27        def __init__(self, *args, **kwargs):28            """Mock init method."""29            raise ImportError(30                "OpenTelemetry packages are not installed. "31                "Install optional OpenTelemetry dependencies with: "32                "pip install langsmith[otel]"33            )34 35    class BatchSpanProcessor:  # type: ignore[no-redef]36        """Mock batch span processor class."""37 38        def __init__(self, *args, **kwargs):39            """Mock init method."""40            raise ImportError(41                "OpenTelemetry packages are not installed. "42                "Install optional OpenTelemetry dependencies with: "43                "pip install langsmith[otel]"44            )45 46    class trace:47        """Mock trace class."""48 49        @staticmethod50        def get_tracer_provider():51            """Mock get tracer provider method."""52            raise ImportError(53                "OpenTelemetry packages are not installed. "54                "Install optional OpenTelemetry dependencies with: "55                "pip install langsmith[otel]"56            )57 58    OTEL_AVAILABLE = False59 60LANGSMITH_METADATA_PREFIX = "langsmith.metadata"61 62 63class OtelExporter(OTLPSpanExporter):64    """A subclass of `OTLPSpanExporter` configured for LangSmith.65 66    Environment Variables:67 68    - `LANGSMITH_API_KEY`: Your LangSmith API key.69    - `LANGSMITH_ENDPOINT`: Base URL for LangSmith API (defaults to `https://api.smith.langchain.com`).70    - `LANGSMITH_PROJECT`: Project identifier.71    """72 73    def __init__(74        self,75        url: Optional[str] = None,76        api_key: Optional[str] = None,77        project: Optional[str] = None,78        headers: Optional[dict[str, str]] = None,79        **kwargs,80    ):81        """Initialize the `OtelExporter`.82 83        Args:84            url: OTLP endpoint URL. Defaults to `{LANGSMITH_ENDPOINT}/otel/v1/traces`.85            api_key: LangSmith API key. Defaults to `LANGSMITH_API_KEY` env var.86            parent: Parent identifier (e.g., `'project_name:test'`).87 88                Defaults to `LANGSMITH_PARENT` env var.89            headers: Additional headers to include in requests.90            **kwargs: Additional arguments passed to `OTLPSpanExporter`.91        """92        base_url = ls_utils.get_api_url(None)93        # Ensure base_url ends with / for proper joining94        if not base_url.endswith("/"):95            base_url += "/"96        endpoint = url or urljoin(base_url, "otel/v1/traces")97        api_key = api_key or ls_utils.get_api_key(None)98        project = project or ls_utils.get_tracer_project()99        headers = headers or {}100 101        if not api_key:102            raise ValueError(103                "API key is required. Provide it via api_key parameter or "104                "LANGSMITH_API_KEY environment variable."105            )106 107        if not project:108            project = "default"109            logging.info(110                "No project specified, using default. "111                "Configure with LANGSMITH_PROJECT environment variable or "112                "project parameter."113            )114 115        exporter_headers = {116            "x-api-key": api_key,117            **headers,118        }119 120        if project:121            exporter_headers["Langsmith-Project"] = project122 123        self.project = project124 125        super().__init__(endpoint=endpoint, headers=exporter_headers, **kwargs)126 127 128class OtelSpanProcessor:129    """A span processor for adding LangSmith to OpenTelemetry setups.130 131    This class combines the `OtelExporter` and `BatchSpanProcessor`132    into a single processor that can be added to any `TracerProvider`.133 134    Use this when:135 136    1. You already have OpenTelemetry initialized with other tools137    2. You want to add LangSmith alongside existing OTEL exporters138 139    Examples:140        # Fresh OpenTelemetry setup (LangSmith only):141        from langsmith.integrations.otel import configure142        configure(api_key="your-key", project="your-project")143 144        # Add LangSmith to existing OpenTelemetry setup:145        from opentelemetry import trace146        from langsmith.integrations.otel.processor import OtelSpanProcessor147 148        # Get your existing TracerProvider (already set by other tools)149        provider = trace.get_tracer_provider()150 151        # Add LangSmith processor alongside existing processors152        langsmith_processor = OtelSpanProcessor(153            project="your-project",154        )155        provider.add_span_processor(langsmith_processor)156    """157 158    def __init__(159        self,160        api_key: Optional[str] = None,161        project: Optional[str] = None,162        url: Optional[str] = None,163        headers: Optional[dict[str, str]] = None,164        SpanProcessor: Optional[type] = None,165    ):166        """Initialize the `OtelSpanProcessor`.167 168        Args:169            api_key: LangSmith API key. Defaults to `LANGSMITH_API_KEY` env var.170            project: Project identifier. Defaults to `LANGSMITH_PROJECT` env var.171            url: Base URL for LangSmith API. Defaults to `LANGSMITH_ENDPOINT` env var172                or `https://api.smith.langchain.com`.173            headers: Additional headers to include in requests.174            SpanProcessor: Optional span processor class. Defaults to175                `BatchSpanProcessor`.176        """177        # Create the exporter178        # Convert url to the full endpoint URL that OtelExporter expects179        exporter_url = None180        if url:181            exporter_url = f"{url.rstrip('/')}/otel/v1/traces"182 183        self._exporter = OtelExporter(184            url=exporter_url, api_key=api_key, project=project, headers=headers185        )186 187        # Create the processor chain188        if not OTEL_AVAILABLE:189            raise ImportError(190                "OpenTelemetry packages are not installed. "191                "Install optional OpenTelemetry dependencies with: "192                "pip install langsmith[otel]"193            )194 195        if SpanProcessor is None:196            SpanProcessor = BatchSpanProcessor197 198        self._processor = SpanProcessor(self._exporter)199        self._metadata: dict[str, Any] = {}200 201    def set_metadata(self, metadata: dict[str, Any]) -> None:202        """Set metadata attributes to propagate to all spans.203 204        Use this to ensure metadata like ``thread_id`` appears on every span205        in a trace, not just the root span.  This is required for LangSmith206        features (e.g. the threads view) that query runs by metadata fields.207        """208        self._metadata = metadata.copy()209 210    def on_start(self, span, parent_context=None):211        """Forward span start events to the inner processor."""212        if self._metadata:213            for key, value in self._metadata.items():214                if value is not None:215                    span.set_attribute(f"{LANGSMITH_METADATA_PREFIX}.{key}", value)216        self._processor.on_start(span, parent_context)217 218    def on_end(self, span):219        """Forward span end events to the inner processor."""220        self._processor.on_end(span)221 222    def _on_ending(self, span):223        """Forward span ending events to the inner processor."""224        if hasattr(self._processor, "_on_ending"):225            self._processor._on_ending(span)226 227    def shutdown(self):228        """Shutdown processor."""229        self._processor.shutdown()230 231    def force_flush(self, timeout_millis=30000):232        """Force flush the inner processor."""233        return self._processor.force_flush(timeout_millis)234 235    @property236    def exporter(self):237        """The underlying OtelExporter."""238        return self._exporter239 240    @property241    def processor(self):242        """The underlying span processor."""243        return self._processor244 
codekingpro/portable-devtools · Team Ai