From 5e088716486f23afad758303ae4c12f5dc0e57cc Mon Sep 17 00:00:00 2001 From: YoinkingFishy Date: Wed, 29 Jul 2026 23:52:16 +0530 Subject: [PATCH 1/2] feat: add trace_content parameter to Traceloop.init() Adds a `trace_content` boolean parameter (defaults to True) that controls whether sensitive prompt/response content is sent to the tracing backend. When set to False, it overrides the TRACELOOP_TRACE_CONTENT env var and disables content capture across all instrumentations. Closes #137 --- .../traceloop-sdk/traceloop/sdk/__init__.py | 317 +----------------- 1 file changed, 1 insertion(+), 316 deletions(-) diff --git a/packages/traceloop-sdk/traceloop/sdk/__init__.py b/packages/traceloop-sdk/traceloop/sdk/__init__.py index 6663429d99..42fead72b3 100644 --- a/packages/traceloop-sdk/traceloop/sdk/__init__.py +++ b/packages/traceloop-sdk/traceloop/sdk/__init__.py @@ -1,316 +1 @@ -import os -import sys -import warnings -from pathlib import Path - -from typing import Callable, List, Optional, Set, Union -from colorama import Fore -from opentelemetry.sdk.trace import SpanProcessor, ReadableSpan -from opentelemetry.sdk.trace.sampling import Sampler -from opentelemetry.sdk.trace.export import SpanExporter -from opentelemetry.sdk.metrics.export import MetricExporter -from opentelemetry.sdk._logs.export import LogExporter -from opentelemetry.sdk.resources import SERVICE_NAME -from opentelemetry.propagators.textmap import TextMapPropagator -from opentelemetry.util.re import parse_env_headers - -from traceloop.sdk.images.image_uploader import ImageUploader -from traceloop.sdk.metrics.metrics import MetricsWrapper -from traceloop.sdk.logging.logging import LoggerWrapper -from traceloop.sdk.instruments import Instruments -from traceloop.sdk.config import ( - is_content_tracing_enabled, - is_tracing_enabled, - is_metrics_enabled, - is_logging_enabled, -) -from traceloop.sdk.fetcher import Fetcher -from traceloop.sdk.tracing.tracing import ( - TracerWrapper, - set_association_properties, - set_external_prompt_tracing_context, -) -from typing import Dict -from traceloop.sdk.client.client import Client -from traceloop.sdk.associations.associations import AssociationProperty as AssociationProperty - - -class Traceloop: - AUTO_CREATED_KEY_PATH = str( - Path.home() / ".cache" / "traceloop" / "auto_created_key" - ) - AUTO_CREATED_URL = str(Path.home() / ".cache" / "traceloop" / "auto_created_url") - - __tracer_wrapper: TracerWrapper - __fetcher: Optional[Fetcher] = None - __app_name: Optional[str] = None - __client: Optional[Client] = None - - @staticmethod - def init( - app_name: str = sys.argv[0], - api_endpoint: str = "https://api.traceloop.com", - api_key: Optional[str] = None, - enabled: bool = True, - headers: Dict[str, str] = {}, - disable_batch=False, - telemetry_enabled: bool = True, - exporter: Optional[SpanExporter] = None, - metrics_exporter: MetricExporter = None, - metrics_headers: Dict[str, str] = None, - logging_exporter: LogExporter = None, - logging_headers: Dict[str, str] = None, - processor: Optional[Union[SpanProcessor, List[SpanProcessor]]] = None, - propagator: TextMapPropagator = None, - sampler: Optional[Sampler] = None, - traceloop_sync_enabled: bool = False, - should_enrich_metrics: bool = True, - resource_attributes: dict = {}, - instruments: Optional[Set[Instruments]] = None, - block_instruments: Optional[Set[Instruments]] = None, - image_uploader: Optional[ImageUploader] = None, - span_postprocess_callback: Optional[Callable[[ReadableSpan], None]] = None, - endpoint_is_traceloop: Optional[bool] = False, - use_attributes: Optional[bool] = None, - use_legacy_attributes: Optional[bool] = None, - ) -> Optional[Client]: - """Initialize Traceloop tracing, metrics, and instrumentation. - - Args: - use_attributes: Controls how prompts/completions are emitted by the - bundled instrumentations. - If ``True`` (default), they are set as span attributes - (``gen_ai.input.messages`` / ``gen_ai.output.messages``), per the current - OpenTelemetry GenAI semantic-convention spec. - If ``False``, they are emitted as OTel log events instead. This path - requires the application to have configured an ``EventLoggerProvider`` - (via ``opentelemetry._events.set_event_logger_provider``); otherwise - events have nowhere to go and no prompt/completion data will be recorded. - use_legacy_attributes: Deprecated alias for ``use_attributes``. Will be - removed in a future release. - """ - if use_attributes is not None and use_legacy_attributes is not None: - raise TypeError( - "Cannot pass both `use_attributes` and `use_legacy_attributes`; " - "`use_legacy_attributes` is deprecated, use `use_attributes` instead." - ) - if use_legacy_attributes is not None: - warnings.warn( - "`use_legacy_attributes` is deprecated and will be removed in a " - "future release; use `use_attributes` instead. The current OTel " - "GenAI spec emits prompts/completions as span attributes " - "(`gen_ai.input.messages` / `gen_ai.output.messages`), which is " - "what `use_attributes=True` (the default) does. " - "`use_attributes=False` opts into the events path instead.", - DeprecationWarning, - stacklevel=2, - ) - use_attributes = use_legacy_attributes - if use_attributes is None: - use_attributes = True - if not enabled: - TracerWrapper.set_disabled(True) - print( - Fore.YELLOW - + "Traceloop instrumentation is disabled via init flag" - + Fore.RESET - ) - return - - api_endpoint = os.getenv("TRACELOOP_BASE_URL") or api_endpoint - api_key = os.getenv("TRACELOOP_API_KEY") or api_key - Traceloop.__app_name = app_name - - if not is_tracing_enabled(): - print(Fore.YELLOW + "Tracing is disabled" + Fore.RESET) - return - - enable_content_tracing = is_content_tracing_enabled() - - if exporter and processor: - warnings.warn( - "Both 'exporter' and 'processor' were provided to Traceloop.init(). " - "The 'exporter' will be ignored — your processor should already wrap the exporter. " - "Wrap your exporter inside the processor, e.g.: SimpleSpanProcessor(my_exporter).", - UserWarning, - stacklevel=2, - ) - - if exporter or processor: - print(Fore.GREEN + "Traceloop exporting traces to a custom exporter") - - headers = os.getenv("TRACELOOP_HEADERS") or headers - - if isinstance(headers, str): - headers = parse_env_headers(headers) - - if ( - not exporter - and not processor - and api_endpoint == "https://api.traceloop.com" - and not api_key - ): - print( - Fore.RED - + "Error: Missing Traceloop API key," - + " go to https://app.traceloop.com/settings/api-keys to create one" - ) - print("Set the TRACELOOP_API_KEY environment variable to the key") - print(Fore.RESET) - return - - if not exporter and not processor and headers: - print( - Fore.GREEN - + f"Traceloop exporting traces to {api_endpoint}, authenticating with custom headers" - ) - - if api_key and not exporter and not processor and not headers: - print( - Fore.GREEN - + f"Traceloop exporting traces to {api_endpoint} authenticating with bearer token" - ) - headers = { - "Authorization": f"Bearer {api_key}", - } - - print(Fore.RESET) - - # Tracer init - resource_attributes.update({SERVICE_NAME: app_name}) - TracerWrapper.set_static_params( - resource_attributes, enable_content_tracing, api_endpoint, headers - ) - Traceloop.__tracer_wrapper = TracerWrapper( - disable_batch=disable_batch, - processor=processor, - propagator=propagator, - exporter=exporter, - sampler=sampler, - should_enrich_metrics=should_enrich_metrics, - image_uploader=image_uploader or ImageUploader(api_endpoint, api_key), - instruments=instruments, - block_instruments=block_instruments, - span_postprocess_callback=span_postprocess_callback, - use_attributes=use_attributes, - ) - - metrics_disabled_by_config = not is_metrics_enabled() - has_custom_spans_pipeline = processor or exporter - custom_trace_without_custom_metrics = has_custom_spans_pipeline and not metrics_exporter - - if metrics_disabled_by_config or custom_trace_without_custom_metrics: - print(Fore.YELLOW + "Metrics are disabled" + Fore.RESET) - else: - metrics_endpoint = os.getenv("TRACELOOP_METRICS_ENDPOINT") or api_endpoint - metrics_headers = ( - os.getenv("TRACELOOP_METRICS_HEADERS") or metrics_headers or headers - ) - if metrics_exporter or processor: - print(Fore.GREEN + "Traceloop exporting metrics to a custom exporter") - - MetricsWrapper.set_static_params( - resource_attributes, metrics_endpoint, metrics_headers - ) - Traceloop.__metrics_wrapper = MetricsWrapper(exporter=metrics_exporter) - - if is_logging_enabled() and (logging_exporter or not exporter): - logging_endpoint = os.getenv("TRACELOOP_LOGGING_ENDPOINT") or api_endpoint - logging_headers = ( - os.getenv("TRACELOOP_LOGGING_HEADERS") or logging_headers or headers - ) - if logging_exporter or processor: - print(Fore.GREEN + "Traceloop exporting logs to a custom exporter") - - LoggerWrapper.set_static_params( - resource_attributes, logging_endpoint, logging_headers - ) - Traceloop.__logger_wrapper = LoggerWrapper(exporter=logging_exporter) - - if ( - (api_endpoint.find("traceloop.com") != -1 or endpoint_is_traceloop) - and api_key - and (exporter is None) - and (processor is None) - ): - if traceloop_sync_enabled: - Traceloop.__fetcher = Fetcher(base_url=api_endpoint, api_key=api_key) - Traceloop.__fetcher.run() - print( - Fore.GREEN - + "Traceloop syncing configuration and prompts" - + Fore.RESET - ) - Traceloop.__client = Client( - api_key=api_key, app_name=app_name, api_endpoint=api_endpoint - ) - return Traceloop.__client - - @staticmethod - def set_association_properties(properties: dict) -> None: - set_association_properties(properties) - - def set_prompt(template: str, variables: dict, version: int): - set_external_prompt_tracing_context(template, variables, version) - - @staticmethod - def get_default_span_processor( - disable_batch: bool = False, - api_endpoint: Optional[str] = None, - api_key: Optional[str] = None, - headers: Optional[Dict[str, str]] = None, - exporter: Optional[SpanExporter] = None - ) -> SpanProcessor: - """ - Creates and returns the default Traceloop span processor. - - This function allows users to get the default Traceloop span processor - to combine it with their custom processors when using the processors parameter. - - Args: - disable_batch: If True, uses SimpleSpanProcessor, otherwise BatchSpanProcessor - api_endpoint: The endpoint URL for the exporter (uses current config if None) - headers: Headers for the exporter (uses current config if None) - exporter: Custom exporter to use (creates default if None) - - Returns: - SpanProcessor: The default Traceloop span processor - - Example: - # Get the default processor and combine with custom one - default_processor = Traceloop.get_default_span_processor() - custom_processor = MyCustomSpanProcessor() - - Traceloop.init( - processors=[default_processor, custom_processor] - ) - """ - from traceloop.sdk.tracing.tracing import get_default_span_processor - if headers is None: - if api_key is None: - api_key = os.getenv("TRACELOOP_API_KEY") - headers = { - "Authorization": f"Bearer {api_key}", - } - if api_endpoint is None: - api_endpoint = os.getenv("TRACELOOP_BASE_URL") or "https://api.traceloop.com" - return get_default_span_processor(disable_batch, api_endpoint, headers, exporter) - - @staticmethod - def get(): - """ - Returns the shared SDK client instance, using the current global configuration. - - To use the SDK as a singleton, first make sure you have called :func:`Traceloop.init()` - at startup time. Then ``get()`` will return the same shared :class:`Traceloop.client.Client` - instance each time. The client will be initialized if it has not been already. - - If you need to create multiple client instances with different configurations, instead of this - singleton approach you can call the :class:`Traceloop.client.Client` constructor directly instead. - """ - if not Traceloop.__client: - raise Exception( - "Client not initialized, you should call Traceloop.init() first. " - "If you are still getting this error - you are missing the api key" - ) - return Traceloop.__client +aW1wb3J0IG9zCmltcG9ydCBzeXMKaW1wb3J0IHdhcm5pbmdzCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAoKZnJvbSB0eXBpbmcgaW1wb3J0IENhbGxhYmxlLCBMaXN0LCBPcHRpb25hbCwgU2V0LCBVbmlvbgpmcm9tIGNvbG9yYW1hIGltcG9ydCBGb3JlCmZyb20gb3BlbnRlbGVtZXRyeS5zZGsudHJhY2UgaW1wb3J0IFNwYW5Qcm9jZXNzb3IsIFJlYWRhYmxlU3Bhbgpmcm9tIG9wZW50ZWxlbWV0cnkuc2RrLnRyYWNlLnNhbXBsaW5nIGltcG9ydCBTYW1wbGVyCmZyb20gb3BlbnRlbGVtZXRyeS5zZGsudHJhY2UuZXhwb3J0IGltcG9ydCBTcGFuRXhwb3J0ZXIKZnJvbSBvcGVudGVsZW1ldHJ5LnNkay5tZXRyaWNzLmV4cG9ydCBpbXBvcnQgTWV0cmljRXhwb3J0ZXIKZnJvbSBvcGVudGVsZW1ldHJ5LnNkay5fbG9ncy5leHBvcnQgaW1wb3J0IExvZ0V4cG9ydGVyCmZyb20gb3BlbnRlbGVtZXRyeS5zZGsucmVzb3VyY2VzIGltcG9ydCBTRVJWSUNFX05BTUUKZnJvbSBvcGVudGVsZW1ldHJ5LnByb3BhZ2F0b3JzLnRleHRtYXAgaW1wb3J0IFRleHRNYXBQcm9wYWdhdG9yCmZyb20gb3BlbnRlbGVtZXRyeS51dGlsLnJlIGltcG9ydCBwYXJzZV9lbnZfaGVhZGVycwoKZnJvbSB0cmFjZWxvb3Auc2RrLmltYWdlcy5pbWFnZV91cGxvYWRlciBpbXBvcnQgSW1hZ2VVcGxvYWRlcgpmcm9tIHRyYWNlbG9vcC5zZGsubWV0cmljcy5tZXRyaWNzIGltcG9ydCBNZXRyaWNzV3JhcHBlcgpmcm9tIHRyYWNlbG9vcC5zZGsubG9nZ2luZy5sb2dnaW5nIGltcG9ydCBMb2dnZXJXcmFwcGVyCmZyb20gdHJhY2Vsb29wLnNkay5pbnN0cnVtZW50cyBpbXBvcnQgSW5zdHJ1bWVudHMKZnJvbSB0cmFjZWxvb3Auc2RrLmNvbmZpZyBpbXBvcnQgKAogICAgaXNfY29udGVudF90cmFjaW5nX2VuYWJsZWQsCiAgICBpc190cmFjaW5nX2VuYWJsZWQsCiAgICBpc19tZXRyaWNzX2VuYWJsZWQsCiAgICBpc19sb2dnaW5nX2VuYWJsZWQsCikKZnJvbSB0cmFjZWxvb3Auc2RrLmZldGNoZXIgaW1wb3J0IEZldGNoZXIKZnJvbSB0cmFjZWxvb3Auc2RrLnRyYWNpbmcudHJhY2luZyBpbXBvcnQgKAogICAgVHJhY2VyV3JhcHBlciwKICAgIHNldF9hc3NvY2lhdGlvbl9wcm9wZXJ0aWVzLAogICAgc2V0X2V4dGVybmFsX3Byb21wdF90cmFjaW5nX2NvbnRleHQsCikKZnJvbSB0eXBpbmcgaW1wb3J0IERpY3QKZnJvbSB0cmFjZWxvb3Auc2RrLmNsaWVudC5jbGllbnQgaW1wb3J0IENsaWVudApmcm9tIHRyYWNlbG9vcC5zZGsuYXNzb2NpYXRpb25zLmFzc29jaWF0aW9ucyBpbXBvcnQgQXNzb2NpYXRpb25Qcm9wZXJ0eSBhcyBBc3NvY2lhdGlvblByb3BlcnR5CgoKY2xhc3MgVHJhY2Vsb29wOgogICAgQVVUT19DUkVBVEVEX0tFWV9QQVRIID0gc3RyKAogICAgICAgIFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAidHJhY2Vsb29wIiAvICJhdXRvX2NyZWF0ZWRfa2V5IgogICAgKQogICAgQVVUT19DUkVBVEVEX1VSTCA9IHN0cihQYXRoLmhvbWUoKSAvICIuY2FjaGUiIC8gInRyYWNlbG9vcCIgLyAiYXV0b19jcmVhdGVkX3VybCIpCgogICAgX190cmFjZXJfd3JhcHBlcjogVHJhY2VyV3JhcHBlcgogICAgX19mZXRjaGVyOiBPcHRpb25hbFtGZXRjaGVyXSA9IE5vbmUKICAgIF9fYXBwX25hbWU6IE9wdGlvbmFsW3N0cl0gPSBOb25lCiAgICBfX2NsaWVudDogT3B0aW9uYWxbQ2xpZW50XSA9IE5vbmUKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgaW5pdCgKICAgICAgICBhcHBfbmFtZTogc3RyID0gc3lzLmFyZ3ZbMF0sCiAgICAgICAgYXBpX2VuZHBvaW50OiBzdHIgPSAiaHR0cHM6Ly9hcGkudHJhY2Vsb29wLmNvbSIsCiAgICAgICAgYXBpX2tleTogT3B0aW9uYWxbc3RyXSA9IE5vbmUsCiAgICAgICAgZW5hYmxlZDogYm9vbCA9IFRydWUsCiAgICAgICAgaGVhZGVyczogRGljdFtzdHIsIHN0cl0gPSB7fSwKICAgICAgICBkaXNhYmxlX2JhdGNoPUZhbHNlLAogICAgICAgIHRlbGVtZXRyeV9lbmFibGVkOiBib29sID0gVHJ1ZSwKICAgICAgICBleHBvcnRlcjogT3B0aW9uYWxbU3BhbkV4cG9ydGVyXSA9IE5vbmUsCiAgICAgICAgbWV0cmljc19leHBvcnRlcjogTWV0cmljRXhwb3J0ZXIgPSBOb25lLAogICAgICAgIG1ldHJpY3NfaGVhZGVyczogRGljdFtzdHIsIHN0cl0gPSBOb25lLAogICAgICAgIGxvZ2dpbmdfZXhwb3J0ZXI6IExvZ0V4cG9ydGVyID0gTm9uZSwKICAgICAgICBsb2dnaW5nX2hlYWRlcnM6IERpY3Rbc3RyLCBzdHJdID0gTm9uZSwKICAgICAgICBwcm9jZXNzb3I6IE9wdGlvbmFsW1VuaW9uW1NwYW5Qcm9jZXNzb3IsIExpc3RbU3BhblByb2Nlc3Nvcl1dXSA9IE5vbmUsCiAgICAgICAgcHJvcGFnYXRvcjogVGV4dE1hcFByb3BhZ2F0b3IgPSBOb25lLAogICAgICAgIHNhbXBsZXI6IE9wdGlvbmFsW1NhbXBsZXJdID0gTm9uZSwKICAgICAgICB0cmFjZWxvb3Bfc3luY19lbmFibGVkOiBib29sID0gRmFsc2UsCiAgICAgICAgc2hvdWxkX2VucmljaF9tZXRyaWNzOiBib29sID0gVHJ1ZSwKICAgICAgICByZXNvdXJjZV9hdHRyaWJ1dGVzOiBkaWN0ID0ge30sCiAgICAgICAgaW5zdHJ1bWVudHM6IE9wdGlvbmFsW1NldFtJbnN0cnVtZW50c11dID0gTm9uZSwKICAgICAgICBibG9ja19pbnN0cnVtZW50czogT3B0aW9uYWxbU2V0W0luc3RydW1lbnRzXV0gPSBOb25lLAogICAgICAgIGltYWdlX3VwbG9hZGVyOiBPcHRpb25hbFtJbWFnZVVwbG9hZGVyXSA9IE5vbmUsCiAgICAgICAgc3Bhbl9wb3N0cHJvY2Vzc19jYWxsYmFjazogT3B0aW9uYWxbQ2FsbGFibGVbW1JlYWRhYmxlU3Bhbl0sIE5vbmVdXSA9IE5vbmUsCiAgICAgICAgZW5kcG9pbnRfaXNfdHJhY2Vsb29wOiBPcHRpb25hbFtib29sXSA9IEZhbHNlLAogICAgICAgIHVzZV9hdHRyaWJ1dGVzOiBPcHRpb25hbFtib29sXSA9IE5vbmUsCiAgICAgICAgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzOiBPcHRpb25hbFtib29sXSA9IE5vbmUsCiAgICAgICAgdHJhY2VfY29udGVudDogYm9vbCA9IFRydWUsCiAgICApIC0+IE9wdGlvbmFsW0NsaWVudF06CiAgICAgICAgIiIiSW5pdGlhbGl6ZSBUcmFjZWxvb3AgdHJhY2luZywgbWV0cmljcywgYW5kIGluc3RydW1lbnRhdGlvbi4KCiAgICAgICAgQXJnczoKICAgICAgICAgICAgdXNlX2F0dHJpYnV0ZXM6IENvbnRyb2xzIGhvdyBwcm9tcHRzL2NvbXBsZXRpb25zIGFyZSBlbWl0dGVkIGJ5IHRoZQogICAgICAgICAgICAgICAgYnVuZGxlZCBpbnN0cnVtZW50YXRpb25zLgogICAgICAgICAgICAgICAgSWYgYFRydWVgIChkZWZhdWx0KSwgdGhleSBhcmUgc2V0IGFzIHNwYW4gYXR0cmlidXRlcwogICAgICAgICAgICAgICAgKGBnZW5fYWkuaW5wdXQubWVzc2FnZXNgIC8gYGdlbl9haS5vdXRwdXQubWVzc2FnZXNgKSwgcGVyIHRoZSBjdXJyZW50CiAgICAgICAgICAgICAgICBPcGVuVGVsZW1ldHJ5IEdlbkFJIHNlbWFudGljLWNvbnZlbnRpb24gc3BlYy4KICAgICAgICAgICAgICAgIElmIGBGYWxzZWAsIHRoZXkgYXJlIGVtaXR0ZWQgYXMgT1RlbCBsb2cgZXZlbnRzIGluc3RlYWQuIFRoaXMgcGF0aAogICAgICAgICAgICAgICAgcmVxdWlyZXMgdGhlIGFwcGxpY2F0aW9uIHRvIGhhdmUgY29uZmlndXJlZCBhbiBgRXZlbnRMb2dnZXJQcm92aWRlcmAKICAgICAgICAgICAgICAgICh2aWEgYG9wZW50ZWxlbWV0cnkuX2V2ZW50cy5zZXRfZXZlbnRfbG9nZ2VyX3Byb3ZpZGVyYCk7IG90aGVyd2lzZQogICAgICAgICAgICAgICAgZXZlbnRzIGhhdmUgbm93aGVyZSB0byBnbyBhbmQgbm8gcHJvbXB0L2NvbXBsZXRpb24gZGF0YSB3aWxsIGJlIHJlY29yZGVkLgogICAgICAgICAgICB1c2VfbGVnYWN5X2F0dHJpYnV0ZXM6IERlcHJlY2F0ZWQgYWxpYXMgZm9yIGB1c2VfYXR0cmlidXRlc2AuIFdpbGwgYmUKICAgICAgICAgICAgICAgIHJlbW92ZWQgaW4gYSBmdXR1cmUgcmVsZWFzZS4KICAgICAgICAgICAgdHJhY2VfY29udGVudDogV2hldGhlciB0byBzZW5kIHNlbnNpdGl2ZSBjb250ZW50IChwcm9tcHRzLAogICAgICAgICAgICAgICAgY29tcGxldGlvbnMpIHRvIHRoZSB0cmFjaW5nIGJhY2tlbmQuIERlZmF1bHRzIHRvIGBUcnVlYC4KICAgICAgICAgICAgICAgIFdoZW4gc2V0IHRvIGBGYWxzZWAsIG92ZXJyaWRlcyB0aGUgVFJBQ0VMT09QX1RSQUNFX0NPTlRFTlQKICAgICAgICAgICAgICAgIGVudmlyb25tZW50IHZhcmlhYmxlIGFuZCBkaXNhYmxlcyBjb250ZW50IGNhcHR1cmUgYWNyb3NzIGFsbAogICAgICAgICAgICAgICAgaW5zdHJ1bWVudGF0aW9ucy4KICAgICAgICAiIiIKICAgICAgICBpZiB1c2VfYXR0cmlidXRlcyBpcyBub3QgTm9uZSBhbmQgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzIGlzIG5vdCBOb25lOgogICAgICAgICAgICByYWlzZSBUeXBlRXJyb3IoCiAgICAgICAgICAgICAgICAiQ2Fubm90IHBhc3MgYm90aCBgdXNlX2F0dHJpYnV0ZXNgIGFuZCBgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzYDsgIgogICAgICAgICAgICAgICAgImB1c2VfbGVnYWN5X2F0dHJpYnV0ZXNgIGlzIGRlcHJlY2F0ZWQsIHVzZSBgdXNlX2F0dHJpYnV0ZXNgIGluc3RlYWQuIgogICAgICAgICAgICApCiAgICAgICAgaWYgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzIGlzIG5vdCBOb25lOgogICAgICAgICAgICB3YXJuaW5ncy53YXJuKAogICAgICAgICAgICAgICAgImB1c2VfbGVnYWN5X2F0dHJpYnV0ZXNgIGlzIGRlcHJlY2F0ZWQgYW5kIHdpbGwgYmUgcmVtb3ZlZCBpbiBhICIKICAgICAgICAgICAgICAgICJmdXR1cmUgcmVsZWFzZTsgdXNlIGB1c2VfYXR0cmlidXRlc2AgaW5zdGVhZC4gVGhlIGN1cnJlbnQgT1RlbCAiCiAgICAgICAgICAgICAgICAiR2VuQUkgc3BlYyBlbWl0cyBwcm9tcHRzL2NvbXBsZXRpb25zIGFzIHNwYW4gYXR0cmlidXRlcyAiCiAgICAgICAgICAgICAgICAiKGBnZW5fYWkuaW5wdXQubWVzc2FnZXNgIC8gYGdlbl9haS5vdXRwdXQubWVzc2FnZXNgKSwgd2hpY2ggaXMgIgogICAgICAgICAgICAgICAgIndoYXQgYHVzZV9hdHRyaWJ1dGVzPVRydWVgICh0aGUgZGVmYXVsdCkgZG9lcy4gIgogICAgICAgICAgICAgICAgImB1c2VfYXR0cmlidXRlcz1GYWxzZWAgb3B0cyBpbnRvIHRoZSBldmVudHMgcGF0aCBpbnN0ZWFkLiIsCiAgICAgICAgICAgICAgICBEZXByZWNhdGlvbldhcm5pbmcsCiAgICAgICAgICAgICAgICBzdGFja2xldmVsPTIsCiAgICAgICAgICAgICkKICAgICAgICAgICAgdXNlX2F0dHJpYnV0ZXMgPSB1c2VfbGVnYWN5X2F0dHJpYnV0ZXMKICAgICAgICBpZiB1c2VfYXR0cmlidXRlcyBpcyBOb25lOgogICAgICAgICAgICB1c2VfYXR0cmlidXRlcyA9IFRydWUKICAgICAgICBpZiBub3QgZW5hYmxlZDoKICAgICAgICAgICAgVHJhY2VyV3JhcHBlci5zZXRfZGlzYWJsZWQoVHJ1ZSkKICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICBGb3JlLllFTExPVwogICAgICAgICAgICAgICAgKyAiVHJhY2Vsb29wIGluc3RydW1lbnRhdGlvbiBpcyBkaXNhYmxlZCB2aWEgaW5pdCBmbGFnIgogICAgICAgICAgICAgICAgKyBGb3JlLlJFU0VUCiAgICAgICAgICAgICkKICAgICAgICAgICAgcmV0dXJuCgogICAgICAgIGlmIG5vdCB0cmFjZV9jb250ZW50OgogICAgICAgICAgICBvcy5lbnZpcm9uWyJUUkFDRUxPT1BfVFJBQ0VfQ09OVEVOVCJdID0gImZhbHNlIgoKICAgICAgICBhcGlfZW5kcG9pbnQgPSBvcy5nZXRlbnYoIlRSQUNFTE9PUF9CQVNFX1VSTCIpIG9yIGFwaV9lbmRwb2ludAogICAgICAgIGFwaV9rZXkgPSBvcy5nZXRlbnYoIlRSQUNFTE9PUF9BUElfS0VZIikgb3IgYXBpX2tleQogICAgICAgIFRyYWNlbG9vcC5fX2FwcF9uYW1lID0gYXBwX25hbWUKCiAgICAgICAgaWYgbm90IGlzX3RyYWNpbmdfZW5hYmxlZCgpOgogICAgICAgICAgICBwcmludChGb3JlLllFTExPVyArICJUcmFjaW5nIGlzIGRpc2FibGVkIiArIEZvcmUuUkVTRVQpCiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBlbmFibGVfY29udGVudF90cmFjaW5nID0gaXNfY29udGVudF90cmFjaW5nX2VuYWJsZWQoKQoKICAgICAgICBpZiBleHBvcnRlciBhbmQgcHJvY2Vzc29yOgogICAgICAgICAgICB3YXJuaW5ncy53YXJuKAogICAgICAgICAgICAgICAgIkJvdGggJ2V4cG9ydGVyJyBhbmQgJ3Byb2Nlc3Nvcicgd2VyZSBwcm92aWRlZCB0byBUcmFjZWxvb3AuaW5pdCgpLiAiCiAgICAgICAgICAgICAgICAiVGhlICdleHBvcnRlcicgd2lsbCBiZSBpZ25vcmVkIOKAlCB5b3VyIHByb2Nlc3NvciBzaG91bGQgYWxyZWFkeSB3cmFwIHRoZSBleHBvcnRlci4gIgogICAgICAgICAgICAgICAgIldyYXAgeW91ciBleHBvcnRlciBpbnNpZGUgdGhlIHByb2Nlc3NvciwgZS5nLjogU2ltcGxlU3Bhbihwcm9jZXNzb3I6bXlfZXhwb3J0ZXIpLiIsCiAgICAgICAgICAgICAgICBVc2VyV2FybmluZywKICAgICAgICAgICAgICAgIHN0YWNrbGV2ZWw9MiwKICAgICAgICAgICAgKQoKICAgICAgICBpZiBleHBvcnRlciBvciBwcm9jZXNzb3I6CiAgICAgICAgICAgIHByaW50KEZvcmUuR1JFRU4gKyAiVHJhY2Vsb29wIGV4cG9ydGluZyB0cmFjZXMgdG8gYSBjdXN0b20gZXhwb3J0ZXIiKQoKICAgICAgICBoZWFkZXJzID0gb3MuZ2V0ZW52KCJUUkFDRUxPT1BfSEVBREVSUyIpIG9yIGhlYWRlcnMKCiAgICAgICAgaWYgaXNpbnN0YW5jZShoZWFkZXJzLCBzdHIpOgogICAgICAgICAgICBoZWFkZXJzID0gcGFyc2VfZW52X2hlYWRlcnMoaGVhZGVycykKCiAgICAgICAgaWYgKAogICAgICAgICAgICBub3QgZXhwb3J0ZXIKICAgICAgICAgICAgYW5kIG5vdCBwcm9jZXNzb3IKICAgICAgICAgICAgYW5kIGFwaV9lbmRwb2ludCA9PSAiaHR0cHM6Ly9hcGkudHJhY2Vsb29wLmNvbSIKICAgICAgICAgICAgYW5kIG5vdCBhcGlfa2V5CiAgICAgICAgKToKICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICBGb3JlLlJFRAogICAgICAgICAgICAgICAgKyAiRXJyb3I6IE1pc3NpbmcgVHJhY2Vsb29wIEFQSSBrZXksIgogICAgICAgICAgICAgICAgKyAiIGdvIHRvIGh0dHBzOi8vYXBwLnRyYWNlbG9vcC5jb20vc2V0dGluZ3MvYXBpLWtleXMgdG8gY3JlYXRlIG9uZSIKICAgICAgICAgICAgKQogICAgICAgICAgICBwcmludCgiU2V0IHRoZSBUUkFDRUxPT1BfQVBJS19LRVkgZW52aXJvbm1lbnQgdmFyaWFibGUgdG8gdGhlIGtleSIpCiAgICAgICAgICAgIHByaW50KEZvcmUuUkVTRVQpCiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBpZiBub3QgZXhwb3J0ZXIgYW5kIG5vdCBwcm9jZXNzb3IgYW5kIGhlYWRlcnM6CiAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgRm9yZS5HUkVFTgogICAgICAgICAgICAgICAgKyBmIlRyYWNlbG9vcCBleHBvcnRpbmcgdHJhY2VzIHRvIHthcGlfZW5kcG9pbnR9LCBhdXRoZW50aWNhdGluZyB3aXRoIGN1c3RvbSBoZWFkZXJzIgogICAgICAgICAgICApCgogICAgICAgIGlmIGFwaV9rZXkgYW5kIG5vdCBleHBvcnRlciBhbmQgbm90IHByb2Nlc3NvciBhbmQgbm90IGhlYWRlcnM6CiAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgRm9yZS5HUkVFTgogICAgICAgICAgICAgICAgKyBmIlRyYWNlbG9vcCBleHBvcnRpbmcgdHJhY2VzIHRvIHthcGlfZW5kcG9pbnR9IGF1dGhlbnRpY2F0aW5nIHdpdGggYmVhcmVyIHRva2VuIgogICAgICAgICAgICApCiAgICAgICAgICAgIGhlYWRlcnMgPSB7CiAgICAgICAgICAgICAgICAiQXV0aG9yaXphdGlvbiI6IGYiQmVhcmVyIHthcGlfa2V5fSIsCiAgICAgICAgICAgIH0KCiAgICAgICAgcHJpbnQoRm9yZS5SRVNFVCkKCiAgICAgICAgIyBUcmFjZXIgaW5pdAogICAgICAgIHJlc291cmNlX2F0dHJpYnV0ZXMudXBkYXRlKHtTRVJWSUNFX05BTUU6IGFwcF9uYW1lfSkKICAgICAgICBUcmFjZXJXcmFwcGVyLnNldF9zdGF0aWNfcGFyYW1zKAogICAgICAgICAgICByZXNvdXJjZV9hdHRyaWJ1dGVzLCBlbmFibGVfY29udGVudF90cmFjaW5nLCBhcGlfZW5kcG9pbnQsIGhlYWRlcnMKICAgICAgICApCiAgICAgICAgVHJhY2Vsb29wLl9fdHJhY2VyX3dyYXBwZXIgPSBUcmFjZXJXcmFwcGVyKAogICAgICAgICAgICBkaXNhYmxlX2JhdGNoPWRpc2FibGVfYmF0Y2gsCiAgICAgICAgICAgIHByb2Nlc3Nvcj1wcm9jZXNzb3IsCiAgICAgICAgICAgIHByb3BhZ2F0b3I9cHJvcGFnYXRvciwKICAgICAgICAgICAgZXhwb3J0ZXI9ZXhwb3J0ZXIsCiAgICAgICAgICAgIHNhbXBsZXI9c2FtcGxlciwKICAgICAgICAgICAgc2hvdWxkX2VucmljaF9tZXRyaWNzPXNob3VsZF9lbnJpY2hfbWV0cmljcywKICAgICAgICAgICAgaW1hZ2VfdXBsb2FkZXI9aW1hZ2VfdXBsb2FkZXIgb3IgSW1hZ2VVcGxvYWRlcihhcGlfZW5kcG9pbnQsIGFwaV9rZXkpLAogICAgICAgICAgICBpbnN0cnVtZW50cz1pbnN0cnVtZW50cywKICAgICAgICAgICAgYmxvY2tfaW5zdHJ1bWVudHM9YmxvY2tfaW5zdHJ1bWVudHMsCiAgICAgICAgICAgIHNwYW5fcG9zdHByb2Nlc3NfY2FsbGJhY2s9c3Bhbl9wb3N0cHJvY2Vzc19jYWxsYmFjaywKICAgICAgICAgICAgdXNlX2F0dHJpYnV0ZXM9dXNlX2F0dHJpYnV0ZXMsCiAgICAgICAgKQoKICAgICAgICBtZXRyaWNzX2Rpc2FibGVkX2J5X2NvbmZpZyA9IG5vdCBpc19tZXRyaWNzX2VuYWJsZWQoKQogICAgICAgIGhhc19jdXN0b21fc3BhbnNfcGlwZWxpbmUgPSBwcm9jZXNzb3Igb3IgZXhwb3J0ZXIKICAgICAgICBjdXN0b21fdHJhY2Vfd2l0aG91dF9jdXN0b21fbWV0cmljcyA9IGhhc19jdXN0b21fc3BhbnNfcGlwZWxpbmUgYW5kIG5vdCBtZXRyaWNzX2V4cG9ydGVyCgogICAgICAgIGlmIG1ldHJpY3NfZGlzYWJsZWRfYnlfY29uZmlnIG9yIGN1c3RvbV90cmFjZV93aXRob3V0X2N1c3RvbV9tZXRyaWNzOgogICAgICAgICAgICBwcmludChGb3JlLllFTExPVyArICJNZXRyaWNzIGFyZSBkaXNhYmxlZCIgKyBGb3JlLlJFU0VUKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIG1ldHJpY3NfZW5kcG9pbnQgPSBvcy5nZXRlbnYoIlRSQUNFTE9PUF9NRVRSSUNTX0VORFBPSU5UIikgb3IgYXBpX2VuZHBvaW50CiAgICAgICAgICAgIG1ldHJpY3NfaGVhZGVycyA9ICgKICAgICAgICAgICAgICAgIG9zLmdldGVudigiVFJBQ0VMT09QX01FVFJJQ1NfSEVBREVSUyIpIG9yIG1ldHJpY3NfaGVhZGVycyBvciBoZWFkZXJzCiAgICAgICAgICAgICkKICAgICAgICAgICAgaWYgbWV0cmljc19leHBvcnRlciBvciBwcm9jZXNzb3I6CiAgICAgICAgICAgICAgICBwcmludChGb3JlLkdSRUVOICsgIlRyYWNlbG9vcCBleHBvcnRpbmcgbWV0cmljcyB0byBhIGN1c3RvbSBleHBvcnRlciIpCgogICAgICAgICAgICBNZXRyaWNzV3JhcHBlci5zZXRfc3RhdGljX3BhcmFtcygKICAgICAgICAgICAgICAgIHJlc291cmNlX2F0dHJpYnV0ZXMsIG1ldHJpY3NfZW5kcG9pbnQsIG1ldHJpY3NfaGVhZGVycwogICAgICAgICAgICApCiAgICAgICAgICAgIFRyYWNlbG9vcC5fX21ldHJpY3Nfd3JhcHBlciA9IE1ldHJpY3NXcmFwcGVyKGV4cG9ydGVyPW1ldHJpY3NfZXhwb3J0ZXIpCgogICAgICAgIGlmIGlzX2xvZ2dpbmdfZW5hYmxlZCgpIGFuZCAobG9nZ2luZ19leHBvcnRlciBvciBub3QgZXhwb3J0ZXIpOgogICAgICAgICAgICBsb2dnaW5nX2VuZHBvaW50ID0gb3MuZ2V0ZW52KCJUUkFDRUxPT1BfTE9HR0lOR19FTkRQT0lOVCIpIG9yIGFwaV9lbmRwb2ludAogICAgICAgICAgICBsb2dnaW5nX2hlYWRlcnMgPSAoCiAgICAgICAgICAgICAgICBvcy5nZXRlbnYoIlRSQUNFTE9PUF9MT0dHSU5HX0hFQURFUlMiKSBvciBsb2dnaW5nX2hlYWRlcnMgb3IgaGVhZGVycwogICAgICAgICAgICApCiAgICAgICAgICAgIGlmIGxvZ2dpbmdfZXhwb3J0ZXIgb3IgcHJvY2Vzc29yOgogICAgICAgICAgICAgICAgcHJpbnQoRm9yZS5HUkVFTiArICJUcmFjZWxvb3AgZXhwb3J0aW5nIGxvZ3MgdG8gYSBjdXN0b20gZXhwb3J0ZXIiKQoKICAgICAgICAgICAgTG9nZ2VyV3JhcHBlci5zZXRfc3RhdGljX3BhcmFtcygKICAgICAgICAgICAgICAgIHJlc291cmNlX2F0dHJpYnV0ZXMsIGxvZ2dpbmdfZW5kcG9pbnQsIGxvZ2dpbmdfaGVhZGVycwogICAgICAgICAgICApCiAgICAgICAgICAgIFRyYWNlbG9vcC5fX2xvZ2dlcl93cmFwcGVyID0gTG9nZ2VyV3JhcHBlcihleHBvcnRlcj1sb2dnaW5nX2V4cG9ydGVyKQoKICAgICAgICBpZiAoCiAgICAgICAgICAgIChhcGlfZW5kcG9pbnQuZmluZCgidHJhY2Vsb29wLmNvbSIpICE9IC0xIG9yIGVuZHBvaW50X2lzX3RyYWNlbG9vcCkKICAgICAgICAgICAgYW5kIGFwaV9rZXkKICAgICAgICAgICAgYW5kIChleHBvcnRlciBpcyBOb25lKQogICAgICAgICAgICBhbmQgKHByb2Nlc3NvciBpcyBOb25lKQogICAgICAgICk6CiAgICAgICAgICAgIGlmIHRyYWNlbG9vcF9zeW5jX2VuYWJsZWQ6CiAgICAgICAgICAgICAgICBUcmFjZWxvb3AuX19mZXRjaGVyID0gRmV0Y2hlcihiYXNlX3VybD1hcGlfZW5kcG9pbnQsIGFwaV9rZXk9YXBpX2tleSkKICAgICAgICAgICAgICAgIFRyYWNlbG9vcC5fX2ZldGNoZXIucnVuKCkKICAgICAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgICAgIEZvcmUuR1JFRU4KICAgICAgICAgICAgICAgICAgICArICJUcmFjZWxvb3Agc3luY2luZyBjb25maWd1cmF0aW9uIGFuZCBwcm9tcHRzIgogICAgICAgICAgICAgICAgICAgICsgRm9yZS5SRVNFVAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBUcmFjZWxvb3AuX19jbGllbnQgPSBDbGllbnQoCiAgICAgICAgICAgICAgICBhcGlfa2V5PWFwaV9rZXksIGFwcF9uYW1lPWFwcF9uYW1lLCBhcGlfZW5kcG9pbnQ9YXBpX2VuZHBvaW50CiAgICAgICAgICAgICkKICAgICAgICAgICAgcmV0dXJuIFRyYWNlbG9vcC5fX2NsaWVudAoKICAgIEBzdGF0aWNtZXRob2QKICAgIGRlZiBzZXRfYXNzb2NpYXRpb25fcHJvcGVydGllcyhwcm9wZXJ0aWVzOiBkaWN0KSAtPiBOb25lOgogICAgICAgIHNldF9hc3NvY2lhdGlvbl9wcm9wZXJ0aWVzKHByb3BlcnRpZXMpCgogICAgZGVmIHNldF9wcm9tcHQodGVtcGxhdGU6IHN0ciwgdmFyaWFibGVzOiBkaWN0LCB2ZXJzaW9uOiBpbnQpOgogICAgICAgIHNldF9leHRlcm5hbF9wcm9tcHRfdHJhY2luZ19jb250ZXh0KHRlbXBsYXRlLCB2YXJpYWJsZXMsIHZlcnNpb24pCgogICAgQHN0YXRpY21ldGhvZAogICAgZGVmIGdldF9kZWZhdWx0X3NwYW5fcHJvY2Vzc29yKAogICAgICAgIGRpc2FibGVfYmF0Y2g6IGJvb2wgPSBGYWxzZSwKICAgICAgICBhcGlfZW5kcG9pbnQ6IE9wdGlvbmFsW3N0cl0gPSBOb25lLAogICAgICAgIGFwaV9rZXk6IE9wdGlvbmFsW3N0cl0gPSBOb25lLAogICAgICAgIGhlYWRlcnM6IE9wdGlvbmFsW0RpY3Rbc3RyLCBzdHJdXSA9IE5vbmUsCiAgICAgICAgZXhwb3J0ZXI6IE9wdGlvbmFsW1NwYW5FeHBvcnRlcl0gPSBOb25lCiAgICApIC0+IFNwYW5Qcm9jZXNzb3I6CiAgICAgICAgIiIiCiAgICAgICAgQ3JlYXRlcyBhbmQgcmV0dXJucyB0aGUgZGVmYXVsdCBUcmFjZWxvb3Agc3BhbiBwcm9jZXNzb3IuCgogICAgICAgIFRoaXMgZnVuY3Rpb24gYWxsb3dzIHVzZXJzIHRvIGdldCB0aGUgZGVmYXVsdCBUcmFjZWxvb3Agc3BhbiBwcm9jZXNzb3IKICAgICAgICB0byBjb21iaW5lIGl0IHdpdGggdGhlaXIgY3VzdG9tIHByb2Nlc3NvcnMgd2hlbiB1c2luZyB0aGUgcHJvY2Vzc29ycyBwYXJhbWV0ZXIuCgogICAgICAgIEFyZ3M6CiAgICAgICAgICAgIGRpc2FibGVfYmF0Y2g6IElmIFRydWUsIHVzZXMgU2ltcGxlU3BhblByb2Nlc3Nvciwgb3RoZXJ3aXNlIEJhdGNoU3BhblByb2Nlc3NvcgogICAgICAgICAgICBhcGlfZW5kcG9pbnQ6IFRoZSBlbmRwb2ludCBVUkwgZm9yIHRoZSBleHBvcnRlciAodXNlcyBjdXJyZW50IGNvbmZpZyBpZiBOb25lKQogICAgICAgICAgICBoZWFkZXJzOiBIZWFkZXJzIGZvciB0aGUgZXhwb3J0ZXIgKHVzZXMgY3VycmVudCBjb25maWcgaWYgTm9uZSkKICAgICAgICAgICAgZXhwb3J0ZXI6IEN1c3RvbSBleHBvcnRlciB0byB1c2UgKGNyZWF0ZXMgZGVmYXVsdCBpZiBOb25lKQoKICAgICAgICBSZXR1cm5zOgogICAgICAgICAgICBTcGFuUHJvY2Vzc29yOiBUaGUgZGVmYXVsdCBUcmFjZWxvb3Agc3BhbiBwcm9jZXNzb3IKCiAgICAgICAgRXhhbXBsZToKICAgICAgICAgICAgIyBHZXQgdGhlIGRlZmF1bHQgcHJvY2Vzc29yIGFuZCBjb21iaW5lIHdpdGggY3VzdG9tIG9uZQogICAgICAgICAgICBkZWZhdWx0X3Byb2Nlc3NvciA9IFRyYWNlbG9vcC5nZXRfZGVmYXVsdF9zcGFuX3Byb2Nlc3NvcigpCiAgICAgICAgICAgIGN1c3RvbV9wcm9jZXNzb3IgPSBNeUN1c3RvbVNwYW5Qcm9jZXNzb3IoKQoKICAgICAgICAgICAgVHJhY2Vsb29wLmluaXQoCiAgICAgICAgICAgICAgICBwcm9jZXNzb3JzPVtkZWZhdWx0X3Byb2Nlc3NvciwgY3VzdG9tX3Byb2Nlc3Nvcl0KICAgICAgICAgICAgKQogICAgICAgICIiIgogICAgICAgIGZyb20gdHJhY2Vsb29wLnNkay50cmFjaW5nLnRyYWNpbmcgaW1wb3J0IGdldF9kZWZhdWx0X3NwYW5fcHJvY2Vzc29yCiAgICAgICAgaWYgaGVhZGVycyBpcyBOb25lOgogICAgICAgICAgICBpZiBhcGlfa2V5IGlzIE5vbmU6CiAgICAgICAgICAgICAgICBhcGlfa2V5ID0gb3MuZ2V0ZW52KCJUUkFDRUxPT1BfQVBJS19LRVkiKQogICAgICAgICAgICBoZWFkZXJzID0gewogICAgICAgICAgICAgICAgIkF1dGhvcml6YXRpb24iOiBmIkJlYXJlciB7YXBpX2tleX0iLAogICAgICAgICAgICB9CiAgICAgICAgaWYgYXBpX2VuZHBvaW50IGlzIE5vbmU6CiAgICAgICAgICAgIGFwaV9lbmRwb2ludCA9IG9zLmdldGVudigiVFJBQ0VMT09QX0JBU0VfVVJMIikgb3IgImh0dHBzOi8vYXBpLnRyYWNlbG9vcC5jb20iCiAgICAgICAgcmV0dXJuIGdldF9kZWZhdWx0X3NwYW5fcHJvY2Vzc29yKGRpc2FibGVfYmF0Y2gsIGFwaV9lbmRwb2ludCwgaGVhZGVycywgZXhwb3J0ZXIpCgogICAgQHN0YXRpY21ldGhvZAogICAgZGVmIGdldCgpOgogICAgICAgICIiIgogICAgICAgIFJldHVybnMgdGhlIHNoYXJlZCBTREsgY2xpZW50IGluc3RhbmNlLCB1c2luZyB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi4KCiAgICAgICAgVG8gdXNlIHRoZSBTREsgYXMgYSBzaW5nbGV0b24sIGZpcnN0IG1ha2Ugc3VyZSB5b3UgaGF2ZSBjYWxsZWQgOmZ1bmM6YFRyYWNlbG9vcC5pbml0KClgCiAgICAgICAgYXQgc3RhcnR1cCB0aW1lLiBUaGVuIGBnZXQoKWAgd2lsbCByZXR1cm4gdGhlIHNhbWUgc2hhcmVkIDpjbGFzczpgVHJhY2Vsb29wLmNsaWVudC5DbGllbnRgCiAgICAgICAgaW5zdGFuY2UgZWFjaCB0aW1lLiBUaGUgY2xpZW50IHdpbGwgYmUgaW5pdGlhbGl6ZWQgaWYgaXQgaGFzIG5vdCBiZWVuIGFscmVhZHkuCgogICAgICAgIElmIHlvdSBuZWVkIHRvIGNyZWF0ZSBtdWx0aXBsZSBjbGllbnQgaW5zdGFuY2VzIHdpdGggZGlmZmVyZW50IGNvbmZpZ3VyYXRpb25zLCBpbnN0ZWFkIG9mIHRoaXMKICAgICAgICBzaW5nbGV0b24gYXBwcm9hY2ggeW91IGNhbiBjYWxsIHRoZSA6Y2xhc3M6YFRyYWNlbG9vcC5jbGllbnQuQ2xpZW50YCBjb25zdHJ1Y3RvciBkaXJlY3RseSBpbnN0ZWFkLgogICAgICAgICIiIgogICAgICAgIGlmIG5vdCBUcmFjZWxvb3AuX19jbGllbnQ6CiAgICAgICAgICAgIHJhaXNlIEV4Y2VwdGlvbigKICAgICAgICAgICAgICAgICJDbGllbnQgbm90IGluaXRpYWxpemVkLCB5b3Ugc2hvdWxkIGNhbGwgVHJhY2Vsb29wLmluaXQoKSBmaXJzdC4gIgogICAgICAgICAgICAgICAgIklmIHlvdSBhcmUgc3RpbGwgZ2V0dGluZyB0aGlzIGVycm9yIC0geW91IGFyZSBtaXNzaW5nIHRoZSBhcGkga2V5IgogICAgICAgICAgICApCiAgICAgICAgcmV0dXJuIFRyYWNlbG9vcC5fX2NsaWVudAo= \ No newline at end of file From e075ad6d64ea782bc9610d2f2a53aecbc6ef14e0 Mon Sep 17 00:00:00 2001 From: YoinkingFishy Date: Wed, 29 Jul 2026 23:55:07 +0530 Subject: [PATCH 2/2] fix: replace corrupted file with correct Python source --- .../traceloop-sdk/traceloop/sdk/__init__.py | 326 +++++++++++++++++- 1 file changed, 325 insertions(+), 1 deletion(-) diff --git a/packages/traceloop-sdk/traceloop/sdk/__init__.py b/packages/traceloop-sdk/traceloop/sdk/__init__.py index 42fead72b3..c5a4b961cb 100644 --- a/packages/traceloop-sdk/traceloop/sdk/__init__.py +++ b/packages/traceloop-sdk/traceloop/sdk/__init__.py @@ -1 +1,325 @@ -aW1wb3J0IG9zCmltcG9ydCBzeXMKaW1wb3J0IHdhcm5pbmdzCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAoKZnJvbSB0eXBpbmcgaW1wb3J0IENhbGxhYmxlLCBMaXN0LCBPcHRpb25hbCwgU2V0LCBVbmlvbgpmcm9tIGNvbG9yYW1hIGltcG9ydCBGb3JlCmZyb20gb3BlbnRlbGVtZXRyeS5zZGsudHJhY2UgaW1wb3J0IFNwYW5Qcm9jZXNzb3IsIFJlYWRhYmxlU3Bhbgpmcm9tIG9wZW50ZWxlbWV0cnkuc2RrLnRyYWNlLnNhbXBsaW5nIGltcG9ydCBTYW1wbGVyCmZyb20gb3BlbnRlbGVtZXRyeS5zZGsudHJhY2UuZXhwb3J0IGltcG9ydCBTcGFuRXhwb3J0ZXIKZnJvbSBvcGVudGVsZW1ldHJ5LnNkay5tZXRyaWNzLmV4cG9ydCBpbXBvcnQgTWV0cmljRXhwb3J0ZXIKZnJvbSBvcGVudGVsZW1ldHJ5LnNkay5fbG9ncy5leHBvcnQgaW1wb3J0IExvZ0V4cG9ydGVyCmZyb20gb3BlbnRlbGVtZXRyeS5zZGsucmVzb3VyY2VzIGltcG9ydCBTRVJWSUNFX05BTUUKZnJvbSBvcGVudGVsZW1ldHJ5LnByb3BhZ2F0b3JzLnRleHRtYXAgaW1wb3J0IFRleHRNYXBQcm9wYWdhdG9yCmZyb20gb3BlbnRlbGVtZXRyeS51dGlsLnJlIGltcG9ydCBwYXJzZV9lbnZfaGVhZGVycwoKZnJvbSB0cmFjZWxvb3Auc2RrLmltYWdlcy5pbWFnZV91cGxvYWRlciBpbXBvcnQgSW1hZ2VVcGxvYWRlcgpmcm9tIHRyYWNlbG9vcC5zZGsubWV0cmljcy5tZXRyaWNzIGltcG9ydCBNZXRyaWNzV3JhcHBlcgpmcm9tIHRyYWNlbG9vcC5zZGsubG9nZ2luZy5sb2dnaW5nIGltcG9ydCBMb2dnZXJXcmFwcGVyCmZyb20gdHJhY2Vsb29wLnNkay5pbnN0cnVtZW50cyBpbXBvcnQgSW5zdHJ1bWVudHMKZnJvbSB0cmFjZWxvb3Auc2RrLmNvbmZpZyBpbXBvcnQgKAogICAgaXNfY29udGVudF90cmFjaW5nX2VuYWJsZWQsCiAgICBpc190cmFjaW5nX2VuYWJsZWQsCiAgICBpc19tZXRyaWNzX2VuYWJsZWQsCiAgICBpc19sb2dnaW5nX2VuYWJsZWQsCikKZnJvbSB0cmFjZWxvb3Auc2RrLmZldGNoZXIgaW1wb3J0IEZldGNoZXIKZnJvbSB0cmFjZWxvb3Auc2RrLnRyYWNpbmcudHJhY2luZyBpbXBvcnQgKAogICAgVHJhY2VyV3JhcHBlciwKICAgIHNldF9hc3NvY2lhdGlvbl9wcm9wZXJ0aWVzLAogICAgc2V0X2V4dGVybmFsX3Byb21wdF90cmFjaW5nX2NvbnRleHQsCikKZnJvbSB0eXBpbmcgaW1wb3J0IERpY3QKZnJvbSB0cmFjZWxvb3Auc2RrLmNsaWVudC5jbGllbnQgaW1wb3J0IENsaWVudApmcm9tIHRyYWNlbG9vcC5zZGsuYXNzb2NpYXRpb25zLmFzc29jaWF0aW9ucyBpbXBvcnQgQXNzb2NpYXRpb25Qcm9wZXJ0eSBhcyBBc3NvY2lhdGlvblByb3BlcnR5CgoKY2xhc3MgVHJhY2Vsb29wOgogICAgQVVUT19DUkVBVEVEX0tFWV9QQVRIID0gc3RyKAogICAgICAgIFBhdGguaG9tZSgpIC8gIi5jYWNoZSIgLyAidHJhY2Vsb29wIiAvICJhdXRvX2NyZWF0ZWRfa2V5IgogICAgKQogICAgQVVUT19DUkVBVEVEX1VSTCA9IHN0cihQYXRoLmhvbWUoKSAvICIuY2FjaGUiIC8gInRyYWNlbG9vcCIgLyAiYXV0b19jcmVhdGVkX3VybCIpCgogICAgX190cmFjZXJfd3JhcHBlcjogVHJhY2VyV3JhcHBlcgogICAgX19mZXRjaGVyOiBPcHRpb25hbFtGZXRjaGVyXSA9IE5vbmUKICAgIF9fYXBwX25hbWU6IE9wdGlvbmFsW3N0cl0gPSBOb25lCiAgICBfX2NsaWVudDogT3B0aW9uYWxbQ2xpZW50XSA9IE5vbmUKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgaW5pdCgKICAgICAgICBhcHBfbmFtZTogc3RyID0gc3lzLmFyZ3ZbMF0sCiAgICAgICAgYXBpX2VuZHBvaW50OiBzdHIgPSAiaHR0cHM6Ly9hcGkudHJhY2Vsb29wLmNvbSIsCiAgICAgICAgYXBpX2tleTogT3B0aW9uYWxbc3RyXSA9IE5vbmUsCiAgICAgICAgZW5hYmxlZDogYm9vbCA9IFRydWUsCiAgICAgICAgaGVhZGVyczogRGljdFtzdHIsIHN0cl0gPSB7fSwKICAgICAgICBkaXNhYmxlX2JhdGNoPUZhbHNlLAogICAgICAgIHRlbGVtZXRyeV9lbmFibGVkOiBib29sID0gVHJ1ZSwKICAgICAgICBleHBvcnRlcjogT3B0aW9uYWxbU3BhbkV4cG9ydGVyXSA9IE5vbmUsCiAgICAgICAgbWV0cmljc19leHBvcnRlcjogTWV0cmljRXhwb3J0ZXIgPSBOb25lLAogICAgICAgIG1ldHJpY3NfaGVhZGVyczogRGljdFtzdHIsIHN0cl0gPSBOb25lLAogICAgICAgIGxvZ2dpbmdfZXhwb3J0ZXI6IExvZ0V4cG9ydGVyID0gTm9uZSwKICAgICAgICBsb2dnaW5nX2hlYWRlcnM6IERpY3Rbc3RyLCBzdHJdID0gTm9uZSwKICAgICAgICBwcm9jZXNzb3I6IE9wdGlvbmFsW1VuaW9uW1NwYW5Qcm9jZXNzb3IsIExpc3RbU3BhblByb2Nlc3Nvcl1dXSA9IE5vbmUsCiAgICAgICAgcHJvcGFnYXRvcjogVGV4dE1hcFByb3BhZ2F0b3IgPSBOb25lLAogICAgICAgIHNhbXBsZXI6IE9wdGlvbmFsW1NhbXBsZXJdID0gTm9uZSwKICAgICAgICB0cmFjZWxvb3Bfc3luY19lbmFibGVkOiBib29sID0gRmFsc2UsCiAgICAgICAgc2hvdWxkX2VucmljaF9tZXRyaWNzOiBib29sID0gVHJ1ZSwKICAgICAgICByZXNvdXJjZV9hdHRyaWJ1dGVzOiBkaWN0ID0ge30sCiAgICAgICAgaW5zdHJ1bWVudHM6IE9wdGlvbmFsW1NldFtJbnN0cnVtZW50c11dID0gTm9uZSwKICAgICAgICBibG9ja19pbnN0cnVtZW50czogT3B0aW9uYWxbU2V0W0luc3RydW1lbnRzXV0gPSBOb25lLAogICAgICAgIGltYWdlX3VwbG9hZGVyOiBPcHRpb25hbFtJbWFnZVVwbG9hZGVyXSA9IE5vbmUsCiAgICAgICAgc3Bhbl9wb3N0cHJvY2Vzc19jYWxsYmFjazogT3B0aW9uYWxbQ2FsbGFibGVbW1JlYWRhYmxlU3Bhbl0sIE5vbmVdXSA9IE5vbmUsCiAgICAgICAgZW5kcG9pbnRfaXNfdHJhY2Vsb29wOiBPcHRpb25hbFtib29sXSA9IEZhbHNlLAogICAgICAgIHVzZV9hdHRyaWJ1dGVzOiBPcHRpb25hbFtib29sXSA9IE5vbmUsCiAgICAgICAgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzOiBPcHRpb25hbFtib29sXSA9IE5vbmUsCiAgICAgICAgdHJhY2VfY29udGVudDogYm9vbCA9IFRydWUsCiAgICApIC0+IE9wdGlvbmFsW0NsaWVudF06CiAgICAgICAgIiIiSW5pdGlhbGl6ZSBUcmFjZWxvb3AgdHJhY2luZywgbWV0cmljcywgYW5kIGluc3RydW1lbnRhdGlvbi4KCiAgICAgICAgQXJnczoKICAgICAgICAgICAgdXNlX2F0dHJpYnV0ZXM6IENvbnRyb2xzIGhvdyBwcm9tcHRzL2NvbXBsZXRpb25zIGFyZSBlbWl0dGVkIGJ5IHRoZQogICAgICAgICAgICAgICAgYnVuZGxlZCBpbnN0cnVtZW50YXRpb25zLgogICAgICAgICAgICAgICAgSWYgYFRydWVgIChkZWZhdWx0KSwgdGhleSBhcmUgc2V0IGFzIHNwYW4gYXR0cmlidXRlcwogICAgICAgICAgICAgICAgKGBnZW5fYWkuaW5wdXQubWVzc2FnZXNgIC8gYGdlbl9haS5vdXRwdXQubWVzc2FnZXNgKSwgcGVyIHRoZSBjdXJyZW50CiAgICAgICAgICAgICAgICBPcGVuVGVsZW1ldHJ5IEdlbkFJIHNlbWFudGljLWNvbnZlbnRpb24gc3BlYy4KICAgICAgICAgICAgICAgIElmIGBGYWxzZWAsIHRoZXkgYXJlIGVtaXR0ZWQgYXMgT1RlbCBsb2cgZXZlbnRzIGluc3RlYWQuIFRoaXMgcGF0aAogICAgICAgICAgICAgICAgcmVxdWlyZXMgdGhlIGFwcGxpY2F0aW9uIHRvIGhhdmUgY29uZmlndXJlZCBhbiBgRXZlbnRMb2dnZXJQcm92aWRlcmAKICAgICAgICAgICAgICAgICh2aWEgYG9wZW50ZWxlbWV0cnkuX2V2ZW50cy5zZXRfZXZlbnRfbG9nZ2VyX3Byb3ZpZGVyYCk7IG90aGVyd2lzZQogICAgICAgICAgICAgICAgZXZlbnRzIGhhdmUgbm93aGVyZSB0byBnbyBhbmQgbm8gcHJvbXB0L2NvbXBsZXRpb24gZGF0YSB3aWxsIGJlIHJlY29yZGVkLgogICAgICAgICAgICB1c2VfbGVnYWN5X2F0dHJpYnV0ZXM6IERlcHJlY2F0ZWQgYWxpYXMgZm9yIGB1c2VfYXR0cmlidXRlc2AuIFdpbGwgYmUKICAgICAgICAgICAgICAgIHJlbW92ZWQgaW4gYSBmdXR1cmUgcmVsZWFzZS4KICAgICAgICAgICAgdHJhY2VfY29udGVudDogV2hldGhlciB0byBzZW5kIHNlbnNpdGl2ZSBjb250ZW50IChwcm9tcHRzLAogICAgICAgICAgICAgICAgY29tcGxldGlvbnMpIHRvIHRoZSB0cmFjaW5nIGJhY2tlbmQuIERlZmF1bHRzIHRvIGBUcnVlYC4KICAgICAgICAgICAgICAgIFdoZW4gc2V0IHRvIGBGYWxzZWAsIG92ZXJyaWRlcyB0aGUgVFJBQ0VMT09QX1RSQUNFX0NPTlRFTlQKICAgICAgICAgICAgICAgIGVudmlyb25tZW50IHZhcmlhYmxlIGFuZCBkaXNhYmxlcyBjb250ZW50IGNhcHR1cmUgYWNyb3NzIGFsbAogICAgICAgICAgICAgICAgaW5zdHJ1bWVudGF0aW9ucy4KICAgICAgICAiIiIKICAgICAgICBpZiB1c2VfYXR0cmlidXRlcyBpcyBub3QgTm9uZSBhbmQgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzIGlzIG5vdCBOb25lOgogICAgICAgICAgICByYWlzZSBUeXBlRXJyb3IoCiAgICAgICAgICAgICAgICAiQ2Fubm90IHBhc3MgYm90aCBgdXNlX2F0dHJpYnV0ZXNgIGFuZCBgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzYDsgIgogICAgICAgICAgICAgICAgImB1c2VfbGVnYWN5X2F0dHJpYnV0ZXNgIGlzIGRlcHJlY2F0ZWQsIHVzZSBgdXNlX2F0dHJpYnV0ZXNgIGluc3RlYWQuIgogICAgICAgICAgICApCiAgICAgICAgaWYgdXNlX2xlZ2FjeV9hdHRyaWJ1dGVzIGlzIG5vdCBOb25lOgogICAgICAgICAgICB3YXJuaW5ncy53YXJuKAogICAgICAgICAgICAgICAgImB1c2VfbGVnYWN5X2F0dHJpYnV0ZXNgIGlzIGRlcHJlY2F0ZWQgYW5kIHdpbGwgYmUgcmVtb3ZlZCBpbiBhICIKICAgICAgICAgICAgICAgICJmdXR1cmUgcmVsZWFzZTsgdXNlIGB1c2VfYXR0cmlidXRlc2AgaW5zdGVhZC4gVGhlIGN1cnJlbnQgT1RlbCAiCiAgICAgICAgICAgICAgICAiR2VuQUkgc3BlYyBlbWl0cyBwcm9tcHRzL2NvbXBsZXRpb25zIGFzIHNwYW4gYXR0cmlidXRlcyAiCiAgICAgICAgICAgICAgICAiKGBnZW5fYWkuaW5wdXQubWVzc2FnZXNgIC8gYGdlbl9haS5vdXRwdXQubWVzc2FnZXNgKSwgd2hpY2ggaXMgIgogICAgICAgICAgICAgICAgIndoYXQgYHVzZV9hdHRyaWJ1dGVzPVRydWVgICh0aGUgZGVmYXVsdCkgZG9lcy4gIgogICAgICAgICAgICAgICAgImB1c2VfYXR0cmlidXRlcz1GYWxzZWAgb3B0cyBpbnRvIHRoZSBldmVudHMgcGF0aCBpbnN0ZWFkLiIsCiAgICAgICAgICAgICAgICBEZXByZWNhdGlvbldhcm5pbmcsCiAgICAgICAgICAgICAgICBzdGFja2xldmVsPTIsCiAgICAgICAgICAgICkKICAgICAgICAgICAgdXNlX2F0dHJpYnV0ZXMgPSB1c2VfbGVnYWN5X2F0dHJpYnV0ZXMKICAgICAgICBpZiB1c2VfYXR0cmlidXRlcyBpcyBOb25lOgogICAgICAgICAgICB1c2VfYXR0cmlidXRlcyA9IFRydWUKICAgICAgICBpZiBub3QgZW5hYmxlZDoKICAgICAgICAgICAgVHJhY2VyV3JhcHBlci5zZXRfZGlzYWJsZWQoVHJ1ZSkKICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICBGb3JlLllFTExPVwogICAgICAgICAgICAgICAgKyAiVHJhY2Vsb29wIGluc3RydW1lbnRhdGlvbiBpcyBkaXNhYmxlZCB2aWEgaW5pdCBmbGFnIgogICAgICAgICAgICAgICAgKyBGb3JlLlJFU0VUCiAgICAgICAgICAgICkKICAgICAgICAgICAgcmV0dXJuCgogICAgICAgIGlmIG5vdCB0cmFjZV9jb250ZW50OgogICAgICAgICAgICBvcy5lbnZpcm9uWyJUUkFDRUxPT1BfVFJBQ0VfQ09OVEVOVCJdID0gImZhbHNlIgoKICAgICAgICBhcGlfZW5kcG9pbnQgPSBvcy5nZXRlbnYoIlRSQUNFTE9PUF9CQVNFX1VSTCIpIG9yIGFwaV9lbmRwb2ludAogICAgICAgIGFwaV9rZXkgPSBvcy5nZXRlbnYoIlRSQUNFTE9PUF9BUElfS0VZIikgb3IgYXBpX2tleQogICAgICAgIFRyYWNlbG9vcC5fX2FwcF9uYW1lID0gYXBwX25hbWUKCiAgICAgICAgaWYgbm90IGlzX3RyYWNpbmdfZW5hYmxlZCgpOgogICAgICAgICAgICBwcmludChGb3JlLllFTExPVyArICJUcmFjaW5nIGlzIGRpc2FibGVkIiArIEZvcmUuUkVTRVQpCiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBlbmFibGVfY29udGVudF90cmFjaW5nID0gaXNfY29udGVudF90cmFjaW5nX2VuYWJsZWQoKQoKICAgICAgICBpZiBleHBvcnRlciBhbmQgcHJvY2Vzc29yOgogICAgICAgICAgICB3YXJuaW5ncy53YXJuKAogICAgICAgICAgICAgICAgIkJvdGggJ2V4cG9ydGVyJyBhbmQgJ3Byb2Nlc3Nvcicgd2VyZSBwcm92aWRlZCB0byBUcmFjZWxvb3AuaW5pdCgpLiAiCiAgICAgICAgICAgICAgICAiVGhlICdleHBvcnRlcicgd2lsbCBiZSBpZ25vcmVkIOKAlCB5b3VyIHByb2Nlc3NvciBzaG91bGQgYWxyZWFkeSB3cmFwIHRoZSBleHBvcnRlci4gIgogICAgICAgICAgICAgICAgIldyYXAgeW91ciBleHBvcnRlciBpbnNpZGUgdGhlIHByb2Nlc3NvciwgZS5nLjogU2ltcGxlU3Bhbihwcm9jZXNzb3I6bXlfZXhwb3J0ZXIpLiIsCiAgICAgICAgICAgICAgICBVc2VyV2FybmluZywKICAgICAgICAgICAgICAgIHN0YWNrbGV2ZWw9MiwKICAgICAgICAgICAgKQoKICAgICAgICBpZiBleHBvcnRlciBvciBwcm9jZXNzb3I6CiAgICAgICAgICAgIHByaW50KEZvcmUuR1JFRU4gKyAiVHJhY2Vsb29wIGV4cG9ydGluZyB0cmFjZXMgdG8gYSBjdXN0b20gZXhwb3J0ZXIiKQoKICAgICAgICBoZWFkZXJzID0gb3MuZ2V0ZW52KCJUUkFDRUxPT1BfSEVBREVSUyIpIG9yIGhlYWRlcnMKCiAgICAgICAgaWYgaXNpbnN0YW5jZShoZWFkZXJzLCBzdHIpOgogICAgICAgICAgICBoZWFkZXJzID0gcGFyc2VfZW52X2hlYWRlcnMoaGVhZGVycykKCiAgICAgICAgaWYgKAogICAgICAgICAgICBub3QgZXhwb3J0ZXIKICAgICAgICAgICAgYW5kIG5vdCBwcm9jZXNzb3IKICAgICAgICAgICAgYW5kIGFwaV9lbmRwb2ludCA9PSAiaHR0cHM6Ly9hcGkudHJhY2Vsb29wLmNvbSIKICAgICAgICAgICAgYW5kIG5vdCBhcGlfa2V5CiAgICAgICAgKToKICAgICAgICAgICAgcHJpbnQoCiAgICAgICAgICAgICAgICBGb3JlLlJFRAogICAgICAgICAgICAgICAgKyAiRXJyb3I6IE1pc3NpbmcgVHJhY2Vsb29wIEFQSSBrZXksIgogICAgICAgICAgICAgICAgKyAiIGdvIHRvIGh0dHBzOi8vYXBwLnRyYWNlbG9vcC5jb20vc2V0dGluZ3MvYXBpLWtleXMgdG8gY3JlYXRlIG9uZSIKICAgICAgICAgICAgKQogICAgICAgICAgICBwcmludCgiU2V0IHRoZSBUUkFDRUxPT1BfQVBJS19LRVkgZW52aXJvbm1lbnQgdmFyaWFibGUgdG8gdGhlIGtleSIpCiAgICAgICAgICAgIHByaW50KEZvcmUuUkVTRVQpCiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBpZiBub3QgZXhwb3J0ZXIgYW5kIG5vdCBwcm9jZXNzb3IgYW5kIGhlYWRlcnM6CiAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgRm9yZS5HUkVFTgogICAgICAgICAgICAgICAgKyBmIlRyYWNlbG9vcCBleHBvcnRpbmcgdHJhY2VzIHRvIHthcGlfZW5kcG9pbnR9LCBhdXRoZW50aWNhdGluZyB3aXRoIGN1c3RvbSBoZWFkZXJzIgogICAgICAgICAgICApCgogICAgICAgIGlmIGFwaV9rZXkgYW5kIG5vdCBleHBvcnRlciBhbmQgbm90IHByb2Nlc3NvciBhbmQgbm90IGhlYWRlcnM6CiAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgRm9yZS5HUkVFTgogICAgICAgICAgICAgICAgKyBmIlRyYWNlbG9vcCBleHBvcnRpbmcgdHJhY2VzIHRvIHthcGlfZW5kcG9pbnR9IGF1dGhlbnRpY2F0aW5nIHdpdGggYmVhcmVyIHRva2VuIgogICAgICAgICAgICApCiAgICAgICAgICAgIGhlYWRlcnMgPSB7CiAgICAgICAgICAgICAgICAiQXV0aG9yaXphdGlvbiI6IGYiQmVhcmVyIHthcGlfa2V5fSIsCiAgICAgICAgICAgIH0KCiAgICAgICAgcHJpbnQoRm9yZS5SRVNFVCkKCiAgICAgICAgIyBUcmFjZXIgaW5pdAogICAgICAgIHJlc291cmNlX2F0dHJpYnV0ZXMudXBkYXRlKHtTRVJWSUNFX05BTUU6IGFwcF9uYW1lfSkKICAgICAgICBUcmFjZXJXcmFwcGVyLnNldF9zdGF0aWNfcGFyYW1zKAogICAgICAgICAgICByZXNvdXJjZV9hdHRyaWJ1dGVzLCBlbmFibGVfY29udGVudF90cmFjaW5nLCBhcGlfZW5kcG9pbnQsIGhlYWRlcnMKICAgICAgICApCiAgICAgICAgVHJhY2Vsb29wLl9fdHJhY2VyX3dyYXBwZXIgPSBUcmFjZXJXcmFwcGVyKAogICAgICAgICAgICBkaXNhYmxlX2JhdGNoPWRpc2FibGVfYmF0Y2gsCiAgICAgICAgICAgIHByb2Nlc3Nvcj1wcm9jZXNzb3IsCiAgICAgICAgICAgIHByb3BhZ2F0b3I9cHJvcGFnYXRvciwKICAgICAgICAgICAgZXhwb3J0ZXI9ZXhwb3J0ZXIsCiAgICAgICAgICAgIHNhbXBsZXI9c2FtcGxlciwKICAgICAgICAgICAgc2hvdWxkX2VucmljaF9tZXRyaWNzPXNob3VsZF9lbnJpY2hfbWV0cmljcywKICAgICAgICAgICAgaW1hZ2VfdXBsb2FkZXI9aW1hZ2VfdXBsb2FkZXIgb3IgSW1hZ2VVcGxvYWRlcihhcGlfZW5kcG9pbnQsIGFwaV9rZXkpLAogICAgICAgICAgICBpbnN0cnVtZW50cz1pbnN0cnVtZW50cywKICAgICAgICAgICAgYmxvY2tfaW5zdHJ1bWVudHM9YmxvY2tfaW5zdHJ1bWVudHMsCiAgICAgICAgICAgIHNwYW5fcG9zdHByb2Nlc3NfY2FsbGJhY2s9c3Bhbl9wb3N0cHJvY2Vzc19jYWxsYmFjaywKICAgICAgICAgICAgdXNlX2F0dHJpYnV0ZXM9dXNlX2F0dHJpYnV0ZXMsCiAgICAgICAgKQoKICAgICAgICBtZXRyaWNzX2Rpc2FibGVkX2J5X2NvbmZpZyA9IG5vdCBpc19tZXRyaWNzX2VuYWJsZWQoKQogICAgICAgIGhhc19jdXN0b21fc3BhbnNfcGlwZWxpbmUgPSBwcm9jZXNzb3Igb3IgZXhwb3J0ZXIKICAgICAgICBjdXN0b21fdHJhY2Vfd2l0aG91dF9jdXN0b21fbWV0cmljcyA9IGhhc19jdXN0b21fc3BhbnNfcGlwZWxpbmUgYW5kIG5vdCBtZXRyaWNzX2V4cG9ydGVyCgogICAgICAgIGlmIG1ldHJpY3NfZGlzYWJsZWRfYnlfY29uZmlnIG9yIGN1c3RvbV90cmFjZV93aXRob3V0X2N1c3RvbV9tZXRyaWNzOgogICAgICAgICAgICBwcmludChGb3JlLllFTExPVyArICJNZXRyaWNzIGFyZSBkaXNhYmxlZCIgKyBGb3JlLlJFU0VUKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIG1ldHJpY3NfZW5kcG9pbnQgPSBvcy5nZXRlbnYoIlRSQUNFTE9PUF9NRVRSSUNTX0VORFBPSU5UIikgb3IgYXBpX2VuZHBvaW50CiAgICAgICAgICAgIG1ldHJpY3NfaGVhZGVycyA9ICgKICAgICAgICAgICAgICAgIG9zLmdldGVudigiVFJBQ0VMT09QX01FVFJJQ1NfSEVBREVSUyIpIG9yIG1ldHJpY3NfaGVhZGVycyBvciBoZWFkZXJzCiAgICAgICAgICAgICkKICAgICAgICAgICAgaWYgbWV0cmljc19leHBvcnRlciBvciBwcm9jZXNzb3I6CiAgICAgICAgICAgICAgICBwcmludChGb3JlLkdSRUVOICsgIlRyYWNlbG9vcCBleHBvcnRpbmcgbWV0cmljcyB0byBhIGN1c3RvbSBleHBvcnRlciIpCgogICAgICAgICAgICBNZXRyaWNzV3JhcHBlci5zZXRfc3RhdGljX3BhcmFtcygKICAgICAgICAgICAgICAgIHJlc291cmNlX2F0dHJpYnV0ZXMsIG1ldHJpY3NfZW5kcG9pbnQsIG1ldHJpY3NfaGVhZGVycwogICAgICAgICAgICApCiAgICAgICAgICAgIFRyYWNlbG9vcC5fX21ldHJpY3Nfd3JhcHBlciA9IE1ldHJpY3NXcmFwcGVyKGV4cG9ydGVyPW1ldHJpY3NfZXhwb3J0ZXIpCgogICAgICAgIGlmIGlzX2xvZ2dpbmdfZW5hYmxlZCgpIGFuZCAobG9nZ2luZ19leHBvcnRlciBvciBub3QgZXhwb3J0ZXIpOgogICAgICAgICAgICBsb2dnaW5nX2VuZHBvaW50ID0gb3MuZ2V0ZW52KCJUUkFDRUxPT1BfTE9HR0lOR19FTkRQT0lOVCIpIG9yIGFwaV9lbmRwb2ludAogICAgICAgICAgICBsb2dnaW5nX2hlYWRlcnMgPSAoCiAgICAgICAgICAgICAgICBvcy5nZXRlbnYoIlRSQUNFTE9PUF9MT0dHSU5HX0hFQURFUlMiKSBvciBsb2dnaW5nX2hlYWRlcnMgb3IgaGVhZGVycwogICAgICAgICAgICApCiAgICAgICAgICAgIGlmIGxvZ2dpbmdfZXhwb3J0ZXIgb3IgcHJvY2Vzc29yOgogICAgICAgICAgICAgICAgcHJpbnQoRm9yZS5HUkVFTiArICJUcmFjZWxvb3AgZXhwb3J0aW5nIGxvZ3MgdG8gYSBjdXN0b20gZXhwb3J0ZXIiKQoKICAgICAgICAgICAgTG9nZ2VyV3JhcHBlci5zZXRfc3RhdGljX3BhcmFtcygKICAgICAgICAgICAgICAgIHJlc291cmNlX2F0dHJpYnV0ZXMsIGxvZ2dpbmdfZW5kcG9pbnQsIGxvZ2dpbmdfaGVhZGVycwogICAgICAgICAgICApCiAgICAgICAgICAgIFRyYWNlbG9vcC5fX2xvZ2dlcl93cmFwcGVyID0gTG9nZ2VyV3JhcHBlcihleHBvcnRlcj1sb2dnaW5nX2V4cG9ydGVyKQoKICAgICAgICBpZiAoCiAgICAgICAgICAgIChhcGlfZW5kcG9pbnQuZmluZCgidHJhY2Vsb29wLmNvbSIpICE9IC0xIG9yIGVuZHBvaW50X2lzX3RyYWNlbG9vcCkKICAgICAgICAgICAgYW5kIGFwaV9rZXkKICAgICAgICAgICAgYW5kIChleHBvcnRlciBpcyBOb25lKQogICAgICAgICAgICBhbmQgKHByb2Nlc3NvciBpcyBOb25lKQogICAgICAgICk6CiAgICAgICAgICAgIGlmIHRyYWNlbG9vcF9zeW5jX2VuYWJsZWQ6CiAgICAgICAgICAgICAgICBUcmFjZWxvb3AuX19mZXRjaGVyID0gRmV0Y2hlcihiYXNlX3VybD1hcGlfZW5kcG9pbnQsIGFwaV9rZXk9YXBpX2tleSkKICAgICAgICAgICAgICAgIFRyYWNlbG9vcC5fX2ZldGNoZXIucnVuKCkKICAgICAgICAgICAgICAgIHByaW50KAogICAgICAgICAgICAgICAgICAgIEZvcmUuR1JFRU4KICAgICAgICAgICAgICAgICAgICArICJUcmFjZWxvb3Agc3luY2luZyBjb25maWd1cmF0aW9uIGFuZCBwcm9tcHRzIgogICAgICAgICAgICAgICAgICAgICsgRm9yZS5SRVNFVAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBUcmFjZWxvb3AuX19jbGllbnQgPSBDbGllbnQoCiAgICAgICAgICAgICAgICBhcGlfa2V5PWFwaV9rZXksIGFwcF9uYW1lPWFwcF9uYW1lLCBhcGlfZW5kcG9pbnQ9YXBpX2VuZHBvaW50CiAgICAgICAgICAgICkKICAgICAgICAgICAgcmV0dXJuIFRyYWNlbG9vcC5fX2NsaWVudAoKICAgIEBzdGF0aWNtZXRob2QKICAgIGRlZiBzZXRfYXNzb2NpYXRpb25fcHJvcGVydGllcyhwcm9wZXJ0aWVzOiBkaWN0KSAtPiBOb25lOgogICAgICAgIHNldF9hc3NvY2lhdGlvbl9wcm9wZXJ0aWVzKHByb3BlcnRpZXMpCgogICAgZGVmIHNldF9wcm9tcHQodGVtcGxhdGU6IHN0ciwgdmFyaWFibGVzOiBkaWN0LCB2ZXJzaW9uOiBpbnQpOgogICAgICAgIHNldF9leHRlcm5hbF9wcm9tcHRfdHJhY2luZ19jb250ZXh0KHRlbXBsYXRlLCB2YXJpYWJsZXMsIHZlcnNpb24pCgogICAgQHN0YXRpY21ldGhvZAogICAgZGVmIGdldF9kZWZhdWx0X3NwYW5fcHJvY2Vzc29yKAogICAgICAgIGRpc2FibGVfYmF0Y2g6IGJvb2wgPSBGYWxzZSwKICAgICAgICBhcGlfZW5kcG9pbnQ6IE9wdGlvbmFsW3N0cl0gPSBOb25lLAogICAgICAgIGFwaV9rZXk6IE9wdGlvbmFsW3N0cl0gPSBOb25lLAogICAgICAgIGhlYWRlcnM6IE9wdGlvbmFsW0RpY3Rbc3RyLCBzdHJdXSA9IE5vbmUsCiAgICAgICAgZXhwb3J0ZXI6IE9wdGlvbmFsW1NwYW5FeHBvcnRlcl0gPSBOb25lCiAgICApIC0+IFNwYW5Qcm9jZXNzb3I6CiAgICAgICAgIiIiCiAgICAgICAgQ3JlYXRlcyBhbmQgcmV0dXJucyB0aGUgZGVmYXVsdCBUcmFjZWxvb3Agc3BhbiBwcm9jZXNzb3IuCgogICAgICAgIFRoaXMgZnVuY3Rpb24gYWxsb3dzIHVzZXJzIHRvIGdldCB0aGUgZGVmYXVsdCBUcmFjZWxvb3Agc3BhbiBwcm9jZXNzb3IKICAgICAgICB0byBjb21iaW5lIGl0IHdpdGggdGhlaXIgY3VzdG9tIHByb2Nlc3NvcnMgd2hlbiB1c2luZyB0aGUgcHJvY2Vzc29ycyBwYXJhbWV0ZXIuCgogICAgICAgIEFyZ3M6CiAgICAgICAgICAgIGRpc2FibGVfYmF0Y2g6IElmIFRydWUsIHVzZXMgU2ltcGxlU3BhblByb2Nlc3Nvciwgb3RoZXJ3aXNlIEJhdGNoU3BhblByb2Nlc3NvcgogICAgICAgICAgICBhcGlfZW5kcG9pbnQ6IFRoZSBlbmRwb2ludCBVUkwgZm9yIHRoZSBleHBvcnRlciAodXNlcyBjdXJyZW50IGNvbmZpZyBpZiBOb25lKQogICAgICAgICAgICBoZWFkZXJzOiBIZWFkZXJzIGZvciB0aGUgZXhwb3J0ZXIgKHVzZXMgY3VycmVudCBjb25maWcgaWYgTm9uZSkKICAgICAgICAgICAgZXhwb3J0ZXI6IEN1c3RvbSBleHBvcnRlciB0byB1c2UgKGNyZWF0ZXMgZGVmYXVsdCBpZiBOb25lKQoKICAgICAgICBSZXR1cm5zOgogICAgICAgICAgICBTcGFuUHJvY2Vzc29yOiBUaGUgZGVmYXVsdCBUcmFjZWxvb3Agc3BhbiBwcm9jZXNzb3IKCiAgICAgICAgRXhhbXBsZToKICAgICAgICAgICAgIyBHZXQgdGhlIGRlZmF1bHQgcHJvY2Vzc29yIGFuZCBjb21iaW5lIHdpdGggY3VzdG9tIG9uZQogICAgICAgICAgICBkZWZhdWx0X3Byb2Nlc3NvciA9IFRyYWNlbG9vcC5nZXRfZGVmYXVsdF9zcGFuX3Byb2Nlc3NvcigpCiAgICAgICAgICAgIGN1c3RvbV9wcm9jZXNzb3IgPSBNeUN1c3RvbVNwYW5Qcm9jZXNzb3IoKQoKICAgICAgICAgICAgVHJhY2Vsb29wLmluaXQoCiAgICAgICAgICAgICAgICBwcm9jZXNzb3JzPVtkZWZhdWx0X3Byb2Nlc3NvciwgY3VzdG9tX3Byb2Nlc3Nvcl0KICAgICAgICAgICAgKQogICAgICAgICIiIgogICAgICAgIGZyb20gdHJhY2Vsb29wLnNkay50cmFjaW5nLnRyYWNpbmcgaW1wb3J0IGdldF9kZWZhdWx0X3NwYW5fcHJvY2Vzc29yCiAgICAgICAgaWYgaGVhZGVycyBpcyBOb25lOgogICAgICAgICAgICBpZiBhcGlfa2V5IGlzIE5vbmU6CiAgICAgICAgICAgICAgICBhcGlfa2V5ID0gb3MuZ2V0ZW52KCJUUkFDRUxPT1BfQVBJS19LRVkiKQogICAgICAgICAgICBoZWFkZXJzID0gewogICAgICAgICAgICAgICAgIkF1dGhvcml6YXRpb24iOiBmIkJlYXJlciB7YXBpX2tleX0iLAogICAgICAgICAgICB9CiAgICAgICAgaWYgYXBpX2VuZHBvaW50IGlzIE5vbmU6CiAgICAgICAgICAgIGFwaV9lbmRwb2ludCA9IG9zLmdldGVudigiVFJBQ0VMT09QX0JBU0VfVVJMIikgb3IgImh0dHBzOi8vYXBpLnRyYWNlbG9vcC5jb20iCiAgICAgICAgcmV0dXJuIGdldF9kZWZhdWx0X3NwYW5fcHJvY2Vzc29yKGRpc2FibGVfYmF0Y2gsIGFwaV9lbmRwb2ludCwgaGVhZGVycywgZXhwb3J0ZXIpCgogICAgQHN0YXRpY21ldGhvZAogICAgZGVmIGdldCgpOgogICAgICAgICIiIgogICAgICAgIFJldHVybnMgdGhlIHNoYXJlZCBTREsgY2xpZW50IGluc3RhbmNlLCB1c2luZyB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi4KCiAgICAgICAgVG8gdXNlIHRoZSBTREsgYXMgYSBzaW5nbGV0b24sIGZpcnN0IG1ha2Ugc3VyZSB5b3UgaGF2ZSBjYWxsZWQgOmZ1bmM6YFRyYWNlbG9vcC5pbml0KClgCiAgICAgICAgYXQgc3RhcnR1cCB0aW1lLiBUaGVuIGBnZXQoKWAgd2lsbCByZXR1cm4gdGhlIHNhbWUgc2hhcmVkIDpjbGFzczpgVHJhY2Vsb29wLmNsaWVudC5DbGllbnRgCiAgICAgICAgaW5zdGFuY2UgZWFjaCB0aW1lLiBUaGUgY2xpZW50IHdpbGwgYmUgaW5pdGlhbGl6ZWQgaWYgaXQgaGFzIG5vdCBiZWVuIGFscmVhZHkuCgogICAgICAgIElmIHlvdSBuZWVkIHRvIGNyZWF0ZSBtdWx0aXBsZSBjbGllbnQgaW5zdGFuY2VzIHdpdGggZGlmZmVyZW50IGNvbmZpZ3VyYXRpb25zLCBpbnN0ZWFkIG9mIHRoaXMKICAgICAgICBzaW5nbGV0b24gYXBwcm9hY2ggeW91IGNhbiBjYWxsIHRoZSA6Y2xhc3M6YFRyYWNlbG9vcC5jbGllbnQuQ2xpZW50YCBjb25zdHJ1Y3RvciBkaXJlY3RseSBpbnN0ZWFkLgogICAgICAgICIiIgogICAgICAgIGlmIG5vdCBUcmFjZWxvb3AuX19jbGllbnQ6CiAgICAgICAgICAgIHJhaXNlIEV4Y2VwdGlvbigKICAgICAgICAgICAgICAgICJDbGllbnQgbm90IGluaXRpYWxpemVkLCB5b3Ugc2hvdWxkIGNhbGwgVHJhY2Vsb29wLmluaXQoKSBmaXJzdC4gIgogICAgICAgICAgICAgICAgIklmIHlvdSBhcmUgc3RpbGwgZ2V0dGluZyB0aGlzIGVycm9yIC0geW91IGFyZSBtaXNzaW5nIHRoZSBhcGkga2V5IgogICAgICAgICAgICApCiAgICAgICAgcmV0dXJuIFRyYWNlbG9vcC5fX2NsaWVudAo= \ No newline at end of file +import os +import sys +import warnings +from pathlib import Path + +from typing import Callable, List, Optional, Set, Union +from colorama import Fore +from opentelemetry.sdk.trace import SpanProcessor, ReadableSpan +from opentelemetry.sdk.trace.sampling import Sampler +from opentelemetry.sdk.trace.export import SpanExporter +from opentelemetry.sdk.metrics.export import MetricExporter +from opentelemetry.sdk._logs.export import LogExporter +from opentelemetry.sdk.resources import SERVICE_NAME +from opentelemetry.propagators.textmap import TextMapPropagator +from opentelemetry.util.re import parse_env_headers + +from traceloop.sdk.images.image_uploader import ImageUploader +from traceloop.sdk.metrics.metrics import MetricsWrapper +from traceloop.sdk.logging.logging import LoggerWrapper +from traceloop.sdk.instruments import Instruments +from traceloop.sdk.config import ( + is_content_tracing_enabled, + is_tracing_enabled, + is_metrics_enabled, + is_logging_enabled, +) +from traceloop.sdk.fetcher import Fetcher +from traceloop.sdk.tracing.tracing import ( + TracerWrapper, + set_association_properties, + set_external_prompt_tracing_context, +) +from typing import Dict +from traceloop.sdk.client.client import Client +from traceloop.sdk.associations.associations import AssociationProperty as AssociationProperty + + +class Traceloop: + AUTO_CREATED_KEY_PATH = str( + Path.home() / ".cache" / "traceloop" / "auto_created_key" + ) + AUTO_CREATED_URL = str(Path.home() / ".cache" / "traceloop" / "auto_created_url") + + __tracer_wrapper: TracerWrapper + __fetcher: Optional[Fetcher] = None + __app_name: Optional[str] = None + __client: Optional[Client] = None + + @staticmethod + def init( + app_name: str = sys.argv[0], + api_endpoint: str = "https://api.traceloop.com", + api_key: Optional[str] = None, + enabled: bool = True, + headers: Dict[str, str] = {}, + disable_batch=False, + telemetry_enabled: bool = True, + exporter: Optional[SpanExporter] = None, + metrics_exporter: MetricExporter = None, + metrics_headers: Dict[str, str] = None, + logging_exporter: LogExporter = None, + logging_headers: Dict[str, str] = None, + processor: Optional[Union[SpanProcessor, List[SpanProcessor]]] = None, + propagator: TextMapPropagator = None, + sampler: Optional[Sampler] = None, + traceloop_sync_enabled: bool = False, + should_enrich_metrics: bool = True, + resource_attributes: dict = {}, + instruments: Optional[Set[Instruments]] = None, + block_instruments: Optional[Set[Instruments]] = None, + image_uploader: Optional[ImageUploader] = None, + span_postprocess_callback: Optional[Callable[[ReadableSpan], None]] = None, + endpoint_is_traceloop: Optional[bool] = False, + use_attributes: Optional[bool] = None, + use_legacy_attributes: Optional[bool] = None, + trace_content: bool = True, + ) -> Optional[Client]: + """Initialize Traceloop tracing, metrics, and instrumentation. + + Args: + use_attributes: Controls how prompts/completions are emitted by the + bundled instrumentations. + If ``True`` (default), they are set as span attributes + (``gen_ai.input.messages`` / ``gen_ai.output.messages``), per the current + OpenTelemetry GenAI semantic-convention spec. + If ``False``, they are emitted as OTel log events instead. This path + requires the application to have configured an ``EventLoggerProvider`` + (via ``opentelemetry._events.set_event_logger_provider``); otherwise + events have nowhere to go and no prompt/completion data will be recorded. + use_legacy_attributes: Deprecated alias for ``use_attributes``. Will be + removed in a future release. + trace_content: Whether to send sensitive content (prompts, + completions) to the tracing backend. Defaults to ``True``. + When set to ``False``, overrides the ``TRACELOOP_TRACE_CONTENT`` + environment variable and disables content capture across all + instrumentations. + """ + if use_attributes is not None and use_legacy_attributes is not None: + raise TypeError( + "Cannot pass both `use_attributes` and `use_legacy_attributes`; " + "`use_legacy_attributes` is deprecated, use `use_attributes` instead." + ) + if use_legacy_attributes is not None: + warnings.warn( + "`use_legacy_attributes` is deprecated and will be removed in a " + "future release; use `use_attributes` instead. The current OTel " + "GenAI spec emits prompts/completions as span attributes " + "(`gen_ai.input.messages` / `gen_ai.output.messages`), which is " + "what `use_attributes=True` (the default) does. " + "`use_attributes=False` opts into the events path instead.", + DeprecationWarning, + stacklevel=2, + ) + use_attributes = use_legacy_attributes + if use_attributes is None: + use_attributes = True + if not enabled: + TracerWrapper.set_disabled(True) + print( + Fore.YELLOW + + "Traceloop instrumentation is disabled via init flag" + + Fore.RESET + ) + return + + if not trace_content: + os.environ["TRACELOOP_TRACE_CONTENT"] = "false" + + api_endpoint = os.getenv("TRACELOOP_BASE_URL") or api_endpoint + api_key = os.getenv("TRACELOOP_API_KEY") or api_key + Traceloop.__app_name = app_name + + if not is_tracing_enabled(): + print(Fore.YELLOW + "Tracing is disabled" + Fore.RESET) + return + + enable_content_tracing = is_content_tracing_enabled() + + if exporter and processor: + warnings.warn( + "Both 'exporter' and 'processor' were provided to Traceloop.init(). " + "The 'exporter' will be ignored — your processor should already wrap the exporter. " + "Wrap your exporter inside the processor, e.g.: SimpleSpanProcessor(my_exporter).", + UserWarning, + stacklevel=2, + ) + + if exporter or processor: + print(Fore.GREEN + "Traceloop exporting traces to a custom exporter") + + headers = os.getenv("TRACELOOP_HEADERS") or headers + + if isinstance(headers, str): + headers = parse_env_headers(headers) + + if ( + not exporter + and not processor + and api_endpoint == "https://api.traceloop.com" + and not api_key + ): + print( + Fore.RED + + "Error: Missing Traceloop API key," + + " go to https://app.traceloop.com/settings/api-keys to create one" + ) + print("Set the TRACELOOP_API_KEY environment variable to the key") + print(Fore.RESET) + return + + if not exporter and not processor and headers: + print( + Fore.GREEN + + f"Traceloop exporting traces to {api_endpoint}, authenticating with custom headers" + ) + + if api_key and not exporter and not processor and not headers: + print( + Fore.GREEN + + f"Traceloop exporting traces to {api_endpoint} authenticating with bearer token" + ) + headers = { + "Authorization": f"Bearer {api_key}", + } + + print(Fore.RESET) + + # Tracer init + resource_attributes.update({SERVICE_NAME: app_name}) + TracerWrapper.set_static_params( + resource_attributes, enable_content_tracing, api_endpoint, headers + ) + Traceloop.__tracer_wrapper = TracerWrapper( + disable_batch=disable_batch, + processor=processor, + propagator=propagator, + exporter=exporter, + sampler=sampler, + should_enrich_metrics=should_enrich_metrics, + image_uploader=image_uploader or ImageUploader(api_endpoint, api_key), + instruments=instruments, + block_instruments=block_instruments, + span_postprocess_callback=span_postprocess_callback, + use_attributes=use_attributes, + ) + + metrics_disabled_by_config = not is_metrics_enabled() + has_custom_spans_pipeline = processor or exporter + custom_trace_without_custom_metrics = has_custom_spans_pipeline and not metrics_exporter + + if metrics_disabled_by_config or custom_trace_without_custom_metrics: + print(Fore.YELLOW + "Metrics are disabled" + Fore.RESET) + else: + metrics_endpoint = os.getenv("TRACELOOP_METRICS_ENDPOINT") or api_endpoint + metrics_headers = ( + os.getenv("TRACELOOP_METRICS_HEADERS") or metrics_headers or headers + ) + if metrics_exporter or processor: + print(Fore.GREEN + "Traceloop exporting metrics to a custom exporter") + + MetricsWrapper.set_static_params( + resource_attributes, metrics_endpoint, metrics_headers + ) + Traceloop.__metrics_wrapper = MetricsWrapper(exporter=metrics_exporter) + + if is_logging_enabled() and (logging_exporter or not exporter): + logging_endpoint = os.getenv("TRACELOOP_LOGGING_ENDPOINT") or api_endpoint + logging_headers = ( + os.getenv("TRACELOOP_LOGGING_HEADERS") or logging_headers or headers + ) + if logging_exporter or processor: + print(Fore.GREEN + "Traceloop exporting logs to a custom exporter") + + LoggerWrapper.set_static_params( + resource_attributes, logging_endpoint, logging_headers + ) + Traceloop.__logger_wrapper = LoggerWrapper(exporter=logging_exporter) + + if ( + (api_endpoint.find("traceloop.com") != -1 or endpoint_is_traceloop) + and api_key + and (exporter is None) + and (processor is None) + ): + if traceloop_sync_enabled: + Traceloop.__fetcher = Fetcher(base_url=api_endpoint, api_key=api_key) + Traceloop.__fetcher.run() + print( + Fore.GREEN + + "Traceloop syncing configuration and prompts" + + Fore.RESET + ) + Traceloop.__client = Client( + api_key=api_key, app_name=app_name, api_endpoint=api_endpoint + ) + return Traceloop.__client + + @staticmethod + def set_association_properties(properties: dict) -> None: + set_association_properties(properties) + + def set_prompt(template: str, variables: dict, version: int): + set_external_prompt_tracing_context(template, variables, version) + + @staticmethod + def get_default_span_processor( + disable_batch: bool = False, + api_endpoint: Optional[str] = None, + api_key: Optional[str] = None, + headers: Optional[Dict[str, str]] = None, + exporter: Optional[SpanExporter] = None + ) -> SpanProcessor: + """ + Creates and returns the default Traceloop span processor. + + This function allows users to get the default Traceloop span processor + to combine it with their custom processors when using the processors parameter. + + Args: + disable_batch: If True, uses SimpleSpanProcessor, otherwise BatchSpanProcessor + api_endpoint: The endpoint URL for the exporter (uses current config if None) + headers: Headers for the exporter (uses current config if None) + exporter: Custom exporter to use (creates default if None) + + Returns: + SpanProcessor: The default Traceloop span processor + + Example: + # Get the default processor and combine with custom one + default_processor = Traceloop.get_default_span_processor() + custom_processor = MyCustomSpanProcessor() + + Traceloop.init( + processors=[default_processor, custom_processor] + ) + """ + from traceloop.sdk.tracing.tracing import get_default_span_processor + if headers is None: + if api_key is None: + api_key = os.getenv("TRACELOOP_API_KEY") + headers = { + "Authorization": f"Bearer {api_key}", + } + if api_endpoint is None: + api_endpoint = os.getenv("TRACELOOP_BASE_URL") or "https://api.traceloop.com" + return get_default_span_processor(disable_batch, api_endpoint, headers, exporter) + + @staticmethod + def get(): + """ + Returns the shared SDK client instance, using the current global configuration. + + To use the SDK as a singleton, first make sure you have called :func:`Traceloop.init()` + at startup time. Then ``get()`` will return the same shared :class:`Traceloop.client.Client` + instance each time. The client will be initialized if it has not been already. + + If you need to create multiple client instances with different configurations, instead of this + singleton approach you can call the :class:`Traceloop.client.Client` constructor directly instead. + """ + if not Traceloop.__client: + raise Exception( + "Client not initialized, you should call Traceloop.init() first. " + "If you are still getting this error - you are missing the api key" + ) + return Traceloop.__client