Skip to content

feat(sdk): allow disabling prompt sending as argument to Traceloop.init() #1620

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions packages/traceloop-sdk/tests/test_disabling_prompt_feature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import os

import pytest

from traceloop.sdk import Traceloop
from openai import OpenAI
from opentelemetry.semconv.ai import SpanAttributes
from traceloop.sdk.decorators import workflow, task

@pytest.fixture
def traceloop_client():
return Traceloop(trace_content=False)

@pytest.fixture
def openai_client():
return OpenAI()

@pytest.mark.vcr
def test_simple_workflow(exporter, openai_client):
@task(name="joke_creation")
def create_joke():
completion = openai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": "Tell me a joke about opentelemetry"}
],
)
return completion.choices[0].message.content

@workflow(name="pirate_joke_generator")
def joke_workflow():
create_joke()

joke_workflow()

spans = exporter.get_finished_spans()
assert [span.name for span in spans] == [
"openai.chat",
"joke_creation.task",
"pirate_joke_generator.workflow",
]
open_ai_span = spans[0]
assert open_ai_span.attributes[SpanAttributes.LLM_USAGE_PROMPT_TOKENS] == 15
assert not open_ai_span.attributes.get(f"{SpanAttributes.LLM_PROMPTS}.0.content")
assert not open_ai_span.attributes.get(f"{SpanAttributes.LLM_PROMPTS}.0.completions")
312 changes: 311 additions & 1 deletion packages/traceloop-sdk/traceloop/sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import sys
import logging
from pathlib import Path

from typing import Optional, Set
Expand Down Expand Up @@ -53,6 +54,7 @@ def init(
should_enrich_metrics: bool = True,
resource_attributes: dict = {},
instruments: Optional[Set[Instruments]] = None,
trace_content: bool = True # trace_content controls whether instrumentations are sending sensitive content
) -> None:
Telemetry()

Expand All @@ -76,7 +78,315 @@ def init(
print(Fore.YELLOW + "Tracing is disabled" + Fore.RESET)
return

enable_content_tracing = is_content_tracing_enabled()
enable_content_tracing = trace_content

# if trace content is false, disable sensitive content in instruments
if enable_content_tracing is False:
# pass configuration through all instruments
if instruments is not None:
for instrument in instruments:
if instrument == Instruments.OPENAI:
# set instrumentation
try:
from opentelemetry.instrumentation.openai import OpenAIInstrumentor

# set sensitive data off
instrumentor = OpenAIInstrumentor(
enrich_assistant = False,
enrich_token_usage = False,
)

if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()

except Exception as e:
logging.error(f"Error initializing OpenAI instrumentor: {e}")
Telemetry().log_exception(e)

elif instrument == Instruments.ANTHROPIC:
try:
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor

instrumentor = AnthropicInstrumentor(
enrich_token_usage = False,
)

if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Anthropic instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.COHERE:
try:
from opentelemetry.instrumentation.cohere import CohereInstrumentor

instrumentor = CohereInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Cohere instrumentor: {e}")
Telemetry().log_exception(e)

elif instrument == Instruments.PINECONE:
try:
from opentelemetry.instrumentation.pinecone import PineconeInstrumentor

instrumentor = PineconeInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Pinecone instrumentor: {e}")
Telemetry().log_exception(e)

elif instrument == Instruments.CHROMA:
try:
from opentelemetry.instrumentation.chromadb import ChromaInstrumentor

instrumentor = ChromaInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Chroma instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.GOOGLE_GENERATIVEAI:
try:
from opentelemetry.instrumentation.google_generativeai import (
GoogleGenerativeAiInstrumentor,
)

instrumentor = GoogleGenerativeAiInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Google Generative AI instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.LANGCHAIN:
try:
from opentelemetry.instrumentation.langchain import LangchainInstrumentor

instrumentor = LangchainInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Langchain instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.MISTRAL:
try:
from opentelemetry.instrumentation.MISTRAL import MistralAiInstrumentor

instrumentor = MistralAiInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Mistral instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.OLLAMA:
try:
from opentelemetry.instrumentation.pinecone import OllamaInstrumentor

instrumentor = OllamaInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Ollama instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.LLAMA_INDEX:
try:
from opentelemetry.instrumentation.llamaindex import LlamaIndexInstrumentor

instrumentor = LlamaIndexInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Llama Index instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.MILVUS:
try:
from opentelemetry.instrumentation.milvus import MilvusInstrumentor

instrumentor = MilvusInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Milvus instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.TRANSFORMERS:
try:
from opentelemetry.instrumentation.transformers import (
TransformersInstrumentor,
)

instrumentor = TransformersInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Transformers instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.TOGETHER:
try:
from opentelemetry.instrumentation.together import TogetherAiInstrumentor

instrumentor = TogetherAiInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Together instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.REQUESTS:
try:
from opentelemetry.instrumentation.requests import RequestsInstrumentor

instrumentor = RequestsInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Requests instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.URLLIB3:
try:
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor

instrumentor = URLLib3Instrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing urllib3 instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.PYMYSQL:
try:
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor

instrumentor = SQLAlchemyInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing SQLAlchemy instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.BEDROCK:
try:
from opentelemetry.instrumentation.bedrock import BedrockInstrumentor

instrumentor = BedrockInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Bedrock instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.REPLICATE:
try:
from opentelemetry.instrumentation.replicate import ReplicateInstrumentor

instrumentor = ReplicateInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Replicate instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.VERTEXAI:
try:
from opentelemetry.instrumentation.vertexai import VertexAiInstrumentor

instrumentor = VertexAiInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Vertex AI instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.WATSONX:
try:
from opentelemetry.instrumentation.watsonx import WatsonxInstrumentor

instrumentor = WatsonxInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing WatsonX instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.WEAVIATE:
try:
from opentelemetry.instrumentation.weaviate import WeaviateInstrumentor

instrumentor = WeaviateInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Weaviate instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.ALEPHALPHA:
try:
from opentelemetry.instrumentation.alephalpha import AlephAlphaInstrumentor

instrumentor = AlephAlphaInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing AlephAlpha instrumentor: {e}")
Telemetry().log_exception(e)
elif instrument == Instruments.MARQO:
try:
from opentelemetry.instrumentation.marqo import MarqoInstrumentor

instrumentor = MarqoInstrumentor(

)
if not instrumentor.is_instrumented_by_opentelemetry:
instrumentor.instrument()
except Exception as e:
logging.error(f"Error initializing Marqo instrumentor: {e}")
Telemetry().log_exception(e)
else:
print(
Fore.RED
+ "Warning: "
+ instrument
+ " instrumentation does not exist."
)
print(
# good error message
"Usage:\n"
+ "from traceloop.sdk.instruments import Instruments\n"
)
print(Fore.RESET)

if exporter or processor:
print(Fore.GREEN + "Traceloop exporting traces to a custom exporter")
Expand Down