Skip to content

Commit 1392007

Browse files
fix(langchain): avoid direct root imports from langchain [backport 1.20] (#7230)
Backport 8b941d2 from #7215 to 1.20. Fixes #7123. This PR fixes an issue brought up with `langchain>=0.0.300` where directly importing from langchain root is not supported anymore, which was previously not an issue in our patch wrapping code. The fix was to import the base modules containing the functions this integration wraps around before wrapping to get around the import error from trying to access directly from langchain root. ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [x] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) - [x] If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. - [x] This PR doesn't touch any of that. Co-authored-by: Yun Kim <[email protected]>
1 parent 112aa4c commit 1392007

File tree

4 files changed

+21
-8
lines changed

4 files changed

+21
-8
lines changed

ddtrace/contrib/langchain/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"EmbaasEmbeddings",
2828
)
2929

30-
vectorstores = (
30+
vectorstore_classes = (
3131
"AzureSearch",
3232
"Redis",
3333
"ElasticVectorSearch",

ddtrace/contrib/langchain/patch.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from ddtrace.contrib.langchain.constants import TOTAL_COST
2020
from ddtrace.contrib.langchain.constants import TYPE
2121
from ddtrace.contrib.langchain.constants import text_embedding_models
22-
from ddtrace.contrib.langchain.constants import vectorstores
22+
from ddtrace.contrib.langchain.constants import vectorstore_classes
2323
from ddtrace.contrib.trace_utils import unwrap
2424
from ddtrace.contrib.trace_utils import with_traced_module
2525
from ddtrace.contrib.trace_utils import wrap
@@ -756,6 +756,14 @@ def patch():
756756
)
757757
integration.start_log_writer()
758758

759+
# Langchain doesn't allow wrapping directly from root, so we have to import the base classes first before wrapping.
760+
# ref: https://github.com/DataDog/dd-trace-py/issues/7123
761+
from langchain import embeddings # noqa
762+
from langchain import vectorstores # noqa
763+
from langchain.chains.base import Chain # noqa
764+
from langchain.chat_models.base import BaseChatModel # noqa
765+
from langchain.llms.base import BaseLLM # noqa
766+
759767
wrap("langchain", "llms.base.BaseLLM.generate", traced_llm_generate(langchain))
760768
wrap("langchain", "llms.base.BaseLLM.agenerate", traced_llm_agenerate(langchain))
761769
wrap("langchain", "chat_models.base.BaseChatModel.generate", traced_chat_model_generate(langchain))
@@ -777,7 +785,7 @@ def patch():
777785
wrap("langchain", "embeddings.%s.embed_documents" % text_embedding_model, traced_embedding(langchain))
778786
# TODO: langchain >= 0.0.209 includes async embedding implementation (only for OpenAI)
779787
# We need to do the same with Vectorstores.
780-
for vectorstore in vectorstores:
788+
for vectorstore in vectorstore_classes:
781789
if hasattr(langchain.vectorstores, vectorstore):
782790
# Ensure not double patched, as some Embeddings interfaces are pointers to other Embeddings.
783791
if not isinstance(
@@ -809,7 +817,7 @@ def unpatch():
809817
deep_getattr(langchain.embeddings, "%s.embed_documents" % text_embedding_model), wrapt.ObjectProxy
810818
):
811819
unwrap(getattr(langchain.embeddings, text_embedding_model), "embed_documents")
812-
for vectorstore in vectorstores:
820+
for vectorstore in vectorstore_classes:
813821
if hasattr(langchain.vectorstores, vectorstore):
814822
if isinstance(
815823
deep_getattr(langchain.vectorstores, "%s.similarity_search" % vectorstore), wrapt.ObjectProxy
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
langchain: This fix resolves an import error with patching langchain versions newer than 0.0.300 due to
5+
langchain dropping support for wrapping or importing directly from root.

tests/contrib/langchain/test_langchain_patch.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from ddtrace.contrib.langchain import patch
33
from ddtrace.contrib.langchain import unpatch
44
from ddtrace.contrib.langchain.constants import text_embedding_models
5-
from ddtrace.contrib.langchain.constants import vectorstores
5+
from ddtrace.contrib.langchain.constants import vectorstore_classes
66
from tests.contrib.patch import PatchTestCase
77

88

@@ -24,7 +24,7 @@ def assert_module_patched(self, langchain):
2424
if embedding_model:
2525
self.assert_wrapped(embedding_model.embed_query)
2626
self.assert_wrapped(embedding_model.embed_documents)
27-
for vectorstore in vectorstores:
27+
for vectorstore in vectorstore_classes:
2828
vectorstore_interface = getattr(langchain.vectorstores, vectorstore, None)
2929
if vectorstore_interface:
3030
self.assert_wrapped(vectorstore_interface.similarity_search)
@@ -41,7 +41,7 @@ def assert_not_module_patched(self, langchain):
4141
if embedding_model:
4242
self.assert_not_wrapped(embedding_model.embed_query)
4343
self.assert_not_wrapped(embedding_model.embed_documents)
44-
for vectorstore in vectorstores:
44+
for vectorstore in vectorstore_classes:
4545
vectorstore_interface = getattr(langchain.vectorstores, vectorstore, None)
4646
if vectorstore_interface:
4747
self.assert_not_wrapped(vectorstore_interface.similarity_search)
@@ -58,7 +58,7 @@ def assert_not_module_double_patched(self, langchain):
5858
if embedding_model:
5959
self.assert_not_double_wrapped(embedding_model.embed_query)
6060
self.assert_not_double_wrapped(embedding_model.embed_documents)
61-
for vectorstore in vectorstores:
61+
for vectorstore in vectorstore_classes:
6262
vectorstore_interface = getattr(langchain.vectorstores, vectorstore, None)
6363
if vectorstore_interface:
6464
self.assert_not_double_wrapped(vectorstore_interface.similarity_search)

0 commit comments

Comments
 (0)