Skip to content

Commit 4e17ec3

Browse files
authored
chore(langchain): ignore tagging complex prompt templates (#6371)
Builds on top of work done by @NarekA in #6369. This PR does not need a changelog as the LangChain integration has not been released yet. Current behavior of the traced LangChain chain is to grab the `prompt.template` attribute from the chain instance, but certain `PromptTemplate` classes do not have simple string `template` attributes which causes errors when we look these items up in code. For example the `ChatPromptTemplate` class stores a list of messages each with their own prompt template, making this non-trivial to parse and tag. As a result, we'll check if simple string templates are available on the PromptTemplate, and do nothing otherwise. In the future, we can consider tagging complex prompt values, but this is outside the scope of this PR (which aims to avoid raising errors by us looking up non-existent template attributes). ## 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 rexease 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 - [ ] Title is accurate. - [ ] No unnecessary changes are introduced. - [ ] Description motivates each change. - [ ] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [ ] Testing strategy adequately addresses listed risk(s). - [ ] Change is maintainable (easy to change, telemetry, documentation). - [ ] Release note makes sense to a user of the library. - [ ] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [ ] 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)
1 parent ba82227 commit 4e17ec3

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

ddtrace/contrib/langchain/patch.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from ddtrace.internal.logger import get_logger
2828
from ddtrace.internal.utils import get_argument_value
2929
from ddtrace.internal.utils.formats import asbool
30+
from ddtrace.internal.utils.formats import deep_getattr
3031
from ddtrace.pin import Pin
3132

3233

@@ -564,8 +565,9 @@ def traced_chain_call(langchain, pin, func, instance, args, kwargs):
564565
if integration.is_pc_sampled_span(span):
565566
for k, v in inputs.items():
566567
span.set_tag_str("langchain.request.inputs.%s" % k, integration.trunc(str(v)))
567-
if hasattr(instance, "prompt"):
568-
span.set_tag_str("langchain.request.prompt", integration.trunc(str(instance.prompt.template)))
568+
template = deep_getattr(instance, "prompt.template", default="")
569+
if template:
570+
span.set_tag_str("langchain.request.prompt", integration.trunc(str(template)))
569571
final_outputs = func(*args, **kwargs)
570572
if integration.is_pc_sampled_span(span):
571573
for k, v in final_outputs.items():
@@ -590,7 +592,7 @@ def traced_chain_call(langchain, pin, func, instance, args, kwargs):
590592
"sampled %s.%s" % (instance.__module__, instance.__class__.__name__),
591593
attrs={
592594
"inputs": log_inputs,
593-
"prompt": str(instance.prompt.template) if hasattr(instance, "prompt") else "",
595+
"prompt": str(deep_getattr(instance, "prompt.template", default="")),
594596
"outputs": log_outputs,
595597
},
596598
)
@@ -609,8 +611,9 @@ async def traced_chain_acall(langchain, pin, func, instance, args, kwargs):
609611
if integration.is_pc_sampled_span(span):
610612
for k, v in inputs.items():
611613
span.set_tag_str("langchain.request.inputs.%s" % k, integration.trunc(str(v)))
612-
if hasattr(instance, "prompt"):
613-
span.set_tag_str("langchain.request.prompt", integration.trunc(str(instance.prompt.template)))
614+
template = deep_getattr(instance, "prompt.template", default="")
615+
if template:
616+
span.set_tag_str("langchain.request.prompt", integration.trunc(str(template)))
614617
final_outputs = await func(*args, **kwargs)
615618
if integration.is_pc_sampled_span(span):
616619
for k, v in final_outputs.items():
@@ -635,7 +638,7 @@ async def traced_chain_acall(langchain, pin, func, instance, args, kwargs):
635638
"sampled %s.%s" % (instance.__module__, instance.__class__.__name__),
636639
attrs={
637640
"inputs": log_inputs,
638-
"prompt": str(instance.prompt.template) if hasattr(instance, "prompt") else "",
641+
"prompt": str(deep_getattr(instance, "prompt.template", default="")),
639642
"outputs": log_outputs,
640643
},
641644
)

tests/contrib/langchain/test_langchain.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,34 @@ def test_chain_logs(langchain, ddtrace_config_langchain, request_vcr, mock_logs,
10301030
mock_metrics.count.assert_not_called()
10311031

10321032

1033+
def test_chat_prompt_template_does_not_parse_template(langchain, mock_tracer):
1034+
"""
1035+
Test that tracing a chain with a ChatPromptTemplate does not try to directly parse the template,
1036+
as ChatPromptTemplates do not contain a specific template attribute (which will lead to an attribute error)
1037+
but instead contain multiple messages each with their own prompt template and are not trivial to tag.
1038+
"""
1039+
with mock.patch("langchain.chat_models.openai.ChatOpenAI._generate", side_effect=Exception("Mocked Error")):
1040+
with pytest.raises(Exception) as exc_info:
1041+
chat = langchain.chat_models.ChatOpenAI(temperature=0)
1042+
template = "You are a helpful assistant that translates english to pirate."
1043+
system_message_prompt = langchain.prompts.chat.SystemMessagePromptTemplate.from_template(template)
1044+
example_human = langchain.prompts.chat.HumanMessagePromptTemplate.from_template("Hi")
1045+
example_ai = langchain.prompts.chat.AIMessagePromptTemplate.from_template("Argh me mateys")
1046+
human_template = "{text}"
1047+
human_message_prompt = langchain.prompts.chat.HumanMessagePromptTemplate.from_template(human_template)
1048+
chat_prompt = langchain.prompts.chat.ChatPromptTemplate.from_messages(
1049+
[system_message_prompt, example_human, example_ai, human_message_prompt]
1050+
)
1051+
chain = langchain.chains.LLMChain(llm=chat, prompt=chat_prompt)
1052+
chain.run("I love programming.")
1053+
assert str(exc_info.value) == "Mocked Error"
1054+
traces = mock_tracer.pop_traces()
1055+
chain_span = traces[0][0]
1056+
assert chain_span.get_tag("langchain.request.inputs.text") == "I love programming."
1057+
assert chain_span.get_tag("langchain.request.type") == "chain"
1058+
assert chain_span.get_tag("langchain.request.prompt") is None
1059+
1060+
10331061
@pytest.mark.snapshot
10341062
def test_pinecone_vectorstore_similarity_search(langchain, request_vcr):
10351063
"""

0 commit comments

Comments
 (0)