diff --git a/packages/traceloop-sdk/tests/test_sdk_initialization.py b/packages/traceloop-sdk/tests/test_sdk_initialization.py index 18ef5d7d0f..9f41bb0e01 100644 --- a/packages/traceloop-sdk/tests/test_sdk_initialization.py +++ b/packages/traceloop-sdk/tests/test_sdk_initialization.py @@ -358,3 +358,51 @@ def probe(): del TracerWrapper.instance if saved_instance is not None: TracerWrapper.instance = saved_instance + + +def test_trace_content_default_true(isolated_tracer_wrapper): + """trace_content defaults to True when not passed, equivalent to the + default env var behaviour.""" + Traceloop.init(exporter=InMemorySpanExporter(), disable_batch=True) + + assert TracerWrapper.enable_content_tracing is True + + +def test_trace_content_false_disables_content(isolated_tracer_wrapper): + """Explicit trace_content=False must disable content tracing.""" + Traceloop.init( + exporter=InMemorySpanExporter(), + disable_batch=True, + trace_content=False, + ) + + assert TracerWrapper.enable_content_tracing is False + + +def test_trace_content_env_var_still_works(isolated_tracer_wrapper): + """When trace_content is not passed, the TRACELOOP_TRACE_CONTENT env var + must still be honoured.""" + import os + + os.environ["TRACELOOP_TRACE_CONTENT"] = "false" + try: + Traceloop.init(exporter=InMemorySpanExporter(), disable_batch=True) + assert TracerWrapper.enable_content_tracing is False + finally: + os.environ.pop("TRACELOOP_TRACE_CONTENT", None) + + +def test_trace_content_overrides_env_var(isolated_tracer_wrapper): + """Explicit trace_content=False must override TRACELOOP_TRACE_CONTENT=true.""" + import os + + os.environ["TRACELOOP_TRACE_CONTENT"] = "true" + try: + Traceloop.init( + exporter=InMemorySpanExporter(), + disable_batch=True, + trace_content=False, + ) + assert TracerWrapper.enable_content_tracing is False + finally: + os.environ.pop("TRACELOOP_TRACE_CONTENT", None) diff --git a/packages/traceloop-sdk/traceloop/sdk/__init__.py b/packages/traceloop-sdk/traceloop/sdk/__init__.py index 6663429d99..c93041ecb1 100644 --- a/packages/traceloop-sdk/traceloop/sdk/__init__.py +++ b/packages/traceloop-sdk/traceloop/sdk/__init__.py @@ -73,6 +73,7 @@ def init( endpoint_is_traceloop: Optional[bool] = False, use_attributes: Optional[bool] = None, use_legacy_attributes: Optional[bool] = None, + trace_content: Optional[bool] = None, ) -> Optional[Client]: """Initialize Traceloop tracing, metrics, and instrumentation. @@ -88,6 +89,12 @@ def init( 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: Controls whether prompts, completions, and other + sensitive content are sent to Traceloop. When ``False``, only + metadata (token counts, latency, model name, etc.) is traced + and the actual content is omitted. Defaults to ``True``. + Falls back to the ``TRACELOOP_TRACE_CONTENT`` environment + variable if not provided. """ if use_attributes is not None and use_legacy_attributes is not None: raise TypeError( @@ -125,7 +132,10 @@ def init( print(Fore.YELLOW + "Tracing is disabled" + Fore.RESET) return - enable_content_tracing = is_content_tracing_enabled() + if trace_content is not None: + enable_content_tracing = trace_content + else: + enable_content_tracing = is_content_tracing_enabled() if exporter and processor: warnings.warn(