From 20cde1974635b37ce1bd32c7042d9ad3846dd4a9 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 10:57:04 -0400 Subject: [PATCH 01/23] streaming support --- ddtrace/contrib/internal/langchain/patch.py | 188 ++++++++++++++++++++ ddtrace/contrib/internal/langchain/utils.py | 80 +++++++++ 2 files changed, 268 insertions(+) create mode 100644 ddtrace/contrib/internal/langchain/utils.py diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index 86344d40311..07c33b8842f 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1,5 +1,6 @@ import os import sys +import json from typing import Any from typing import Dict from typing import Optional @@ -60,6 +61,7 @@ from ddtrace.internal.utils.version import parse_version from ddtrace.llmobs._integrations import LangChainIntegration from ddtrace.pin import Pin +from ddtrace.contrib.internal.langchain.utils import shared_stream log = get_logger(__name__) @@ -974,6 +976,166 @@ def traced_similarity_search(langchain, pin, func, instance, args, kwargs): return documents +# TODO refactor some of these on_span_started/on_span_finished functions +# that are used in other patched methods in this file into the utils module +@with_traced_module +def traced_chain_stream(langchain, pin, func, instance, args, kwargs): + integration: LangChainIntegration = langchain._datadog_integration + + def _on_span_started(span: Span): + inputs = get_argument_value(args, kwargs, 0, "input") + if integration.is_pc_sampled_span(span): + if not isinstance(inputs, list): + inputs = [inputs] + for idx, inp in enumerate(inputs): + if not isinstance(inp, dict): + span.set_tag_str("langchain.request.inputs.%d" % idx, integration.trunc(str(inp))) + else: + for k, v in inp.items(): + span.set_tag_str("langchain.request.inputs.%d.%s" % (idx, k), integration.trunc(str(v))) + + def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None): + if not error and integration.is_pc_sampled_span(span): + if langchain_core and isinstance(instance.steps[-1], langchain_core.output_parsers.JsonOutputParser): + # it's possible that the chain has a json output parser + # this will have already concatenated the chunks into a json object + + # it's also possible the json output parser isn't the last step, + # but one of the last steps, in which case we won't act on it here + # TODO (sam.brenner) make this more robust + content = json.dumps(streamed_chunks[-1]) + else: + # best effort to join chunks together + content = "".join([str(chunk) for chunk in streamed_chunks]) + span.set_tag_str("langchain.response.content", integration.trunc(content)) + + return shared_stream( + integration=integration, + pin=pin, + func=func, + instance=instance, + args=args, + kwargs=kwargs, + interface_type="chain", + on_span_started=_on_span_started, + on_span_finished=_on_span_finished, + ) + + +@with_traced_module +def traced_chat_stream(langchain, pin, func, instance, args, kwargs): + integration: LangChainIntegration = langchain._datadog_integration + llm_provider = instance._llm_type + + def _on_span_started(span: Span): + chat_messages = get_argument_value(args, kwargs, 0, "input") + if not isinstance(chat_messages, list): + chat_messages = [chat_messages] + for message_idx, message in enumerate(chat_messages): + if integration.is_pc_sampled_span(span): + if isinstance(message, dict): + span.set_tag_str( + "langchain.request.messages.%d.content" % (message_idx), + integration.trunc(str(message.get("content", ""))), + ) + span.set_tag_str( + "langchain.request.messages.%d.role" % (message_idx), + str(message.get("role", "")), + ) + elif isinstance(message, langchain_core.prompt_values.PromptValue): + for langchain_message_idx, langchain_message in enumerate(message.messages): + span.set_tag_str( + "langchain.request.messages.%d.%d.content" % (message_idx, langchain_message_idx), + integration.trunc(str(langchain_message.content)), + ) + span.set_tag_str( + "langchain.request.messages.%d.%d.role" % (message_idx, langchain_message_idx), + str(langchain_message.__class__.__name__), + ) + elif isinstance(message, langchain_core.messages.BaseMessage): + span.set_tag_str( + "langchain.request.messages.%d.content" % (message_idx), integration.trunc(str(message.content)) + ) + span.set_tag_str( + "langchain.request.messages.%d.role" % (message_idx), str(message.__class__.__name__) + ) + else: + span.set_tag_str( + "langchain.request.messages.%d.content" % (message_idx), integration.trunc(message) + ) + + for param, val in getattr(instance, "_identifying_params", {}).items(): + if isinstance(val, dict): + for k, v in val.items(): + span.set_tag_str("langchain.request.%s.parameters.%s.%s" % (llm_provider, param, k), str(v)) + else: + span.set_tag_str("langchain.request.%s.parameters.%s" % (llm_provider, param), str(val)) + + def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None): + if not error and integration.is_pc_sampled_span(span): + content = "".join([str(chunk.content) for chunk in streamed_chunks]) + span.set_tag_str("langchain.response.content", integration.trunc(content)) + + usage = getattr(streamed_chunks[-1], "usage_metadata", None) + if usage: + for k, v in usage.items(): + span.set_tag_str("langchain.response.usage_metadata.%s" % k, str(v)) + print("done with span") + + return shared_stream( + integration=integration, + pin=pin, + func=func, + instance=instance, + args=args, + kwargs=kwargs, + interface_type="chat_model", + on_span_started=_on_span_started, + on_span_finished=_on_span_finished, + api_key=_extract_api_key(instance), + provider=llm_provider, + ) + + +@with_traced_module +def traced_llm_stream(langchain, pin, func, instance, args, kwargs): + integration: LangChainIntegration = langchain._datadog_integration + llm_provider = instance._llm_type + + def _on_span_start(span: Span): + inp = get_argument_value(args, kwargs, 0, "input") + if not isinstance(inp, list): + inp = [inp] + if integration.is_pc_sampled_span(span): + for idx, prompt in enumerate(inp): + span.set_tag_str("langchain.request.prompts.%d" % idx, integration.trunc(str(prompt))) + for param, val in getattr(instance, "_identifying_params", {}).items(): + if isinstance(val, dict): + for k, v in val.items(): + span.set_tag_str("langchain.request.%s.parameters.%s.%s" % (llm_provider, param, k), str(v)) + else: + span.set_tag_str("langchain.request.%s.parameters.%s" % (llm_provider, param), str(val)) + + def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None): + if not error and integration.is_pc_sampled_span(span): + content = "".join([str(chunk) for chunk in streamed_chunks]) + span.set_tag_str("langchain.response.content", integration.trunc(content)) + + return shared_stream( + integration=integration, + pin=pin, + func=func, + instance=instance, + args=args, + kwargs=kwargs, + interface_type="llm", + on_span_started=_on_span_start, + on_span_finished=_on_span_finished, + api_key=_extract_api_key(instance), + provider=llm_provider, + ) + + def _patch_embeddings_and_vectorstores(): """ Text embedding models override two abstract base methods instead of super calls, @@ -1080,6 +1242,9 @@ def patch(): wrap("langchain", "embeddings.OpenAIEmbeddings.embed_documents", traced_embedding(langchain)) else: from langchain.chains.base import Chain # noqa:F401 + from langchain_core import prompt_values # noqa: F401 + from langchain_core import output_parsers # noqa: F401 + from langchain_core import messages # noqa: F401 wrap("langchain_core", "language_models.llms.BaseLLM.generate", traced_llm_generate(langchain)) wrap("langchain_core", "language_models.llms.BaseLLM.agenerate", traced_llm_agenerate(langchain)) @@ -1101,6 +1266,23 @@ def patch(): ) wrap("langchain_core", "runnables.base.RunnableSequence.batch", traced_lcel_runnable_sequence(langchain)) wrap("langchain_core", "runnables.base.RunnableSequence.abatch", traced_lcel_runnable_sequence_async(langchain)) + + # streaming + wrap("langchain_core", "runnables.base.RunnableSequence.stream", traced_chain_stream(langchain)) + wrap("langchain_core", "runnables.base.RunnableSequence.astream", traced_chain_stream(langchain)) + wrap( + "langchain_core", + "language_models.chat_models.BaseChatModel.stream", + traced_chat_stream(langchain), + ) + wrap( + "langchain_core", + "language_models.chat_models.BaseChatModel.astream", + traced_chat_stream(langchain), + ) + wrap("langchain_core", "language_models.llms.BaseLLM.stream", traced_llm_stream(langchain)) + wrap("langchain_core", "language_models.llms.BaseLLM.astream", traced_llm_stream(langchain)) + if langchain_openai: wrap("langchain_openai", "OpenAIEmbeddings.embed_documents", traced_embedding(langchain)) if langchain_pinecone: @@ -1149,6 +1331,12 @@ def unpatch(): unwrap(langchain_core.runnables.base.RunnableSequence, "ainvoke") unwrap(langchain_core.runnables.base.RunnableSequence, "batch") unwrap(langchain_core.runnables.base.RunnableSequence, "abatch") + unwrap(langchain_core.runnables.base.RunnableSequence, "stream") + unwrap(langchain_core.runnables.base.RunnableSequence, "astream") + unwrap(langchain_core.language_models.chat_models.BaseChatModel, "stream") + unwrap(langchain_core.language_models.chat_models.BaseChatModel, "astream") + unwrap(langchain_core.language_models.llms.BaseLLM, "stream") + unwrap(langchain_core.language_models.llms.BaseLLM, "astream") if langchain_openai: unwrap(langchain_openai.OpenAIEmbeddings, "embed_documents") if langchain_pinecone: diff --git a/ddtrace/contrib/internal/langchain/utils.py b/ddtrace/contrib/internal/langchain/utils.py new file mode 100644 index 00000000000..0159e705ff7 --- /dev/null +++ b/ddtrace/contrib/internal/langchain/utils.py @@ -0,0 +1,80 @@ +import sys + +import inspect + + +class BaseTracedLangChainStreamResponse: + def __init__(self, generator, integration, span, on_span_finish): + self._generator = generator + self._dd_integration = integration + self._dd_span = span + self._on_span_finish = on_span_finish + self._chunks = [] + + +class TracedLangchainStreamResponse(BaseTracedLangChainStreamResponse): + def __iter__(self): + try: + for chunk in self._generator.__iter__(): + self._chunks.append(chunk) + yield chunk + except Exception: + self._dd_span.set_exc_info(*sys.exc_info()) + self._dd_integration.metric(self._dd_span, "incr", "request.error", 1) + raise + finally: + self._on_span_finish(self._dd_span, self._chunks, error=bool(self._dd_span.error)) + self._dd_span.finish() + + +class TracedLangchainAsyncStreamResponse(BaseTracedLangChainStreamResponse): + async def __aiter__(self): + try: + async for chunk in self._generator.__aiter__(): + self._chunks.append(chunk) + yield chunk + except Exception: + self._dd_span.set_exc_info(*sys.exc_info()) + self._dd_integration.metric(self._dd_span, "incr", "request.error", 1) + raise + finally: + self._on_span_finish(self._dd_span, self._chunks, error=bool(self._dd_span.error)) + self._dd_span.finish() + + +def shared_stream( + integration, + pin, + func, + instance, + args, + kwargs, + interface_type, + on_span_started, + on_span_finished, + **extra_options, +): + options = { + "pin": pin, + "operation_id": f"{instance.__module__}.{instance.__class__.__name__}", + "interface_type": interface_type, + } + + options.update(extra_options) + + span = integration.trace(**options) + span.set_tag("langchain.request.stream", True) + on_span_started(span) + + try: + resp = func(*args, **kwargs) + cls = TracedLangchainAsyncStreamResponse if inspect.isasyncgen(resp) else TracedLangchainStreamResponse + + return cls(resp, integration, span, on_span_finished) + except Exception: + # error with the method call itself + span.set_exc_info(*sys.exc_info()) + span.finish() + integration.metric(span, "incr", "request.error", 1) + integration.metric(span, "dist", "request.duration", span.duration_ns) + raise From 57ddab0be6507132943107c9061291c3913927b6 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 10:57:52 -0400 Subject: [PATCH 02/23] tests --- .../lcel_openai_chat_streamed_response.txt | 106 +++++++++++++ ...t_streamed_response_json_output_parser.txt | 150 ++++++++++++++++++ .../lcel_openai_llm_streamed_response.txt | 78 +++++++++ tests/contrib/langchain/conftest.py | 76 +++++++++ .../langchain/test_langchain_community.py | 132 +++++++++++++++ ...gchain_community.test_astreamed_chain.json | 61 +++++++ ...ngchain_community.test_astreamed_chat.json | 37 +++++ ...angchain_community.test_astreamed_llm.json | 39 +++++ ...ngchain_community.test_streamed_chain.json | 61 +++++++ ...angchain_community.test_streamed_chat.json | 37 +++++ ...nity.test_streamed_json_output_parser.json | 63 ++++++++ ...langchain_community.test_streamed_llm.json | 39 +++++ 12 files changed, 879 insertions(+) create mode 100644 tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt create mode 100644 tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt create mode 100644 tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt create mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json create mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json create mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json create mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json create mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json create mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json create mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt new file mode 100644 index 00000000000..0eb8841047e --- /dev/null +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt @@ -0,0 +1,106 @@ +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"#"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Python"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Basics"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"##"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Introduction"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Python"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" high"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-level"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" general"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-purpose"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" programming"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" language"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" widely"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" used"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" web"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" development"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" analysis"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" artificial"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" intelligence"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" scientific"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" computing"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" many"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" other"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" applications"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" It"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" was"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" created"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" by"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Guid"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"o"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" van"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Ros"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"sum"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" first"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"length"}]} + +data: [DONE] + diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt new file mode 100644 index 00000000000..838c1ae3c8e --- /dev/null +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt @@ -0,0 +1,150 @@ +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"Here's"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" country"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" France"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" its"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" population"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" JSON"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" format"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":":\n\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"```"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"json"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"{\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"countries"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" [\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"name"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"France"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"population"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"670"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"810"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"00"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" }\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" ]\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"}\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"``"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"`\n\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"Note"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" population"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" figure"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" an"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" approximation"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" may"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" vary"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" Always"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" recent"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" accurate"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" if"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" needed"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: [DONE] + diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt new file mode 100644 index 00000000000..b1b42a7f12c --- /dev/null +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt @@ -0,0 +1,78 @@ +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"\n\n","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"1","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":".","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Identify","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" your","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" audience","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":":","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Before you","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" start","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" writing","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":",","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" it is important to","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" understand who","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" your","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" audience is","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":".","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Technical","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" documentation","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" can","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" be","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" written","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" for different types","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" of","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" people","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":",","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" such as developers,","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" end","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"-users","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":",","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" or","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" system","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" administrators","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":".","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Knowing","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" your","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" audience","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" will help you","index":0,"logprobs":null,"finish_reason":"length"}],"model":"gpt-3.5-turbo-instruct"} + +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"","index":0,"logprobs":null,"finish_reason":"length"}],"model":"gpt-3.5-turbo-instruct"} + +data: [DONE] + diff --git a/tests/contrib/langchain/conftest.py b/tests/contrib/langchain/conftest.py index b9debeca991..4402a6ce4ae 100644 --- a/tests/contrib/langchain/conftest.py +++ b/tests/contrib/langchain/conftest.py @@ -162,3 +162,79 @@ def langchain_pinecone(ddtrace_config_langchain, mock_logs, mock_metrics, langch yield langchain_pinecone except ImportError: yield + + +@pytest.fixture +def streamed_response_responder(): + # TODO (sam.brenner): clean this up a bit, make it more generic + try: + import httpx + import os + import importlib + + class CustomTransport(httpx.BaseTransport): + def __init__(self, file: str): + super().__init__() + self.file = file + + def handle_request(self, request: httpx.Request) -> httpx.Response: + with open( + os.path.join(os.path.dirname(__file__), f"cassettes/langchain_community/{self.file}"), + "r", + encoding="utf-8", + ) as f: + content = f.read() + return httpx.Response(200, request=request, content=content) + + def responder(module, client_class_key, http_client_key, property: list[str], file: str): + clientModule = importlib.import_module(module) # openai, anthropic, etc. + client_class = getattr(clientModule, client_class_key) + client = client_class(**{http_client_key: httpx.Client(transport=CustomTransport(file=file))}) + + for prop in property: + client = getattr(client, prop) + + return client + + yield responder + + except ImportError: + yield + + +@pytest.fixture +def async_streamed_response_responder(): + # TODO (sam.brenner): clean this up a bit, make it more generic + try: + import httpx + import os + import importlib + + class CustomTransport(httpx.AsyncBaseTransport): + def __init__(self, file: str): + super().__init__() + self.file = file + + async def handle_async_request(self, request: httpx.Request) -> httpx.Response: + with open( + os.path.join(os.path.dirname(__file__), f"cassettes/langchain_community/{self.file}"), + "r", + encoding="utf-8", + ) as f: + content = f.read() + return httpx.Response(200, request=request, content=content) + + def responder(module, client_class_key, http_client_key, property: list[str], file: str): + clientModule = importlib.import_module(module) # openai, anthropic, etc. + client_class = getattr(clientModule, client_class_key) + client = client_class(**{http_client_key: httpx.AsyncClient(transport=CustomTransport(file=file))}) + + for prop in property: + client = getattr(client, prop) + + return client + + yield responder + + except ImportError: + yield diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index f4d23181956..e3abb918a26 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -23,6 +23,8 @@ "meta.openai.request.logprobs", # langchain-openai llm call now includes logprobs as param "meta.error.stack", "meta.http.useragent", + "meta.langchain.request.openai-chat.parameters.logprobs", + "meta.langchain.request.openai.parameters.logprobs", "meta.langchain.request.openai.parameters.seed", # langchain-openai llm call now includes seed as param "meta.langchain.request.openai.parameters.logprobs", # langchain-openai llm call now includes seed as param "metrics.langchain.tokens.total_cost", # total_cost depends on if tiktoken is installed @@ -1274,3 +1276,133 @@ def test_faiss_vectorstore_retrieval(langchain_community, langchain_openai, requ retriever = faiss.as_retriever() with request_vcr.use_cassette("openai_retrieval_embedding.yaml"): retriever.invoke("What was the message of the last test query?") + + +@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +def test_streamed_chain(langchain_core, langchain_openai, streamed_response_responder): + client = streamed_response_responder( + module="openai", + client_class_key="OpenAI", + http_client_key="http_client", + property=["chat", "completions"], + file="lcel_openai_chat_streamed_response.txt", + ) + + prompt = langchain_core.prompts.ChatPromptTemplate.from_messages( + [("system", "You are world class technical documentation writer."), ("user", "{input}")] + ) + llm = langchain_openai.ChatOpenAI(client=client) + parser = langchain_core.output_parsers.StrOutputParser() + + chain = prompt | llm | parser + for _ in chain.stream({"input": "how can langsmith help with testing?"}): + pass + + +@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +def test_streamed_chat(langchain_openai, request_vcr, streamed_response_responder): + client = streamed_response_responder( + module="openai", + client_class_key="OpenAI", + http_client_key="http_client", + property=["chat", "completions"], + file="lcel_openai_chat_streamed_response.txt", + ) + model = langchain_openai.ChatOpenAI(client=client) + + for _ in model.stream(input="how can langsmith help with testing?"): + pass + + +@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +def test_streamed_llm(langchain_community, langchain_core, langchain_openai, streamed_response_responder): + client = streamed_response_responder( + module="openai", + client_class_key="OpenAI", + http_client_key="http_client", + property=["completions"], + file="lcel_openai_llm_streamed_response.txt", + ) + + llm = langchain_openai.OpenAI(client=client) + + for _ in llm.stream(input="How do I write technical documentation?"): + pass + + +@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +async def test_astreamed_chain(langchain_core, langchain_openai, async_streamed_response_responder): + client = async_streamed_response_responder( + module="openai", + client_class_key="AsyncOpenAI", + http_client_key="http_client", + property=["chat", "completions"], + file="lcel_openai_chat_streamed_response.txt", + ) + + prompt = langchain_core.prompts.ChatPromptTemplate.from_messages( + [("system", "You are world class technical documentation writer."), ("user", "{input}")] + ) + llm = langchain_openai.ChatOpenAI(async_client=client) + parser = langchain_core.output_parsers.StrOutputParser() + + chain = prompt | llm | parser + async for _ in chain.astream({"input": "how can langsmith help with testing?"}): + pass + + +@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +async def test_astreamed_chat(langchain_core, langchain_openai, async_streamed_response_responder): + client = async_streamed_response_responder( + module="openai", + client_class_key="AsyncOpenAI", + http_client_key="http_client", + property=["chat", "completions"], + file="lcel_openai_chat_streamed_response.txt", + ) + + model = langchain_openai.ChatOpenAI(async_client=client) + + async for _ in model.astream(input="how can langsmith help with testing?"): + pass + + +@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +async def test_astreamed_llm(langchain_community, langchain_core, langchain_openai, async_streamed_response_responder): + client = async_streamed_response_responder( + module="openai", + client_class_key="AsyncOpenAI", + http_client_key="http_client", + property=["completions"], + file="lcel_openai_llm_streamed_response.txt", + ) + + llm = langchain_openai.OpenAI(async_client=client) + + async for _ in llm.astream(input="How do I write technical documentation?"): + pass + + +@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai, streamed_response_responder): + client = streamed_response_responder( + module="openai", + client_class_key="OpenAI", + http_client_key="http_client", + property=["chat", "completions"], + file="lcel_openai_chat_streamed_response_json_output_parser.txt", + ) + + model = langchain_openai.ChatOpenAI(model="gpt-4o", max_tokens=50, client=client) + parser = langchain_core.output_parsers.JsonOutputParser() + + chain = model | parser + inp = 'output a list of the country france their population in JSON format. Use a dict with an outer key of "countries" which contains a list of countries. Each country should have the key `name` and `population`' + + messages = [ + langchain.schema.SystemMessage(content="You know everything about the world."), + langchain.schema.HumanMessage(content=inp), + ] + + for _ in chain.stream(input=messages): + pass diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json new file mode 100644 index 00000000000..f7dd8621412 --- /dev/null +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json @@ -0,0 +1,61 @@ +[[ + { + "name": "langchain.request", + "service": "", + "resource": "langchain_core.runnables.base.RunnableSequence", + "trace_id": 0, + "span_id": 1, + "parent_id": 0, + "type": "", + "error": 0, + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66e8391100000000", + "langchain.request.inputs.0.input": "how can langsmith help with testing?", + "langchain.request.stream": "True", + "langchain.request.type": "chain", + "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "language": "python", + "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" + }, + "metrics": { + "_dd.measured": 1, + "_dd.top_level": 1, + "_dd.tracer_kr": 1.0, + "_sampling_priority_v1": 1, + "process_id": 47416 + }, + "duration": 52924000, + "start": 1726494993204980000 + }, + { + "name": "langchain.request", + "service": "", + "resource": "langchain_openai.chat_models.base.ChatOpenAI", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "", + "error": 0, + "meta": { + "langchain.request.api_key": "...key>", + "langchain.request.messages.0.0.content": "You are world class technical documentation writer.", + "langchain.request.messages.0.0.role": "SystemMessage", + "langchain.request.messages.0.1.content": "how can langsmith help with testing?", + "langchain.request.messages.0.1.role": "HumanMessage", + "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.n": "1", + "langchain.request.openai-chat.parameters.stream": "False", + "langchain.request.openai-chat.parameters.temperature": "0.7", + "langchain.request.provider": "openai-chat", + "langchain.request.stream": "True", + "langchain.request.type": "chat_model", + "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d..." + }, + "metrics": { + "_dd.measured": 1 + }, + "duration": 44584000, + "start": 1726494993212392000 + }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json new file mode 100644 index 00000000000..e25971c84c8 --- /dev/null +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json @@ -0,0 +1,37 @@ +[[ + { + "name": "langchain.request", + "service": "", + "resource": "langchain_openai.chat_models.base.ChatOpenAI", + "trace_id": 0, + "span_id": 1, + "parent_id": 0, + "type": "", + "error": 0, + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66e8391100000000", + "langchain.request.api_key": "...key>", + "langchain.request.messages.0.content": "how can langsmith help with testing?", + "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.n": "1", + "langchain.request.openai-chat.parameters.stream": "False", + "langchain.request.openai-chat.parameters.temperature": "0.7", + "langchain.request.provider": "openai-chat", + "langchain.request.stream": "True", + "langchain.request.type": "chat_model", + "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "language": "python", + "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" + }, + "metrics": { + "_dd.measured": 1, + "_dd.top_level": 1, + "_dd.tracer_kr": 1.0, + "_sampling_priority_v1": 1, + "process_id": 47416 + }, + "duration": 15030000, + "start": 1726494993326675000 + }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json new file mode 100644 index 00000000000..ae3268a869d --- /dev/null +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json @@ -0,0 +1,39 @@ +[[ + { + "name": "langchain.request", + "service": "", + "resource": "langchain_openai.llms.base.OpenAI", + "trace_id": 0, + "span_id": 1, + "parent_id": 0, + "type": "", + "error": 0, + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66e8391100000000", + "langchain.request.api_key": "...key>", + "langchain.request.openai.parameters.frequency_penalty": "0", + "langchain.request.openai.parameters.max_tokens": "256", + "langchain.request.openai.parameters.model_name": "gpt-3.5-turbo-instruct", + "langchain.request.openai.parameters.n": "1", + "langchain.request.openai.parameters.presence_penalty": "0", + "langchain.request.openai.parameters.temperature": "0.7", + "langchain.request.openai.parameters.top_p": "1", + "langchain.request.prompts.0": "How do I write technical documentation?", + "langchain.request.provider": "openai", + "langchain.request.stream": "True", + "langchain.request.type": "llm", + "langchain.response.content": "\\n\\n1. Identify your audience: Before you start writing, it is important to understand who your audience is. Technical documenta...", + "language": "python", + "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" + }, + "metrics": { + "_dd.measured": 1, + "_dd.top_level": 1, + "_dd.tracer_kr": 1.0, + "_sampling_priority_v1": 1, + "process_id": 47416 + }, + "duration": 12486000, + "start": 1726494993287544000 + }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json new file mode 100644 index 00000000000..09530b8db9f --- /dev/null +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json @@ -0,0 +1,61 @@ +[[ + { + "name": "langchain.request", + "service": "", + "resource": "langchain_core.runnables.base.RunnableSequence", + "trace_id": 0, + "span_id": 1, + "parent_id": 0, + "type": "", + "error": 0, + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66e8388f00000000", + "langchain.request.inputs.0.input": "how can langsmith help with testing?", + "langchain.request.stream": "True", + "langchain.request.type": "chain", + "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "language": "python", + "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" + }, + "metrics": { + "_dd.measured": 1, + "_dd.top_level": 1, + "_dd.tracer_kr": 1.0, + "_sampling_priority_v1": 1, + "process_id": 44915 + }, + "duration": 49336000, + "start": 1726494863602561000 + }, + { + "name": "langchain.request", + "service": "", + "resource": "langchain_openai.chat_models.base.ChatOpenAI", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "", + "error": 0, + "meta": { + "langchain.request.api_key": "...key>", + "langchain.request.messages.0.0.content": "You are world class technical documentation writer.", + "langchain.request.messages.0.0.role": "SystemMessage", + "langchain.request.messages.0.1.content": "how can langsmith help with testing?", + "langchain.request.messages.0.1.role": "HumanMessage", + "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.n": "1", + "langchain.request.openai-chat.parameters.stream": "False", + "langchain.request.openai-chat.parameters.temperature": "0.7", + "langchain.request.provider": "openai-chat", + "langchain.request.stream": "True", + "langchain.request.type": "chat_model", + "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d..." + }, + "metrics": { + "_dd.measured": 1 + }, + "duration": 42281000, + "start": 1726494863608857000 + }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json new file mode 100644 index 00000000000..0e0ef59fa28 --- /dev/null +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json @@ -0,0 +1,37 @@ +[[ + { + "name": "langchain.request", + "service": "", + "resource": "langchain_openai.chat_models.base.ChatOpenAI", + "trace_id": 0, + "span_id": 1, + "parent_id": 0, + "type": "", + "error": 0, + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66e8388f00000000", + "langchain.request.api_key": "...key>", + "langchain.request.messages.0.content": "how can langsmith help with testing?", + "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", + "langchain.request.openai-chat.parameters.n": "1", + "langchain.request.openai-chat.parameters.stream": "False", + "langchain.request.openai-chat.parameters.temperature": "0.7", + "langchain.request.provider": "openai-chat", + "langchain.request.stream": "True", + "langchain.request.type": "chat_model", + "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "language": "python", + "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" + }, + "metrics": { + "_dd.measured": 1, + "_dd.top_level": 1, + "_dd.tracer_kr": 1.0, + "_sampling_priority_v1": 1, + "process_id": 44915 + }, + "duration": 9681000, + "start": 1726494863731905000 + }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json new file mode 100644 index 00000000000..de01f0184f4 --- /dev/null +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json @@ -0,0 +1,63 @@ +[[ + { + "name": "langchain.request", + "service": "", + "resource": "langchain_core.runnables.base.RunnableSequence", + "trace_id": 0, + "span_id": 1, + "parent_id": 0, + "type": "", + "error": 0, + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66e8451000000000", + "langchain.request.inputs.0": "content='You know everything about the world.'", + "langchain.request.inputs.1": "content='output a list of the country france their population in JSON format. Use a dict with an outer key of \"countries\" which ...", + "langchain.request.stream": "True", + "langchain.request.type": "chain", + "langchain.response.content": "{\"countries\": [{\"name\": \"France\", \"population\": 67081000}]}", + "language": "python", + "runtime-id": "525b9ac09789411c96a2663986ca6fa7" + }, + "metrics": { + "_dd.measured": 1, + "_dd.top_level": 1, + "_dd.tracer_kr": 1.0, + "_sampling_priority_v1": 1, + "process_id": 8694 + }, + "duration": 103255000, + "start": 1726498064306613000 + }, + { + "name": "langchain.request", + "service": "", + "resource": "langchain_openai.chat_models.base.ChatOpenAI", + "trace_id": 0, + "span_id": 2, + "parent_id": 1, + "type": "", + "error": 0, + "meta": { + "langchain.request.api_key": "...key>", + "langchain.request.messages.0.content": "You know everything about the world.", + "langchain.request.messages.0.role": "SystemMessage", + "langchain.request.messages.1.content": "output a list of the country france their population in JSON format. Use a dict with an outer key of \"countries\" which contains ...", + "langchain.request.messages.1.role": "HumanMessage", + "langchain.request.openai-chat.parameters.max_tokens": "50", + "langchain.request.openai-chat.parameters.model": "gpt-4o", + "langchain.request.openai-chat.parameters.model_name": "gpt-4o", + "langchain.request.openai-chat.parameters.n": "1", + "langchain.request.openai-chat.parameters.stream": "False", + "langchain.request.openai-chat.parameters.temperature": "0.7", + "langchain.request.provider": "openai-chat", + "langchain.request.stream": "True", + "langchain.request.type": "chat_model", + "langchain.response.content": "Here's the information for the country France and its population in JSON format:\\n\\n```json\\n{\\n \"countries\": [\\n {\\n \"..." + }, + "metrics": { + "_dd.measured": 1 + }, + "duration": 98388000, + "start": 1726498064310536000 + }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json new file mode 100644 index 00000000000..0be42791387 --- /dev/null +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json @@ -0,0 +1,39 @@ +[[ + { + "name": "langchain.request", + "service": "", + "resource": "langchain_openai.llms.base.OpenAI", + "trace_id": 0, + "span_id": 1, + "parent_id": 0, + "type": "", + "error": 0, + "meta": { + "_dd.p.dm": "-0", + "_dd.p.tid": "66e8388f00000000", + "langchain.request.api_key": "...key>", + "langchain.request.openai.parameters.frequency_penalty": "0", + "langchain.request.openai.parameters.max_tokens": "256", + "langchain.request.openai.parameters.model_name": "gpt-3.5-turbo-instruct", + "langchain.request.openai.parameters.n": "1", + "langchain.request.openai.parameters.presence_penalty": "0", + "langchain.request.openai.parameters.temperature": "0.7", + "langchain.request.openai.parameters.top_p": "1", + "langchain.request.prompts.0": "How do I write technical documentation?", + "langchain.request.provider": "openai", + "langchain.request.stream": "True", + "langchain.request.type": "llm", + "langchain.response.content": "\\n\\n1. Identify your audience: Before you start writing, it is important to understand who your audience is. Technical documenta...", + "language": "python", + "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" + }, + "metrics": { + "_dd.measured": 1, + "_dd.top_level": 1, + "_dd.tracer_kr": 1.0, + "_sampling_priority_v1": 1, + "process_id": 44915 + }, + "duration": 9120000, + "start": 1726494863700015000 + }]] From bd80fa662df52e2784f1390c4e98ef66a2f67725 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 10:59:17 -0400 Subject: [PATCH 03/23] release note --- .../notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml diff --git a/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml new file mode 100644 index 00000000000..e0269e4aff5 --- /dev/null +++ b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + langchain: Adds support for tracing `stream` calls on LCEL chains, chat completion models, or completion models. From a25064404e076963b1ffad2d89f3b4ef5b639c5d Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 11:06:40 -0400 Subject: [PATCH 04/23] lint fixes --- ddtrace/contrib/internal/langchain/patch.py | 10 +++++----- ddtrace/contrib/internal/langchain/utils.py | 3 +-- tests/contrib/langchain/conftest.py | 18 ++++++++++-------- .../langchain/test_langchain_community.py | 8 +++++++- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index 2a6e8c41103..9ca2d4ac7ec 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1,7 +1,6 @@ import json import os import sys -import json from typing import Any from typing import Dict from typing import Optional @@ -51,6 +50,7 @@ from ddtrace.contrib.internal.langchain.constants import agent_output_parser_classes from ddtrace.contrib.internal.langchain.constants import text_embedding_models from ddtrace.contrib.internal.langchain.constants import vectorstore_classes +from ddtrace.contrib.internal.langchain.utils import shared_stream from ddtrace.contrib.trace_utils import unwrap from ddtrace.contrib.trace_utils import with_traced_module from ddtrace.contrib.trace_utils import wrap @@ -62,7 +62,6 @@ from ddtrace.internal.utils.version import parse_version from ddtrace.llmobs._integrations import LangChainIntegration from ddtrace.pin import Pin -from ddtrace.contrib.internal.langchain.utils import shared_stream log = get_logger(__name__) @@ -1081,7 +1080,6 @@ def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None) if usage: for k, v in usage.items(): span.set_tag_str("langchain.response.usage_metadata.%s" % k, str(v)) - print("done with span") return shared_stream( integration=integration, @@ -1135,6 +1133,8 @@ def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None) api_key=_extract_api_key(instance), provider=llm_provider, ) + + @with_traced_module def traced_base_tool_invoke(langchain, pin, func, instance, args, kwargs): integration = langchain._datadog_integration @@ -1363,9 +1363,9 @@ def patch(): wrap("langchain", "embeddings.OpenAIEmbeddings.embed_documents", traced_embedding(langchain)) else: from langchain.chains.base import Chain # noqa:F401 - from langchain_core import prompt_values # noqa: F401 - from langchain_core import output_parsers # noqa: F401 from langchain_core import messages # noqa: F401 + from langchain_core import output_parsers # noqa: F401 + from langchain_core import prompt_values # noqa: F401 from langchain_core.tools import BaseTool # noqa:F401 wrap("langchain_core", "language_models.llms.BaseLLM.generate", traced_llm_generate(langchain)) diff --git a/ddtrace/contrib/internal/langchain/utils.py b/ddtrace/contrib/internal/langchain/utils.py index 0159e705ff7..330c890875b 100644 --- a/ddtrace/contrib/internal/langchain/utils.py +++ b/ddtrace/contrib/internal/langchain/utils.py @@ -1,6 +1,5 @@ -import sys - import inspect +import sys class BaseTracedLangChainStreamResponse: diff --git a/tests/contrib/langchain/conftest.py b/tests/contrib/langchain/conftest.py index 4402a6ce4ae..d8d41d923df 100644 --- a/tests/contrib/langchain/conftest.py +++ b/tests/contrib/langchain/conftest.py @@ -168,9 +168,10 @@ def langchain_pinecone(ddtrace_config_langchain, mock_logs, mock_metrics, langch def streamed_response_responder(): # TODO (sam.brenner): clean this up a bit, make it more generic try: - import httpx - import os import importlib + import os + + import httpx class CustomTransport(httpx.BaseTransport): def __init__(self, file: str): @@ -186,12 +187,12 @@ def handle_request(self, request: httpx.Request) -> httpx.Response: content = f.read() return httpx.Response(200, request=request, content=content) - def responder(module, client_class_key, http_client_key, property: list[str], file: str): + def responder(module, client_class_key, http_client_key, client_path: list[str], file: str): clientModule = importlib.import_module(module) # openai, anthropic, etc. client_class = getattr(clientModule, client_class_key) client = client_class(**{http_client_key: httpx.Client(transport=CustomTransport(file=file))}) - for prop in property: + for prop in client_path: client = getattr(client, prop) return client @@ -206,9 +207,10 @@ def responder(module, client_class_key, http_client_key, property: list[str], fi def async_streamed_response_responder(): # TODO (sam.brenner): clean this up a bit, make it more generic try: - import httpx - import os import importlib + import os + + import httpx class CustomTransport(httpx.AsyncBaseTransport): def __init__(self, file: str): @@ -224,12 +226,12 @@ async def handle_async_request(self, request: httpx.Request) -> httpx.Response: content = f.read() return httpx.Response(200, request=request, content=content) - def responder(module, client_class_key, http_client_key, property: list[str], file: str): + def responder(module, client_class_key, http_client_key, client_path: list[str], file: str): clientModule = importlib.import_module(module) # openai, anthropic, etc. client_class = getattr(clientModule, client_class_key) client = client_class(**{http_client_key: httpx.AsyncClient(transport=CustomTransport(file=file))}) - for prop in property: + for prop in client_path: client = getattr(client, prop) return client diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index f20ff191edc..c8f6453922c 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -1397,7 +1397,11 @@ def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai parser = langchain_core.output_parsers.JsonOutputParser() chain = model | parser - inp = 'output a list of the country france their population in JSON format. Use a dict with an outer key of "countries" which contains a list of countries. Each country should have the key `name` and `population`' + inp = """ + output a list of the country france their population in JSON format. + Use a dict with an outer key of "countries" which contains a list of countries. + Each country should have the key `name` and `population` + """ messages = [ langchain.schema.SystemMessage(content="You know everything about the world."), @@ -1406,6 +1410,8 @@ def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai for _ in chain.stream(input=messages): pass + + @pytest.mark.snapshot( # tool description is generated differently is some langchain_core versions ignores=["meta.langchain.request.tool.description"], From 62b83bda0e90c9f184b1b4142a05f659a543f94c Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 12:30:27 -0400 Subject: [PATCH 05/23] try making mocks for test shorter --- .../lcel_openai_chat_streamed_response.txt | 96 +------------ ...t_streamed_response_json_output_parser.txt | 126 +----------------- .../lcel_openai_llm_streamed_response.txt | 72 +--------- .../langchain/test_langchain_community.py | 14 +- ...gchain_community.test_astreamed_chain.json | 4 +- ...ngchain_community.test_astreamed_chat.json | 2 +- ...angchain_community.test_astreamed_llm.json | 2 +- ...ngchain_community.test_streamed_chain.json | 4 +- ...angchain_community.test_streamed_chat.json | 2 +- ...nity.test_streamed_json_output_parser.json | 4 +- ...langchain_community.test_streamed_llm.json | 2 +- 11 files changed, 31 insertions(+), 297 deletions(-) diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt index 0eb8841047e..56cc9060b39 100644 --- a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response.txt @@ -1,104 +1,16 @@ data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"#"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Python"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Basics"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"##"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Introduction"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n\n"},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Python"},"logprobs":null,"finish_reason":null}]} data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" high"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-level"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" general"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-purpose"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" programming"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" language"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" widely"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" used"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" web"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" development"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" analysis"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" artificial"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" intelligence"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" scientific"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" computing"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" many"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" other"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" applications"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" It"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" was"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" created"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" by"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Guid"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"o"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" van"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Ros"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n\n"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"sum"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"the"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" be"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" first"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"st!"},"logprobs":null,"finish_reason":null}]} data: {"id":"chatcmpl-A75i9IDlLLFDI6n75COTQV8IsQ91c","object":"chat.completion.chunk","created":1726253613,"model":"gpt-3.5-turbo-0125","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"length"}]} diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt index 838c1ae3c8e..659eb06572a 100644 --- a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt @@ -1,30 +1,6 @@ data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"Here's"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" country"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" France"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" its"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" population"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" JSON"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" format"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"Here"},"logprobs":null,"finish_reason":null}]} data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":":\n\n"},"logprobs":null,"finish_reason":null}]} @@ -44,107 +20,17 @@ data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.c data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" [\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"name"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"France"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"population"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"670"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"810"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"00"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" }\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" ]\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"}\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"``"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"`\n\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"Note"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" population"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" figure"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\"France"},"logprobs":null,"finish_reason":null}]} data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" an"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" approximation"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" country!\""},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\n}"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" may"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" vary"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" Always"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" recent"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" accurate"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" if"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":" needed"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"```"},"logprobs":null,"finish_reason":null}]} data: [DONE] diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt index b1b42a7f12c..2cd6857e909 100644 --- a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt @@ -1,76 +1,12 @@ data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"\n\n","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"1","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"Python","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":".","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" is","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Identify","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" coo","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" your","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" audience","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":":","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Before you","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" start","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" writing","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":",","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" it is important to","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" understand who","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" your","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" audience is","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":".","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Technical","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" documentation","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" can","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" be","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" written","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" for different types","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" of","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" people","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":",","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" such as developers,","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" end","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"-users","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":",","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" or","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" system","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" administrators","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":".","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" Knowing","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" your","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" audience","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} - -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" will help you","index":0,"logprobs":null,"finish_reason":"length"}],"model":"gpt-3.5-turbo-instruct"} +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" l!","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"","index":0,"logprobs":null,"finish_reason":"length"}],"model":"gpt-3.5-turbo-instruct"} diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index c8f6453922c..12aeb754c51 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -1284,7 +1284,7 @@ def test_streamed_chain(langchain_core, langchain_openai, streamed_response_resp module="openai", client_class_key="OpenAI", http_client_key="http_client", - property=["chat", "completions"], + client_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) @@ -1305,7 +1305,7 @@ def test_streamed_chat(langchain_openai, streamed_response_responder): module="openai", client_class_key="OpenAI", http_client_key="http_client", - property=["chat", "completions"], + client_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) model = langchain_openai.ChatOpenAI(client=client) @@ -1320,7 +1320,7 @@ def test_streamed_llm(langchain_openai, streamed_response_responder): module="openai", client_class_key="OpenAI", http_client_key="http_client", - property=["completions"], + client_path=["completions"], file="lcel_openai_llm_streamed_response.txt", ) @@ -1336,7 +1336,7 @@ async def test_astreamed_chain(langchain_core, langchain_openai, async_streamed_ module="openai", client_class_key="AsyncOpenAI", http_client_key="http_client", - property=["chat", "completions"], + client_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) @@ -1357,7 +1357,7 @@ async def test_astreamed_chat(langchain_openai, async_streamed_response_responde module="openai", client_class_key="AsyncOpenAI", http_client_key="http_client", - property=["chat", "completions"], + client_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) @@ -1373,7 +1373,7 @@ async def test_astreamed_llm(langchain_openai, async_streamed_response_responder module="openai", client_class_key="AsyncOpenAI", http_client_key="http_client", - property=["completions"], + client_path=["completions"], file="lcel_openai_llm_streamed_response.txt", ) @@ -1389,7 +1389,7 @@ def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai module="openai", client_class_key="OpenAI", http_client_key="http_client", - property=["chat", "completions"], + client_path=["chat", "completions"], file="lcel_openai_chat_streamed_response_json_output_parser.txt", ) diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json index f7dd8621412..4531bb6d3c9 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json @@ -14,7 +14,7 @@ "langchain.request.inputs.0.input": "how can langsmith help with testing?", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "langchain.response.content": "Python is\n\nthe best!", "language": "python", "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" }, @@ -51,7 +51,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d..." + "langchain.response.content": "Python is\n\nthe best!" }, "metrics": { "_dd.measured": 1 diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json index e25971c84c8..bf9e694b315 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json @@ -21,7 +21,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "langchain.response.content": "Python is\n\nthe best!", "language": "python", "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" }, diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json index ae3268a869d..0bb99266f9b 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json @@ -23,7 +23,7 @@ "langchain.request.provider": "openai", "langchain.request.stream": "True", "langchain.request.type": "llm", - "langchain.response.content": "\\n\\n1. Identify your audience: Before you start writing, it is important to understand who your audience is. Technical documenta...", + "langchain.response.content": "\\n\\nPython is cool!", "language": "python", "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" }, diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json index 09530b8db9f..abd9ee3b0a3 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json @@ -14,7 +14,7 @@ "langchain.request.inputs.0.input": "how can langsmith help with testing?", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "langchain.response.content": "Python is\n\nthe best!", "language": "python", "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" }, @@ -51,7 +51,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d..." + "langchain.response.content": "Python is\n\nthe best!" }, "metrics": { "_dd.measured": 1 diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json index 0e0ef59fa28..b036945c070 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json @@ -21,7 +21,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "# Python Basics\\n\\n## Introduction\\n\\nPython is a high-level, general-purpose programming language that is widely used for web d...", + "langchain.response.content": "Python is\n\nthe best!", "language": "python", "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" }, diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json index de01f0184f4..5cbbb55f02d 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json @@ -15,7 +15,7 @@ "langchain.request.inputs.1": "content='output a list of the country france their population in JSON format. Use a dict with an outer key of \"countries\" which ...", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "{\"countries\": [{\"name\": \"France\", \"population\": 67081000}]}", + "langchain.response.content": "{\"countries\": \"France is a country\"", "language": "python", "runtime-id": "525b9ac09789411c96a2663986ca6fa7" }, @@ -53,7 +53,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Here's the information for the country France and its population in JSON format:\\n\\n```json\\n{\\n \"countries\": [\\n {\\n \"..." + "langchain.response.content": "Here:\\n\\n```json\\n{\\n \"countries\": \"France is a country\"\\n}\\n```" }, "metrics": { "_dd.measured": 1 diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json index 0be42791387..487f657e09a 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json @@ -23,7 +23,7 @@ "langchain.request.provider": "openai", "langchain.request.stream": "True", "langchain.request.type": "llm", - "langchain.response.content": "\\n\\n1. Identify your audience: Before you start writing, it is important to understand who your audience is. Technical documenta...", + "langchain.response.content": "\\n\\nPython is cool!", "language": "python", "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" }, From 4527af76b75cfde9d0810864b50bfdfffc9f6494 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 12:49:38 -0400 Subject: [PATCH 06/23] fix snapshots --- tests/contrib/langchain/test_langchain_community.py | 8 +++----- ...ain.test_langchain_community.test_astreamed_chain.json | 4 ++-- ...hain.test_langchain_community.test_astreamed_chat.json | 2 +- ...hain.test_langchain_community.test_streamed_chain.json | 4 ++-- ...chain.test_langchain_community.test_streamed_chat.json | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index 12aeb754c51..14f3452c2d8 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -1397,11 +1397,9 @@ def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai parser = langchain_core.output_parsers.JsonOutputParser() chain = model | parser - inp = """ - output a list of the country france their population in JSON format. - Use a dict with an outer key of "countries" which contains a list of countries. - Each country should have the key `name` and `population` - """ + inp = ('output a list of the country france their population in JSON format. ' + 'Use a dict with an outer key of "countries" which contains a list of countries. ' + 'Each country should have the key `name` and `population`') messages = [ langchain.schema.SystemMessage(content="You know everything about the world."), diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json index 4531bb6d3c9..715ff8cd779 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json @@ -14,7 +14,7 @@ "langchain.request.inputs.0.input": "how can langsmith help with testing?", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "Python is\n\nthe best!", + "langchain.response.content": "Python is\\n\\nthe best!", "language": "python", "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" }, @@ -51,7 +51,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Python is\n\nthe best!" + "langchain.response.content": "Python is\\n\\nthe best!" }, "metrics": { "_dd.measured": 1 diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json index bf9e694b315..baebfa8f8b2 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json @@ -21,7 +21,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Python is\n\nthe best!", + "langchain.response.content": "Python is\\n\\nthe best!", "language": "python", "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" }, diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json index abd9ee3b0a3..39efc4affeb 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json @@ -14,7 +14,7 @@ "langchain.request.inputs.0.input": "how can langsmith help with testing?", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "Python is\n\nthe best!", + "langchain.response.content": "Python is\\n\\nthe best!", "language": "python", "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" }, @@ -51,7 +51,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Python is\n\nthe best!" + "langchain.response.content": "Python is\\n\\nthe best!" }, "metrics": { "_dd.measured": 1 diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json index b036945c070..b32c4fe1cf6 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json @@ -21,7 +21,7 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Python is\n\nthe best!", + "langchain.response.content": "Python is\\n\\nthe best!", "language": "python", "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" }, From 669498a9dbb8f743073d326a2a1222b3778af19e Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 13:01:00 -0400 Subject: [PATCH 07/23] fix mock and snapshot --- .../langchain_community/lcel_openai_llm_streamed_response.txt | 2 +- ...st_langchain_community.test_streamed_json_output_parser.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt index 2cd6857e909..e9be5532760 100644 --- a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_llm_streamed_response.txt @@ -6,7 +6,7 @@ data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","cre data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" coo","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} -data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":" l!","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} +data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"l!","index":0,"logprobs":null,"finish_reason":null}],"model":"gpt-3.5-turbo-instruct"} data: {"id":"cmpl-A76h6hDhraI7ckuzJD4IqzfSVfqJV","object":"text_completion","created":1726257392,"choices":[{"text":"","index":0,"logprobs":null,"finish_reason":"length"}],"model":"gpt-3.5-turbo-instruct"} diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json index 5cbbb55f02d..1ba70a1055d 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json @@ -15,7 +15,7 @@ "langchain.request.inputs.1": "content='output a list of the country france their population in JSON format. Use a dict with an outer key of \"countries\" which ...", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "{\"countries\": \"France is a country\"", + "langchain.response.content": "{\"countries\": \"France is a country!\"}", "language": "python", "runtime-id": "525b9ac09789411c96a2663986ca6fa7" }, From c0ae8770d33043005855086c9cc2bb7a45bcb72e Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 13:06:49 -0400 Subject: [PATCH 08/23] lint --- tests/contrib/langchain/test_langchain_community.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index 14f3452c2d8..74162bc5db4 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -1397,9 +1397,11 @@ def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai parser = langchain_core.output_parsers.JsonOutputParser() chain = model | parser - inp = ('output a list of the country france their population in JSON format. ' + inp = ( + "output a list of the country france their population in JSON format. " 'Use a dict with an outer key of "countries" which contains a list of countries. ' - 'Each country should have the key `name` and `population`') + "Each country should have the key `name` and `population`" + ) messages = [ langchain.schema.SystemMessage(content="You know everything about the world."), From cfb6a0337ff3ee7ec87f0942d8eba56723af0595 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 13:20:50 -0400 Subject: [PATCH 09/23] remove unnecessary astreamed snapshots --- .../langchain/test_langchain_community.py | 15 ++++- ...gchain_community.test_astreamed_chain.json | 61 ------------------- ...ngchain_community.test_astreamed_chat.json | 37 ----------- ...angchain_community.test_astreamed_llm.json | 39 ------------ 4 files changed, 12 insertions(+), 140 deletions(-) delete mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json delete mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json delete mode 100644 tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index 74162bc5db4..1a0d0acbc53 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -1330,7 +1330,10 @@ def test_streamed_llm(langchain_openai, streamed_response_responder): pass -@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +@pytest.mark.snapshot( + ignores=IGNORE_FIELDS, + token="tests.contrib.langchain.test_langchain_community.test_streamed_chain", +) async def test_astreamed_chain(langchain_core, langchain_openai, async_streamed_response_responder): client = async_streamed_response_responder( module="openai", @@ -1351,7 +1354,10 @@ async def test_astreamed_chain(langchain_core, langchain_openai, async_streamed_ pass -@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +@pytest.mark.snapshot( + ignores=IGNORE_FIELDS, + token="tests.contrib.langchain.test_langchain_community.test_streamed_chat", +) async def test_astreamed_chat(langchain_openai, async_streamed_response_responder): client = async_streamed_response_responder( module="openai", @@ -1367,7 +1373,10 @@ async def test_astreamed_chat(langchain_openai, async_streamed_response_responde pass -@pytest.mark.snapshot(ignores=IGNORE_FIELDS) +@pytest.mark.snapshot( + ignores=IGNORE_FIELDS, + token="tests.contrib.langchain.test_langchain_community.test_streamed_llm", +) async def test_astreamed_llm(langchain_openai, async_streamed_response_responder): client = async_streamed_response_responder( module="openai", diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json deleted file mode 100644 index 715ff8cd779..00000000000 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chain.json +++ /dev/null @@ -1,61 +0,0 @@ -[[ - { - "name": "langchain.request", - "service": "", - "resource": "langchain_core.runnables.base.RunnableSequence", - "trace_id": 0, - "span_id": 1, - "parent_id": 0, - "type": "", - "error": 0, - "meta": { - "_dd.p.dm": "-0", - "_dd.p.tid": "66e8391100000000", - "langchain.request.inputs.0.input": "how can langsmith help with testing?", - "langchain.request.stream": "True", - "langchain.request.type": "chain", - "langchain.response.content": "Python is\\n\\nthe best!", - "language": "python", - "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" - }, - "metrics": { - "_dd.measured": 1, - "_dd.top_level": 1, - "_dd.tracer_kr": 1.0, - "_sampling_priority_v1": 1, - "process_id": 47416 - }, - "duration": 52924000, - "start": 1726494993204980000 - }, - { - "name": "langchain.request", - "service": "", - "resource": "langchain_openai.chat_models.base.ChatOpenAI", - "trace_id": 0, - "span_id": 2, - "parent_id": 1, - "type": "", - "error": 0, - "meta": { - "langchain.request.api_key": "...key>", - "langchain.request.messages.0.0.content": "You are world class technical documentation writer.", - "langchain.request.messages.0.0.role": "SystemMessage", - "langchain.request.messages.0.1.content": "how can langsmith help with testing?", - "langchain.request.messages.0.1.role": "HumanMessage", - "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", - "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", - "langchain.request.openai-chat.parameters.n": "1", - "langchain.request.openai-chat.parameters.stream": "False", - "langchain.request.openai-chat.parameters.temperature": "0.7", - "langchain.request.provider": "openai-chat", - "langchain.request.stream": "True", - "langchain.request.type": "chat_model", - "langchain.response.content": "Python is\\n\\nthe best!" - }, - "metrics": { - "_dd.measured": 1 - }, - "duration": 44584000, - "start": 1726494993212392000 - }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json deleted file mode 100644 index baebfa8f8b2..00000000000 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_chat.json +++ /dev/null @@ -1,37 +0,0 @@ -[[ - { - "name": "langchain.request", - "service": "", - "resource": "langchain_openai.chat_models.base.ChatOpenAI", - "trace_id": 0, - "span_id": 1, - "parent_id": 0, - "type": "", - "error": 0, - "meta": { - "_dd.p.dm": "-0", - "_dd.p.tid": "66e8391100000000", - "langchain.request.api_key": "...key>", - "langchain.request.messages.0.content": "how can langsmith help with testing?", - "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", - "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", - "langchain.request.openai-chat.parameters.n": "1", - "langchain.request.openai-chat.parameters.stream": "False", - "langchain.request.openai-chat.parameters.temperature": "0.7", - "langchain.request.provider": "openai-chat", - "langchain.request.stream": "True", - "langchain.request.type": "chat_model", - "langchain.response.content": "Python is\\n\\nthe best!", - "language": "python", - "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" - }, - "metrics": { - "_dd.measured": 1, - "_dd.top_level": 1, - "_dd.tracer_kr": 1.0, - "_sampling_priority_v1": 1, - "process_id": 47416 - }, - "duration": 15030000, - "start": 1726494993326675000 - }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json deleted file mode 100644 index 0bb99266f9b..00000000000 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_astreamed_llm.json +++ /dev/null @@ -1,39 +0,0 @@ -[[ - { - "name": "langchain.request", - "service": "", - "resource": "langchain_openai.llms.base.OpenAI", - "trace_id": 0, - "span_id": 1, - "parent_id": 0, - "type": "", - "error": 0, - "meta": { - "_dd.p.dm": "-0", - "_dd.p.tid": "66e8391100000000", - "langchain.request.api_key": "...key>", - "langchain.request.openai.parameters.frequency_penalty": "0", - "langchain.request.openai.parameters.max_tokens": "256", - "langchain.request.openai.parameters.model_name": "gpt-3.5-turbo-instruct", - "langchain.request.openai.parameters.n": "1", - "langchain.request.openai.parameters.presence_penalty": "0", - "langchain.request.openai.parameters.temperature": "0.7", - "langchain.request.openai.parameters.top_p": "1", - "langchain.request.prompts.0": "How do I write technical documentation?", - "langchain.request.provider": "openai", - "langchain.request.stream": "True", - "langchain.request.type": "llm", - "langchain.response.content": "\\n\\nPython is cool!", - "language": "python", - "runtime-id": "5cc38abdb0cc498b9404b6d0dcbf576f" - }, - "metrics": { - "_dd.measured": 1, - "_dd.top_level": 1, - "_dd.tracer_kr": 1.0, - "_sampling_priority_v1": 1, - "process_id": 47416 - }, - "duration": 12486000, - "start": 1726494993287544000 - }]] From 47e7f3876425903f42caa2546f21a8c595f0ab22 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 13:21:15 -0400 Subject: [PATCH 10/23] fix jsonoutputparser snapshot --- ...chat_streamed_response_json_output_parser.txt | 2 +- ...mmunity.test_streamed_json_output_parser.json | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt index 659eb06572a..32cbb9677f4 100644 --- a/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt +++ b/tests/contrib/langchain/cassettes/langchain_community/lcel_openai_chat_streamed_response_json_output_parser.txt @@ -30,7 +30,7 @@ data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.c data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\n}"},"logprobs":null,"finish_reason":null}]} -data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"```"},"logprobs":null,"finish_reason":null}]} +data: {"id":"chatcmpl-A87AG6I7sUCYtwCx9oIF0UK8vJLkl","object":"chat.completion.chunk","created":1726497528,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_992d1ea92d","choices":[{"index":0,"delta":{"content":"\n```"},"logprobs":null,"finish_reason":null}]} data: [DONE] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json index 1ba70a1055d..9fc49c983e3 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json @@ -10,24 +10,24 @@ "error": 0, "meta": { "_dd.p.dm": "-0", - "_dd.p.tid": "66e8451000000000", + "_dd.p.tid": "66e868dd00000000", "langchain.request.inputs.0": "content='You know everything about the world.'", "langchain.request.inputs.1": "content='output a list of the country france their population in JSON format. Use a dict with an outer key of \"countries\" which ...", "langchain.request.stream": "True", "langchain.request.type": "chain", "langchain.response.content": "{\"countries\": \"France is a country!\"}", "language": "python", - "runtime-id": "525b9ac09789411c96a2663986ca6fa7" + "runtime-id": "4dfebe356bef4570b2d56e91c011025d" }, "metrics": { "_dd.measured": 1, "_dd.top_level": 1, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1, - "process_id": 8694 + "process_id": 56307 }, - "duration": 103255000, - "start": 1726498064306613000 + "duration": 47006000, + "start": 1726507229333366000 }, { "name": "langchain.request", @@ -53,11 +53,11 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Here:\\n\\n```json\\n{\\n \"countries\": \"France is a country\"\\n}\\n```" + "langchain.response.content": "Here:\\n\\n```json\\n{\\n \"countries\":\"France is a country!\"\\n}\\n```" }, "metrics": { "_dd.measured": 1 }, - "duration": 98388000, - "start": 1726498064310536000 + "duration": 42208000, + "start": 1726507229337869000 }]] From aaddc408523f3f29d76929e48913e4509092e886 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 16 Sep 2024 14:25:40 -0400 Subject: [PATCH 11/23] release note formatting --- .../notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml index e0269e4aff5..1b2ce64e68f 100644 --- a/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml +++ b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml @@ -1,4 +1,4 @@ --- features: - | - langchain: Adds support for tracing `stream` calls on LCEL chains, chat completion models, or completion models. + langchain: Adds support for tracing ``stream`` calls on LCEL chains, chat completion models, or completion models. From 087a2687b01a881f698e2c4984cdc8af43681ca9 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Fri, 20 Sep 2024 11:06:19 -0400 Subject: [PATCH 12/23] address some formatting concerns --- ddtrace/contrib/internal/langchain/patch.py | 144 +++++++++--------- ddtrace/contrib/internal/langchain/utils.py | 4 +- ...ngchain_community.test_streamed_chain.json | 22 +-- 3 files changed, 81 insertions(+), 89 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index 9ca2d4ac7ec..ff27d33b7a3 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -976,38 +976,37 @@ def traced_similarity_search(langchain, pin, func, instance, args, kwargs): return documents -# TODO refactor some of these on_span_started/on_span_finished functions -# that are used in other patched methods in this file into the utils module @with_traced_module def traced_chain_stream(langchain, pin, func, instance, args, kwargs): integration: LangChainIntegration = langchain._datadog_integration def _on_span_started(span: Span): inputs = get_argument_value(args, kwargs, 0, "input") - if integration.is_pc_sampled_span(span): - if not isinstance(inputs, list): - inputs = [inputs] - for idx, inp in enumerate(inputs): - if not isinstance(inp, dict): - span.set_tag_str("langchain.request.inputs.%d" % idx, integration.trunc(str(inp))) - else: - for k, v in inp.items(): - span.set_tag_str("langchain.request.inputs.%d.%s" % (idx, k), integration.trunc(str(v))) - - def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None): - if not error and integration.is_pc_sampled_span(span): - if langchain_core and isinstance(instance.steps[-1], langchain_core.output_parsers.JsonOutputParser): - # it's possible that the chain has a json output parser - # this will have already concatenated the chunks into a json object - - # it's also possible the json output parser isn't the last step, - # but one of the last steps, in which case we won't act on it here - # TODO (sam.brenner) make this more robust - content = json.dumps(streamed_chunks[-1]) - else: - # best effort to join chunks together - content = "".join([str(chunk) for chunk in streamed_chunks]) - span.set_tag_str("langchain.response.content", integration.trunc(content)) + if not integration.is_pc_sampled_span(span): + return + if not isinstance(inputs, list): + inputs = [inputs] + for idx, inp in enumerate(inputs): + if not isinstance(inp, dict): + span.set_tag_str("langchain.request.inputs.%d" % idx, integration.trunc(str(inp))) + continue + for k, v in inp.items(): + span.set_tag_str("langchain.request.inputs.%d.%s" % (idx, k), integration.trunc(str(v))) + + def _on_span_finished(span: Span, streamed_chunks): + if span.error or not integration.is_pc_sampled_span(span): + return + if langchain_core and isinstance(instance.steps[-1], langchain_core.output_parsers.JsonOutputParser): + # it's possible that the chain has a json output parser + # this will have already concatenated the chunks into a json object + + # it's also possible the json output parser isn't the last step, + # but one of the last steps, in which case we won't act on it here + content = json.dumps(streamed_chunks[-1]) + else: + # best effort to join chunks together + content = "".join([str(chunk) for chunk in streamed_chunks]) + span.set_tag_str("langchain.response.content", integration.trunc(content)) return shared_stream( integration=integration, @@ -1028,58 +1027,53 @@ def traced_chat_stream(langchain, pin, func, instance, args, kwargs): llm_provider = instance._llm_type def _on_span_started(span: Span): + if not integration.is_pc_sampled_span(span): + return chat_messages = get_argument_value(args, kwargs, 0, "input") - if not isinstance(chat_messages, list): + if langchain_core and isinstance(chat_messages, langchain_core.prompt_values.PromptValue): + chat_messages = chat_messages.to_messages() + elif not isinstance(chat_messages, list): chat_messages = [chat_messages] for message_idx, message in enumerate(chat_messages): - if integration.is_pc_sampled_span(span): - if isinstance(message, dict): - span.set_tag_str( - "langchain.request.messages.%d.content" % (message_idx), - integration.trunc(str(message.get("content", ""))), - ) - span.set_tag_str( - "langchain.request.messages.%d.role" % (message_idx), - str(message.get("role", "")), - ) - elif isinstance(message, langchain_core.prompt_values.PromptValue): - for langchain_message_idx, langchain_message in enumerate(message.messages): - span.set_tag_str( - "langchain.request.messages.%d.%d.content" % (message_idx, langchain_message_idx), - integration.trunc(str(langchain_message.content)), - ) - span.set_tag_str( - "langchain.request.messages.%d.%d.role" % (message_idx, langchain_message_idx), - str(langchain_message.__class__.__name__), - ) - elif isinstance(message, langchain_core.messages.BaseMessage): - span.set_tag_str( - "langchain.request.messages.%d.content" % (message_idx), integration.trunc(str(message.content)) - ) - span.set_tag_str( - "langchain.request.messages.%d.role" % (message_idx), str(message.__class__.__name__) - ) - else: - span.set_tag_str( - "langchain.request.messages.%d.content" % (message_idx), integration.trunc(message) - ) + if isinstance(message, dict): + span.set_tag_str( + "langchain.request.messages.%d.content" % (message_idx), + integration.trunc(str(message.get("content", ""))), + ) + span.set_tag_str( + "langchain.request.messages.%d.role" % (message_idx), + str(message.get("role", "")), + ) + elif langchain_core and isinstance(message, langchain_core.messages.BaseMessage): + content = message.content + role = message.__class__.__name__ + span.set_tag_str( + "langchain.request.messages.%d.content" % (message_idx), integration.trunc(str(content)) + ) + span.set_tag_str("langchain.request.messages.%d.role" % (message_idx), str(role)) + else: + span.set_tag_str( + "langchain.request.messages.%d.content" % (message_idx), integration.trunc(str(message)) + ) for param, val in getattr(instance, "_identifying_params", {}).items(): - if isinstance(val, dict): - for k, v in val.items(): - span.set_tag_str("langchain.request.%s.parameters.%s.%s" % (llm_provider, param, k), str(v)) - else: + if not isinstance(val, dict): span.set_tag_str("langchain.request.%s.parameters.%s" % (llm_provider, param), str(val)) - - def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None): - if not error and integration.is_pc_sampled_span(span): - content = "".join([str(chunk.content) for chunk in streamed_chunks]) - span.set_tag_str("langchain.response.content", integration.trunc(content)) - - usage = getattr(streamed_chunks[-1], "usage_metadata", None) - if usage: - for k, v in usage.items(): - span.set_tag_str("langchain.response.usage_metadata.%s" % k, str(v)) + continue + for k, v in val.items(): + span.set_tag_str("langchain.request.%s.parameters.%s.%s" % (llm_provider, param, k), str(v)) + + def _on_span_finished(span: Span, streamed_chunks): + if span.error or not integration.is_pc_sampled_span(span): + return + content = "".join([str(chunk.content) for chunk in streamed_chunks]) + span.set_tag_str("langchain.response.content", integration.trunc(content)) + + usage = getattr(streamed_chunks[-1], "usage_metadata", None) + if not usage: + return + for k, v in usage.items(): + span.set_tag_str("langchain.response.usage_metadata.%s" % k, str(v)) return shared_stream( integration=integration, @@ -1115,8 +1109,8 @@ def _on_span_start(span: Span): else: span.set_tag_str("langchain.request.%s.parameters.%s" % (llm_provider, param), str(val)) - def _on_span_finished(span: Span, streamed_chunks, error: Optional[bool] = None): - if not error and integration.is_pc_sampled_span(span): + def _on_span_finished(span: Span, streamed_chunks): + if not span.error and integration.is_pc_sampled_span(span): content = "".join([str(chunk) for chunk in streamed_chunks]) span.set_tag_str("langchain.response.content", integration.trunc(content)) @@ -1388,8 +1382,6 @@ def patch(): ) wrap("langchain_core", "runnables.base.RunnableSequence.batch", traced_lcel_runnable_sequence(langchain)) wrap("langchain_core", "runnables.base.RunnableSequence.abatch", traced_lcel_runnable_sequence_async(langchain)) - - # streaming wrap("langchain_core", "runnables.base.RunnableSequence.stream", traced_chain_stream(langchain)) wrap("langchain_core", "runnables.base.RunnableSequence.astream", traced_chain_stream(langchain)) wrap( diff --git a/ddtrace/contrib/internal/langchain/utils.py b/ddtrace/contrib/internal/langchain/utils.py index 330c890875b..22953987391 100644 --- a/ddtrace/contrib/internal/langchain/utils.py +++ b/ddtrace/contrib/internal/langchain/utils.py @@ -22,7 +22,7 @@ def __iter__(self): self._dd_integration.metric(self._dd_span, "incr", "request.error", 1) raise finally: - self._on_span_finish(self._dd_span, self._chunks, error=bool(self._dd_span.error)) + self._on_span_finish(self._dd_span, self._chunks) self._dd_span.finish() @@ -37,7 +37,7 @@ async def __aiter__(self): self._dd_integration.metric(self._dd_span, "incr", "request.error", 1) raise finally: - self._on_span_finish(self._dd_span, self._chunks, error=bool(self._dd_span.error)) + self._on_span_finish(self._dd_span, self._chunks) self._dd_span.finish() diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json index 39efc4affeb..9d07616dcef 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json @@ -10,23 +10,23 @@ "error": 0, "meta": { "_dd.p.dm": "-0", - "_dd.p.tid": "66e8388f00000000", + "_dd.p.tid": "66ed8c2300000000", "langchain.request.inputs.0.input": "how can langsmith help with testing?", "langchain.request.stream": "True", "langchain.request.type": "chain", "langchain.response.content": "Python is\\n\\nthe best!", "language": "python", - "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" + "runtime-id": "ff238804f0144660a02e67c04e6c6486" }, "metrics": { "_dd.measured": 1, "_dd.top_level": 1, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1, - "process_id": 44915 + "process_id": 74962 }, - "duration": 49336000, - "start": 1726494863602561000 + "duration": 7142000, + "start": 1726843939566754000 }, { "name": "langchain.request", @@ -39,10 +39,10 @@ "error": 0, "meta": { "langchain.request.api_key": "...key>", - "langchain.request.messages.0.0.content": "You are world class technical documentation writer.", - "langchain.request.messages.0.0.role": "SystemMessage", - "langchain.request.messages.0.1.content": "how can langsmith help with testing?", - "langchain.request.messages.0.1.role": "HumanMessage", + "langchain.request.messages.0.content": "You are world class technical documentation writer.", + "langchain.request.messages.0.role": "SystemMessage", + "langchain.request.messages.1.content": "how can langsmith help with testing?", + "langchain.request.messages.1.role": "HumanMessage", "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", "langchain.request.openai-chat.parameters.n": "1", @@ -56,6 +56,6 @@ "metrics": { "_dd.measured": 1 }, - "duration": 42281000, - "start": 1726494863608857000 + "duration": 4567000, + "start": 1726843939569204000 }]] From 36c11bc781e1a89ed25f2a163c2a89511c951176 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Fri, 20 Sep 2024 11:15:51 -0400 Subject: [PATCH 13/23] more formatting --- ddtrace/contrib/internal/langchain/patch.py | 22 +++++++++++---------- tests/contrib/langchain/conftest.py | 2 -- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index ff27d33b7a3..8062e3ccec7 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1096,23 +1096,25 @@ def traced_llm_stream(langchain, pin, func, instance, args, kwargs): llm_provider = instance._llm_type def _on_span_start(span: Span): + if not integration.is_pc_sampled_span(span): + return inp = get_argument_value(args, kwargs, 0, "input") if not isinstance(inp, list): inp = [inp] - if integration.is_pc_sampled_span(span): - for idx, prompt in enumerate(inp): - span.set_tag_str("langchain.request.prompts.%d" % idx, integration.trunc(str(prompt))) + for idx, prompt in enumerate(inp): + span.set_tag_str("langchain.request.prompts.%d" % idx, integration.trunc(str(prompt))) for param, val in getattr(instance, "_identifying_params", {}).items(): - if isinstance(val, dict): - for k, v in val.items(): - span.set_tag_str("langchain.request.%s.parameters.%s.%s" % (llm_provider, param, k), str(v)) - else: + if not isinstance(val, dict): span.set_tag_str("langchain.request.%s.parameters.%s" % (llm_provider, param), str(val)) + return + for k, v in val.items(): + span.set_tag_str("langchain.request.%s.parameters.%s.%s" % (llm_provider, param, k), str(v)) def _on_span_finished(span: Span, streamed_chunks): - if not span.error and integration.is_pc_sampled_span(span): - content = "".join([str(chunk) for chunk in streamed_chunks]) - span.set_tag_str("langchain.response.content", integration.trunc(content)) + if span.error or not integration.is_pc_sampled_span(span): + return + content = "".join([str(chunk) for chunk in streamed_chunks]) + span.set_tag_str("langchain.response.content", integration.trunc(content)) return shared_stream( integration=integration, diff --git a/tests/contrib/langchain/conftest.py b/tests/contrib/langchain/conftest.py index d8d41d923df..026d947b7d9 100644 --- a/tests/contrib/langchain/conftest.py +++ b/tests/contrib/langchain/conftest.py @@ -166,7 +166,6 @@ def langchain_pinecone(ddtrace_config_langchain, mock_logs, mock_metrics, langch @pytest.fixture def streamed_response_responder(): - # TODO (sam.brenner): clean this up a bit, make it more generic try: import importlib import os @@ -205,7 +204,6 @@ def responder(module, client_class_key, http_client_key, client_path: list[str], @pytest.fixture def async_streamed_response_responder(): - # TODO (sam.brenner): clean this up a bit, make it more generic try: import importlib import os From 9dd6717614dc2a7813ebc2fa7e62a2d8d3079036 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Fri, 20 Sep 2024 11:21:31 -0400 Subject: [PATCH 14/23] fix llm input params tagging --- ddtrace/contrib/internal/langchain/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index 8062e3ccec7..da1ec1cae19 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1106,7 +1106,7 @@ def _on_span_start(span: Span): for param, val in getattr(instance, "_identifying_params", {}).items(): if not isinstance(val, dict): span.set_tag_str("langchain.request.%s.parameters.%s" % (llm_provider, param), str(val)) - return + continue for k, v in val.items(): span.set_tag_str("langchain.request.%s.parameters.%s.%s" % (llm_provider, param, k), str(v)) From ef0a51140fe46ab8f11b7f0d22d5d67dfc6e68c2 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Fri, 20 Sep 2024 12:55:46 -0400 Subject: [PATCH 15/23] generalize llm and chat model stream input tagging to types --- ddtrace/contrib/internal/langchain/patch.py | 34 ++----------------- ddtrace/contrib/internal/langchain/utils.py | 24 +++++++++++++ ...langchain_community.test_streamed_llm.json | 12 +++---- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index da1ec1cae19..b71f3b6fa1b 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -51,6 +51,7 @@ from ddtrace.contrib.internal.langchain.constants import text_embedding_models from ddtrace.contrib.internal.langchain.constants import vectorstore_classes from ddtrace.contrib.internal.langchain.utils import shared_stream +from ddtrace.contrib.internal.langchain.utils import tag_general_message_input from ddtrace.contrib.trace_utils import unwrap from ddtrace.contrib.trace_utils import with_traced_module from ddtrace.contrib.trace_utils import wrap @@ -1030,31 +1031,7 @@ def _on_span_started(span: Span): if not integration.is_pc_sampled_span(span): return chat_messages = get_argument_value(args, kwargs, 0, "input") - if langchain_core and isinstance(chat_messages, langchain_core.prompt_values.PromptValue): - chat_messages = chat_messages.to_messages() - elif not isinstance(chat_messages, list): - chat_messages = [chat_messages] - for message_idx, message in enumerate(chat_messages): - if isinstance(message, dict): - span.set_tag_str( - "langchain.request.messages.%d.content" % (message_idx), - integration.trunc(str(message.get("content", ""))), - ) - span.set_tag_str( - "langchain.request.messages.%d.role" % (message_idx), - str(message.get("role", "")), - ) - elif langchain_core and isinstance(message, langchain_core.messages.BaseMessage): - content = message.content - role = message.__class__.__name__ - span.set_tag_str( - "langchain.request.messages.%d.content" % (message_idx), integration.trunc(str(content)) - ) - span.set_tag_str("langchain.request.messages.%d.role" % (message_idx), str(role)) - else: - span.set_tag_str( - "langchain.request.messages.%d.content" % (message_idx), integration.trunc(str(message)) - ) + tag_general_message_input(span, chat_messages, integration, langchain_core) for param, val in getattr(instance, "_identifying_params", {}).items(): if not isinstance(val, dict): @@ -1099,10 +1076,7 @@ def _on_span_start(span: Span): if not integration.is_pc_sampled_span(span): return inp = get_argument_value(args, kwargs, 0, "input") - if not isinstance(inp, list): - inp = [inp] - for idx, prompt in enumerate(inp): - span.set_tag_str("langchain.request.prompts.%d" % idx, integration.trunc(str(prompt))) + tag_general_message_input(span, inp, integration, langchain_core) for param, val in getattr(instance, "_identifying_params", {}).items(): if not isinstance(val, dict): span.set_tag_str("langchain.request.%s.parameters.%s" % (llm_provider, param), str(val)) @@ -1361,8 +1335,6 @@ def patch(): from langchain.chains.base import Chain # noqa:F401 from langchain_core import messages # noqa: F401 from langchain_core import output_parsers # noqa: F401 - from langchain_core import prompt_values # noqa: F401 - from langchain_core.tools import BaseTool # noqa:F401 wrap("langchain_core", "language_models.llms.BaseLLM.generate", traced_llm_generate(langchain)) wrap("langchain_core", "language_models.llms.BaseLLM.agenerate", traced_llm_agenerate(langchain)) diff --git a/ddtrace/contrib/internal/langchain/utils.py b/ddtrace/contrib/internal/langchain/utils.py index 22953987391..b5d85c90a6a 100644 --- a/ddtrace/contrib/internal/langchain/utils.py +++ b/ddtrace/contrib/internal/langchain/utils.py @@ -77,3 +77,27 @@ def shared_stream( integration.metric(span, "incr", "request.error", 1) integration.metric(span, "dist", "request.duration", span.duration_ns) raise + + +def tag_general_message_input(span, inputs, integration, langchain_core): + if langchain_core and isinstance(inputs, langchain_core.prompt_values.PromptValue): + inputs = inputs.to_messages() + elif not isinstance(inputs, list): + inputs = [inputs] + for input_idx, inp in enumerate(inputs): + if isinstance(inp, dict): + span.set_tag_str( + "langchain.request.messages.%d.content" % (input_idx), + integration.trunc(str(inp.get("content", ""))), + ) + span.set_tag_str( + "langchain.request.messages.%d.role" % (input_idx), + str(inp.get("role", "")), + ) + elif langchain_core and isinstance(inp, langchain_core.messages.BaseMessage): + content = inp.content + role = inp.__class__.__name__ + span.set_tag_str("langchain.request.messages.%d.content" % (input_idx), integration.trunc(str(content))) + span.set_tag_str("langchain.request.messages.%d.role" % (input_idx), str(role)) + else: + span.set_tag_str("langchain.request.messages.%d.content" % (input_idx), integration.trunc(str(inp))) diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json index 487f657e09a..b69cc467915 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_llm.json @@ -10,8 +10,9 @@ "error": 0, "meta": { "_dd.p.dm": "-0", - "_dd.p.tid": "66e8388f00000000", + "_dd.p.tid": "66eda7d800000000", "langchain.request.api_key": "...key>", + "langchain.request.messages.0.content": "How do I write technical documentation?", "langchain.request.openai.parameters.frequency_penalty": "0", "langchain.request.openai.parameters.max_tokens": "256", "langchain.request.openai.parameters.model_name": "gpt-3.5-turbo-instruct", @@ -19,21 +20,20 @@ "langchain.request.openai.parameters.presence_penalty": "0", "langchain.request.openai.parameters.temperature": "0.7", "langchain.request.openai.parameters.top_p": "1", - "langchain.request.prompts.0": "How do I write technical documentation?", "langchain.request.provider": "openai", "langchain.request.stream": "True", "langchain.request.type": "llm", "langchain.response.content": "\\n\\nPython is cool!", "language": "python", - "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" + "runtime-id": "0de46c287bae4eb081fc848584384768" }, "metrics": { "_dd.measured": 1, "_dd.top_level": 1, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1, - "process_id": 44915 + "process_id": 21857 }, - "duration": 9120000, - "start": 1726494863700015000 + "duration": 7765000, + "start": 1726851032766410000 }]] From 0c45612b14fa09f2df75f78712ff47640d1e630e Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Fri, 20 Sep 2024 15:58:34 -0400 Subject: [PATCH 16/23] update release note --- .../notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml index 1b2ce64e68f..852ddc3bf3a 100644 --- a/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml +++ b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml @@ -2,3 +2,4 @@ features: - | langchain: Adds support for tracing ``stream`` calls on LCEL chains, chat completion models, or completion models. + Due to an upstream issue with the ``langchain`` library itself, streamed responses are not tagged correctly when the `n` parameter on the model is set to greater than 1. From 7cae821234fdc6cc514171760bc5fe690a9f4948 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Fri, 20 Sep 2024 16:02:54 -0400 Subject: [PATCH 17/23] clarify client path option --- tests/contrib/langchain/conftest.py | 12 ++++++++---- .../contrib/langchain/test_langchain_community.py | 14 +++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/contrib/langchain/conftest.py b/tests/contrib/langchain/conftest.py index 026d947b7d9..5403417f42e 100644 --- a/tests/contrib/langchain/conftest.py +++ b/tests/contrib/langchain/conftest.py @@ -186,12 +186,14 @@ def handle_request(self, request: httpx.Request) -> httpx.Response: content = f.read() return httpx.Response(200, request=request, content=content) - def responder(module, client_class_key, http_client_key, client_path: list[str], file: str): + def responder(module, client_class_key, http_client_key, endpoint_path: list[str], file: str): + # endpoint_path specified the specific endpoint to retrieve as a client off of the general client + # ie, ["chat", "completions"] would represent openai.chat.completions clientModule = importlib.import_module(module) # openai, anthropic, etc. client_class = getattr(clientModule, client_class_key) client = client_class(**{http_client_key: httpx.Client(transport=CustomTransport(file=file))}) - for prop in client_path: + for prop in endpoint_path: client = getattr(client, prop) return client @@ -224,12 +226,14 @@ async def handle_async_request(self, request: httpx.Request) -> httpx.Response: content = f.read() return httpx.Response(200, request=request, content=content) - def responder(module, client_class_key, http_client_key, client_path: list[str], file: str): + def responder(module, client_class_key, http_client_key, endpoint_path: list[str], file: str): + # endpoint_path specified the specific endpoint to retrieve as a client off of the general client + # ie, ["chat", "completions"] would represent openai.chat.completions clientModule = importlib.import_module(module) # openai, anthropic, etc. client_class = getattr(clientModule, client_class_key) client = client_class(**{http_client_key: httpx.AsyncClient(transport=CustomTransport(file=file))}) - for prop in client_path: + for prop in endpoint_path: client = getattr(client, prop) return client diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index 1a0d0acbc53..11d05cf56a0 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -1284,7 +1284,7 @@ def test_streamed_chain(langchain_core, langchain_openai, streamed_response_resp module="openai", client_class_key="OpenAI", http_client_key="http_client", - client_path=["chat", "completions"], + endpoint_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) @@ -1305,7 +1305,7 @@ def test_streamed_chat(langchain_openai, streamed_response_responder): module="openai", client_class_key="OpenAI", http_client_key="http_client", - client_path=["chat", "completions"], + endpoint_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) model = langchain_openai.ChatOpenAI(client=client) @@ -1320,7 +1320,7 @@ def test_streamed_llm(langchain_openai, streamed_response_responder): module="openai", client_class_key="OpenAI", http_client_key="http_client", - client_path=["completions"], + endpoint_path=["completions"], file="lcel_openai_llm_streamed_response.txt", ) @@ -1339,7 +1339,7 @@ async def test_astreamed_chain(langchain_core, langchain_openai, async_streamed_ module="openai", client_class_key="AsyncOpenAI", http_client_key="http_client", - client_path=["chat", "completions"], + endpoint_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) @@ -1363,7 +1363,7 @@ async def test_astreamed_chat(langchain_openai, async_streamed_response_responde module="openai", client_class_key="AsyncOpenAI", http_client_key="http_client", - client_path=["chat", "completions"], + endpoint_path=["chat", "completions"], file="lcel_openai_chat_streamed_response.txt", ) @@ -1382,7 +1382,7 @@ async def test_astreamed_llm(langchain_openai, async_streamed_response_responder module="openai", client_class_key="AsyncOpenAI", http_client_key="http_client", - client_path=["completions"], + endpoint_path=["completions"], file="lcel_openai_llm_streamed_response.txt", ) @@ -1398,7 +1398,7 @@ def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai module="openai", client_class_key="OpenAI", http_client_key="http_client", - client_path=["chat", "completions"], + endpoint_path=["chat", "completions"], file="lcel_openai_chat_streamed_response_json_output_parser.txt", ) From 73349494d98664a342f2a3862bbe36024c75d8e8 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Tue, 24 Sep 2024 08:57:40 -0400 Subject: [PATCH 18/23] change stream response handling --- ddtrace/contrib/internal/langchain/utils.py | 48 ++++++++++++++----- .../langchain/test_langchain_community.py | 20 ++++++++ 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/utils.py b/ddtrace/contrib/internal/langchain/utils.py index b5d85c90a6a..531d93b51c3 100644 --- a/ddtrace/contrib/internal/langchain/utils.py +++ b/ddtrace/contrib/internal/langchain/utils.py @@ -12,33 +12,55 @@ def __init__(self, generator, integration, span, on_span_finish): class TracedLangchainStreamResponse(BaseTracedLangChainStreamResponse): + def __enter__(self): + self._generator.__enter__() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._generator.__exit__(exc_type, exc_val, exc_tb) + def __iter__(self): + return self + + def __next__(self): try: - for chunk in self._generator.__iter__(): - self._chunks.append(chunk) - yield chunk + chunk = self._generator.__next__() + self._chunks.append(chunk) + return chunk + except StopIteration: + self._on_span_finish(self._dd_span, self._chunks) + self._dd_span.finish() + raise except Exception: self._dd_span.set_exc_info(*sys.exc_info()) self._dd_integration.metric(self._dd_span, "incr", "request.error", 1) raise - finally: - self._on_span_finish(self._dd_span, self._chunks) - self._dd_span.finish() class TracedLangchainAsyncStreamResponse(BaseTracedLangChainStreamResponse): - async def __aiter__(self): + async def __aenter__(self): + await self._generator.__enter__() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self._generator.__exit__(exc_type, exc_val, exc_tb) + + def __aiter__(self): + return self + + async def __anext__(self): try: - async for chunk in self._generator.__aiter__(): - self._chunks.append(chunk) - yield chunk + chunk = await self._generator.__anext__() + self._chunks.append(chunk) + return chunk + except StopAsyncIteration: + self._on_span_finish(self._dd_span, self._chunks) + self._dd_span.finish() + raise except Exception: self._dd_span.set_exc_info(*sys.exc_info()) self._dd_integration.metric(self._dd_span, "incr", "request.error", 1) raise - finally: - self._on_span_finish(self._dd_span, self._chunks) - self._dd_span.finish() def shared_stream( diff --git a/tests/contrib/langchain/test_langchain_community.py b/tests/contrib/langchain/test_langchain_community.py index 11d05cf56a0..f0eb2bf31f8 100644 --- a/tests/contrib/langchain/test_langchain_community.py +++ b/tests/contrib/langchain/test_langchain_community.py @@ -1421,6 +1421,26 @@ def test_streamed_json_output_parser(langchain, langchain_core, langchain_openai pass +# until we fully support `astream_events`, we do not need a snapshot here +# this is just a regression test to make sure we don't throw +async def test_astreamed_events_does_not_throw(langchain_openai, langchain_core, async_streamed_response_responder): + client = async_streamed_response_responder( + module="openai", + client_class_key="AsyncOpenAI", + http_client_key="http_client", + endpoint_path=["chat", "completions"], + file="lcel_openai_chat_streamed_response.txt", + ) + + model = langchain_openai.ChatOpenAI(async_client=client) + parser = langchain_core.output_parsers.StrOutputParser() + + chain = model | parser + + async for _ in chain.astream_events(input="some input", version="v1"): + pass + + @pytest.mark.snapshot( # tool description is generated differently is some langchain_core versions ignores=["meta.langchain.request.tool.description"], From 4aece8826a3a017b76c22917cfd6cca46aa6044b Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Tue, 24 Sep 2024 09:43:45 -0400 Subject: [PATCH 19/23] add some extra guarding --- ddtrace/contrib/internal/langchain/patch.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index b71f3b6fa1b..a4104835f53 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -997,7 +997,11 @@ def _on_span_started(span: Span): def _on_span_finished(span: Span, streamed_chunks): if span.error or not integration.is_pc_sampled_span(span): return - if langchain_core and isinstance(instance.steps[-1], langchain_core.output_parsers.JsonOutputParser): + if ( + streamed_chunks + and langchain_core + and isinstance(instance.steps[-1], langchain_core.output_parsers.JsonOutputParser) + ): # it's possible that the chain has a json output parser # this will have already concatenated the chunks into a json object @@ -1046,7 +1050,7 @@ def _on_span_finished(span: Span, streamed_chunks): content = "".join([str(chunk.content) for chunk in streamed_chunks]) span.set_tag_str("langchain.response.content", integration.trunc(content)) - usage = getattr(streamed_chunks[-1], "usage_metadata", None) + usage = streamed_chunks and getattr(streamed_chunks[-1], "usage_metadata", None) if not usage: return for k, v in usage.items(): From 47a3f15fdc43b840d42c6749b0a9ed2681c68a19 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Tue, 24 Sep 2024 17:21:01 -0400 Subject: [PATCH 20/23] address review comments --- ddtrace/contrib/internal/langchain/patch.py | 7 +++---- ddtrace/contrib/internal/langchain/utils.py | 10 ++++++---- .../langchain-lcel-stream-calls-bff85c974a72cceb.yaml | 3 ++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index a4104835f53..8ec98330fa4 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1047,11 +1047,11 @@ def _on_span_started(span: Span): def _on_span_finished(span: Span, streamed_chunks): if span.error or not integration.is_pc_sampled_span(span): return - content = "".join([str(chunk.content) for chunk in streamed_chunks]) + content = "".join([str(getattr(chunk, "content", chunk)) for chunk in streamed_chunks]) span.set_tag_str("langchain.response.content", integration.trunc(content)) usage = streamed_chunks and getattr(streamed_chunks[-1], "usage_metadata", None) - if not usage: + if not usage or not isinstance(usage, dict): return for k, v in usage.items(): span.set_tag_str("langchain.response.usage_metadata.%s" % k, str(v)) @@ -1337,8 +1337,7 @@ def patch(): wrap("langchain", "embeddings.OpenAIEmbeddings.embed_documents", traced_embedding(langchain)) else: from langchain.chains.base import Chain # noqa:F401 - from langchain_core import messages # noqa: F401 - from langchain_core import output_parsers # noqa: F401 + from langchain_core.tools import BaseTool # noqa:F401 wrap("langchain_core", "language_models.llms.BaseLLM.generate", traced_llm_generate(langchain)) wrap("langchain_core", "language_models.llms.BaseLLM.agenerate", traced_llm_agenerate(langchain)) diff --git a/ddtrace/contrib/internal/langchain/utils.py b/ddtrace/contrib/internal/langchain/utils.py index 531d93b51c3..9d7c36fd749 100644 --- a/ddtrace/contrib/internal/langchain/utils.py +++ b/ddtrace/contrib/internal/langchain/utils.py @@ -112,10 +112,12 @@ def tag_general_message_input(span, inputs, integration, langchain_core): "langchain.request.messages.%d.content" % (input_idx), integration.trunc(str(inp.get("content", ""))), ) - span.set_tag_str( - "langchain.request.messages.%d.role" % (input_idx), - str(inp.get("role", "")), - ) + role = inp.get("role") + if role is not None: + span.set_tag_str( + "langchain.request.messages.%d.role" % (input_idx), + str(inp.get("role", "")), + ) elif langchain_core and isinstance(inp, langchain_core.messages.BaseMessage): content = inp.content role = inp.__class__.__name__ diff --git a/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml index 852ddc3bf3a..786e39d738c 100644 --- a/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml +++ b/releasenotes/notes/langchain-lcel-stream-calls-bff85c974a72cceb.yaml @@ -2,4 +2,5 @@ features: - | langchain: Adds support for tracing ``stream`` calls on LCEL chains, chat completion models, or completion models. - Due to an upstream issue with the ``langchain`` library itself, streamed responses are not tagged correctly when the `n` parameter on the model is set to greater than 1. + Note that due to an upstream issue with the ``langchain`` library itself, streamed responses will not be tagged correctly based on the choice index when the underlying model is configured to return ``n>1`` choices. + Please refer to this github issue for more details: https://github.com/langchain-ai/langchain/issues/26719 From 164cf1a926eadfc8fc88e64982858c607e18618e Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Wed, 25 Sep 2024 13:06:12 -0400 Subject: [PATCH 21/23] address review --- ddtrace/contrib/internal/langchain/patch.py | 5 +++- ddtrace/contrib/internal/langchain/utils.py | 4 ++-- ...ngchain_community.test_streamed_chain.json | 23 ++++++++++--------- ...angchain_community.test_streamed_chat.json | 11 +++++---- ...nity.test_streamed_json_output_parser.json | 23 ++++++++++--------- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index 8ec98330fa4..6a38e9825e2 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1011,7 +1011,7 @@ def _on_span_finished(span: Span, streamed_chunks): else: # best effort to join chunks together content = "".join([str(chunk) for chunk in streamed_chunks]) - span.set_tag_str("langchain.response.content", integration.trunc(content)) + span.set_tag_str("langchain.response.outputs", integration.trunc(content)) return shared_stream( integration=integration, @@ -1048,7 +1048,10 @@ def _on_span_finished(span: Span, streamed_chunks): if span.error or not integration.is_pc_sampled_span(span): return content = "".join([str(getattr(chunk, "content", chunk)) for chunk in streamed_chunks]) + role = streamed_chunks[0].__class__.__name__.replace("Chunk", "") if streamed_chunks else None # AIMessageChunk --> AIeMessage span.set_tag_str("langchain.response.content", integration.trunc(content)) + if role: + span.set_tag_str("langchain.response.message_type", role) usage = streamed_chunks and getattr(streamed_chunks[-1], "usage_metadata", None) if not usage or not isinstance(usage, dict): diff --git a/ddtrace/contrib/internal/langchain/utils.py b/ddtrace/contrib/internal/langchain/utils.py index 9d7c36fd749..8a2d67f31a5 100644 --- a/ddtrace/contrib/internal/langchain/utils.py +++ b/ddtrace/contrib/internal/langchain/utils.py @@ -115,13 +115,13 @@ def tag_general_message_input(span, inputs, integration, langchain_core): role = inp.get("role") if role is not None: span.set_tag_str( - "langchain.request.messages.%d.role" % (input_idx), + "langchain.request.messages.%d.message_type" % (input_idx), str(inp.get("role", "")), ) elif langchain_core and isinstance(inp, langchain_core.messages.BaseMessage): content = inp.content role = inp.__class__.__name__ span.set_tag_str("langchain.request.messages.%d.content" % (input_idx), integration.trunc(str(content))) - span.set_tag_str("langchain.request.messages.%d.role" % (input_idx), str(role)) + span.set_tag_str("langchain.request.messages.%d.message_type" % (input_idx), str(role)) else: span.set_tag_str("langchain.request.messages.%d.content" % (input_idx), integration.trunc(str(inp))) diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json index 9d07616dcef..a5b61fa5663 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chain.json @@ -10,23 +10,23 @@ "error": 0, "meta": { "_dd.p.dm": "-0", - "_dd.p.tid": "66ed8c2300000000", + "_dd.p.tid": "66f4277f00000000", "langchain.request.inputs.0.input": "how can langsmith help with testing?", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "Python is\\n\\nthe best!", + "langchain.response.outputs": "Python is\\n\\nthe best!", "language": "python", - "runtime-id": "ff238804f0144660a02e67c04e6c6486" + "runtime-id": "dd02a535c69f47fd835358ebdcba41f3" }, "metrics": { "_dd.measured": 1, "_dd.top_level": 1, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1, - "process_id": 74962 + "process_id": 88304 }, - "duration": 7142000, - "start": 1726843939566754000 + "duration": 7541000, + "start": 1727276927507127000 }, { "name": "langchain.request", @@ -40,9 +40,9 @@ "meta": { "langchain.request.api_key": "...key>", "langchain.request.messages.0.content": "You are world class technical documentation writer.", - "langchain.request.messages.0.role": "SystemMessage", + "langchain.request.messages.0.message_type": "SystemMessage", "langchain.request.messages.1.content": "how can langsmith help with testing?", - "langchain.request.messages.1.role": "HumanMessage", + "langchain.request.messages.1.message_type": "HumanMessage", "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", "langchain.request.openai-chat.parameters.model_name": "gpt-3.5-turbo", "langchain.request.openai-chat.parameters.n": "1", @@ -51,11 +51,12 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Python is\\n\\nthe best!" + "langchain.response.content": "Python is\\n\\nthe best!", + "langchain.response.message_type": "AIMessage" }, "metrics": { "_dd.measured": 1 }, - "duration": 4567000, - "start": 1726843939569204000 + "duration": 4894000, + "start": 1727276927509619000 }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json index b32c4fe1cf6..bc83c61268b 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_chat.json @@ -10,7 +10,7 @@ "error": 0, "meta": { "_dd.p.dm": "-0", - "_dd.p.tid": "66e8388f00000000", + "_dd.p.tid": "66f4277f00000000", "langchain.request.api_key": "...key>", "langchain.request.messages.0.content": "how can langsmith help with testing?", "langchain.request.openai-chat.parameters.model": "gpt-3.5-turbo", @@ -22,16 +22,17 @@ "langchain.request.stream": "True", "langchain.request.type": "chat_model", "langchain.response.content": "Python is\\n\\nthe best!", + "langchain.response.message_type": "AIMessage", "language": "python", - "runtime-id": "b3018660b0904cbfbe25aa695ac32c27" + "runtime-id": "dd02a535c69f47fd835358ebdcba41f3" }, "metrics": { "_dd.measured": 1, "_dd.top_level": 1, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1, - "process_id": 44915 + "process_id": 88304 }, - "duration": 9681000, - "start": 1726494863731905000 + "duration": 55964000, + "start": 1727276927342138000 }]] diff --git a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json index 9fc49c983e3..8376cc7626a 100644 --- a/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json +++ b/tests/snapshots/tests.contrib.langchain.test_langchain_community.test_streamed_json_output_parser.json @@ -10,24 +10,24 @@ "error": 0, "meta": { "_dd.p.dm": "-0", - "_dd.p.tid": "66e868dd00000000", + "_dd.p.tid": "66f4277f00000000", "langchain.request.inputs.0": "content='You know everything about the world.'", "langchain.request.inputs.1": "content='output a list of the country france their population in JSON format. Use a dict with an outer key of \"countries\" which ...", "langchain.request.stream": "True", "langchain.request.type": "chain", - "langchain.response.content": "{\"countries\": \"France is a country!\"}", + "langchain.response.outputs": "{\"countries\": \"France is a country!\"}", "language": "python", - "runtime-id": "4dfebe356bef4570b2d56e91c011025d" + "runtime-id": "dd02a535c69f47fd835358ebdcba41f3" }, "metrics": { "_dd.measured": 1, "_dd.top_level": 1, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1, - "process_id": 56307 + "process_id": 88304 }, - "duration": 47006000, - "start": 1726507229333366000 + "duration": 15160000, + "start": 1727276927466650000 }, { "name": "langchain.request", @@ -41,9 +41,9 @@ "meta": { "langchain.request.api_key": "...key>", "langchain.request.messages.0.content": "You know everything about the world.", - "langchain.request.messages.0.role": "SystemMessage", + "langchain.request.messages.0.message_type": "SystemMessage", "langchain.request.messages.1.content": "output a list of the country france their population in JSON format. Use a dict with an outer key of \"countries\" which contains ...", - "langchain.request.messages.1.role": "HumanMessage", + "langchain.request.messages.1.message_type": "HumanMessage", "langchain.request.openai-chat.parameters.max_tokens": "50", "langchain.request.openai-chat.parameters.model": "gpt-4o", "langchain.request.openai-chat.parameters.model_name": "gpt-4o", @@ -53,11 +53,12 @@ "langchain.request.provider": "openai-chat", "langchain.request.stream": "True", "langchain.request.type": "chat_model", - "langchain.response.content": "Here:\\n\\n```json\\n{\\n \"countries\":\"France is a country!\"\\n}\\n```" + "langchain.response.content": "Here:\\n\\n```json\\n{\\n \"countries\":\"France is a country!\"\\n}\\n```", + "langchain.response.message_type": "AIMessage" }, "metrics": { "_dd.measured": 1 }, - "duration": 42208000, - "start": 1726507229337869000 + "duration": 8959000, + "start": 1727276927472597000 }]] From 188f1bc23a3ae227b6bae89ef777f4fc602c7d05 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Wed, 25 Sep 2024 13:29:33 -0400 Subject: [PATCH 22/23] fmt --- ddtrace/contrib/internal/langchain/patch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index 6a38e9825e2..965b035ce14 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1048,7 +1048,9 @@ def _on_span_finished(span: Span, streamed_chunks): if span.error or not integration.is_pc_sampled_span(span): return content = "".join([str(getattr(chunk, "content", chunk)) for chunk in streamed_chunks]) - role = streamed_chunks[0].__class__.__name__.replace("Chunk", "") if streamed_chunks else None # AIMessageChunk --> AIeMessage + role = ( + streamed_chunks[0].__class__.__name__.replace("Chunk", "") if streamed_chunks else None + ) # AIMessageChunk --> AIeMessage span.set_tag_str("langchain.response.content", integration.trunc(content)) if role: span.set_tag_str("langchain.response.message_type", role) From 54ba9f01346c199edb4ebec775e74981a05df3b3 Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Wed, 25 Sep 2024 15:03:31 -0400 Subject: [PATCH 23/23] add removed json import --- ddtrace/contrib/internal/langchain/patch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index b6bf4bb2a37..8db95a9dae1 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -1,3 +1,4 @@ +import json import os import sys from typing import Any