diff --git a/ddtrace/contrib/langchain/patch.py b/ddtrace/contrib/langchain/patch.py index 5571058c29c..5ce0ac14514 100644 --- a/ddtrace/contrib/langchain/patch.py +++ b/ddtrace/contrib/langchain/patch.py @@ -7,6 +7,7 @@ import langchain from langchain.callbacks.openai_info import get_openai_token_cost_for_model +from pydantic import SecretStr from ddtrace import config from ddtrace.constants import ERROR_TYPE @@ -140,8 +141,11 @@ def _extract_model_name(instance): def _format_api_key(api_key): - # type: (str) -> str + # type: (str | SecretStr) -> str """Obfuscate a given LLM provider API key by returning the last four characters.""" + if hasattr(api_key, "get_secret_value"): + api_key = api_key.get_secret_value() + if not api_key or len(api_key) < 4: return "" return "...%s" % api_key[-4:] @@ -695,7 +699,7 @@ def traced_similarity_search(langchain, pin, func, instance, args, kwargs): instance._index.configuration.server_variables.get("project_name", ""), ) api_key = instance._index.configuration.api_key.get("ApiKeyAuth", "") - span.set_tag_str(API_KEY, "...%s" % api_key[-4:]) # override api_key for Pinecone + span.set_tag_str(API_KEY, _format_api_key(api_key)) # override api_key for Pinecone documents = func(*args, **kwargs) span.set_metric("langchain.response.document_count", len(documents)) for idx, document in enumerate(documents): diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 7a18a90f1cc..2f4209019fc 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -176,6 +176,7 @@ proxying psutil psycopg py +pydantic pyenv PyFrameObject pylibmc diff --git a/releasenotes/notes/langchain-api-key-secret-str-b51ef4f3be0b7315.yaml b/releasenotes/notes/langchain-api-key-secret-str-b51ef4f3be0b7315.yaml new file mode 100644 index 00000000000..e295a69f61e --- /dev/null +++ b/releasenotes/notes/langchain-api-key-secret-str-b51ef4f3be0b7315.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + langchain: This fix resolves an issue with tagging pydantic `SecretStr` type api keys. diff --git a/tests/contrib/langchain/test_langchain.py b/tests/contrib/langchain/test_langchain.py index bb2f08bf0e3..6c38f3c254e 100644 --- a/tests/contrib/langchain/test_langchain.py +++ b/tests/contrib/langchain/test_langchain.py @@ -53,6 +53,9 @@ def langchain(ddtrace_config_langchain, mock_logs, mock_metrics): with override_config("langchain", ddtrace_config_langchain): # ensure that mock OpenAI API key is passed in os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "<not-a-real-key>") + os.environ["COHERE_API_KEY"] = os.getenv("COHERE_API_KEY", "<not-a-real-key>") + os.environ["HUGGINGFACEHUB_API_TOKEN"] = os.getenv("HUGGINGFACEHUB_API_TOKEN", "<not-a-real-key>") + os.environ["AI21_API_KEY"] = os.getenv("AI21_API_KEY", "<not-a-real-key>") patch() import langchain @@ -1078,9 +1081,7 @@ def test_pinecone_vectorstore_similarity_search(langchain, request_vcr): api_key=os.getenv("PINECONE_API_KEY", "<not-a-real-key>"), environment=os.getenv("PINECONE_ENV", "<not-a-real-env>"), ) - embed = langchain.embeddings.OpenAIEmbeddings( - model="text-embedding-ada-002", openai_api_key=os.getenv("OPENAI_API_KEY", "<not-a-real-key>") - ) + embed = langchain.embeddings.OpenAIEmbeddings(model="text-embedding-ada-002") index = pinecone.Index(index_name="langchain-retrieval") vectorstore = langchain.vectorstores.Pinecone(index, embed.embed_query, "text") vectorstore.similarity_search("Who was Alan Turing?", 1) @@ -1100,9 +1101,7 @@ def test_pinecone_vectorstore_retrieval_chain(langchain, request_vcr): api_key=os.getenv("PINECONE_API_KEY", "<not-a-real-key>"), environment=os.getenv("PINECONE_ENV", "<not-a-real-env>"), ) - embed = langchain.embeddings.OpenAIEmbeddings( - model="text-embedding-ada-002", openai_api_key=os.getenv("OPENAI_API_KEY", "<not-a-real-key>") - ) + embed = langchain.embeddings.OpenAIEmbeddings(model="text-embedding-ada-002") index = pinecone.Index(index_name="langchain-retrieval") vectorstore = langchain.vectorstores.Pinecone(index, embed.embed_query, "text") @@ -1127,9 +1126,7 @@ def test_pinecone_vectorstore_retrieval_chain_39(langchain, request_vcr): api_key=os.getenv("PINECONE_API_KEY", "<not-a-real-key>"), environment=os.getenv("PINECONE_ENV", "<not-a-real-env>"), ) - embed = langchain.embeddings.OpenAIEmbeddings( - model="text-embedding-ada-002", openai_api_key=os.getenv("OPENAI_API_KEY", "<not-a-real-key>") - ) + embed = langchain.embeddings.OpenAIEmbeddings(model="text-embedding-ada-002") index = pinecone.Index(index_name="langchain-retrieval") vectorstore = langchain.vectorstores.Pinecone(index, embed.embed_query, "text") @@ -1152,9 +1149,7 @@ def test_vectorstore_similarity_search_metrics(langchain, request_vcr, mock_metr api_key=os.getenv("PINECONE_API_KEY", "<not-a-real-key>"), environment=os.getenv("PINECONE_ENV", "<not-a-real-env>"), ) - embed = langchain.embeddings.OpenAIEmbeddings( - model="text-embedding-ada-002", openai_api_key=os.getenv("OPENAI_API_KEY", "<not-a-real-key>") - ) + embed = langchain.embeddings.OpenAIEmbeddings(model="text-embedding-ada-002") index = pinecone.Index(index_name="langchain-retrieval") vectorstore = langchain.vectorstores.Pinecone(index, embed.embed_query, "text") vectorstore.similarity_search("Who was Alan Turing?", 1) @@ -1205,9 +1200,7 @@ def test_vectorstore_logs(langchain, ddtrace_config_langchain, request_vcr, mock api_key=os.getenv("PINECONE_API_KEY", "<not-a-real-key>"), environment=os.getenv("PINECONE_ENV", "<not-a-real-env>"), ) - embed = langchain.embeddings.OpenAIEmbeddings( - model="text-embedding-ada-002", openai_api_key=os.getenv("OPENAI_API_KEY", "<not-a-real-key>") - ) + embed = langchain.embeddings.OpenAIEmbeddings(model="text-embedding-ada-002") index = pinecone.Index(index_name="langchain-retrieval") vectorstore = langchain.vectorstores.Pinecone(index, embed.embed_query, "text") vectorstore.similarity_search("Who was Alan Turing?", 1)