From 6c1e21d1282a824fea52225f7b87e24a4edc95d7 Mon Sep 17 00:00:00 2001 From: Erick Friis Date: Tue, 18 Feb 2025 17:45:44 -0800 Subject: [PATCH] core: basemessage.text() (#29078) --- .../integration_template/chat_models.py | 2 +- libs/core/langchain_core/messages/base.py | 21 +++++++ libs/core/tests/unit_tests/test_messages.py | 58 +++++++++++++++++++ .../langchain_anthropic/chat_models.py | 2 +- .../langchain_deepseek/chat_models.py | 2 +- .../groq/langchain_groq/chat_models.py | 2 +- .../ollama/langchain_ollama/chat_models.py | 2 +- .../langchain_openai/chat_models/azure.py | 2 +- .../langchain_openai/chat_models/base.py | 2 +- .../partners/xai/langchain_xai/chat_models.py | 2 +- 10 files changed, 87 insertions(+), 8 deletions(-) diff --git a/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py b/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py index 917feb2df0b..3de9b63179f 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py @@ -86,7 +86,7 @@ class Chat__ModuleName__(BaseChatModel): .. code-block:: python for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python diff --git a/libs/core/langchain_core/messages/base.py b/libs/core/langchain_core/messages/base.py index 0c59f77e06e..5614f6fb7dd 100644 --- a/libs/core/langchain_core/messages/base.py +++ b/libs/core/langchain_core/messages/base.py @@ -92,6 +92,27 @@ class BaseMessage(Serializable): """ return ["langchain", "schema", "messages"] + def text(self) -> str: + """Get the text content of the message. + + Returns: + The text content of the message. + """ + if isinstance(self.content, str): + return self.content + + # must be a list + blocks = [ + block + for block in self.content + if isinstance(block, str) + or block.get("type") == "text" + and isinstance(block.get("text"), str) + ] + return "".join( + block if isinstance(block, str) else block["text"] for block in blocks + ) + def __add__(self, other: Any) -> ChatPromptTemplate: """Concatenate this message with another message.""" from langchain_core.prompts.chat import ChatPromptTemplate diff --git a/libs/core/tests/unit_tests/test_messages.py b/libs/core/tests/unit_tests/test_messages.py index c64611e5a8e..b6b517d2da9 100644 --- a/libs/core/tests/unit_tests/test_messages.py +++ b/libs/core/tests/unit_tests/test_messages.py @@ -1033,3 +1033,61 @@ def test_tool_message_tool_call_id() -> None: ToolMessage("foo", tool_call_id=uuid.uuid4()) ToolMessage("foo", tool_call_id=1) ToolMessage("foo", tool_call_id=1.0) + + +def test_message_text() -> None: + # partitions: + # message types: [ai], [human], [system], [tool] + # content types: [str], [list[str]], [list[dict]], [list[str | dict]] + # content: [empty], [single element], [multiple elements] + # content dict types: [text], [not text], [no type] + + assert HumanMessage(content="foo").text() == "foo" + assert AIMessage(content=[]).text() == "" + assert AIMessage(content=["foo", "bar"]).text() == "foobar" + assert ( + AIMessage( + content=[ + {"type": "text", "text": "thinking..."}, + { + "type": "tool_use", + "id": "toolu_01A09q90qw90lq917835lq9", + "name": "get_weather", + "input": {"location": "San Francisco, CA"}, + }, + ] + ).text() + == "thinking..." + ) + assert ( + SystemMessage(content=[{"type": "text", "text": "foo"}, "bar"]).text() + == "foobar" + ) + assert ( + ToolMessage( + content=[ + {"type": "text", "text": "15 degrees"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": "/9j/4AAQSkZJRg...", + }, + }, + ], + tool_call_id="1", + ).text() + == "15 degrees" + ) + assert ( + AIMessage(content=[{"text": "hi there"}, "hi"]).text() == "hi" + ) # missing type: text + assert AIMessage(content=[{"type": "nottext", "text": "hi"}]).text() == "" + assert AIMessage(content=[]).text() == "" + assert ( + AIMessage( + content="", tool_calls=[create_tool_call(name="a", args={"b": 1}, id=None)] + ).text() + == "" + ) diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 4af4b47ca61..5341c64e3a6 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -368,7 +368,7 @@ class ChatAnthropic(BaseChatModel): .. code-block:: python for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python diff --git a/libs/partners/deepseek/langchain_deepseek/chat_models.py b/libs/partners/deepseek/langchain_deepseek/chat_models.py index e7f3f3e3a7b..f3bcc2d1583 100644 --- a/libs/partners/deepseek/langchain_deepseek/chat_models.py +++ b/libs/partners/deepseek/langchain_deepseek/chat_models.py @@ -70,7 +70,7 @@ class ChatDeepSeek(BaseChatOpenAI): .. code-block:: python for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python diff --git a/libs/partners/groq/langchain_groq/chat_models.py b/libs/partners/groq/langchain_groq/chat_models.py index 962f72979b4..e971b5e39c1 100644 --- a/libs/partners/groq/langchain_groq/chat_models.py +++ b/libs/partners/groq/langchain_groq/chat_models.py @@ -172,7 +172,7 @@ class ChatGroq(BaseChatModel): .. code-block:: python for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python diff --git a/libs/partners/ollama/langchain_ollama/chat_models.py b/libs/partners/ollama/langchain_ollama/chat_models.py index 7a179b2fbed..fdbcf3c105a 100644 --- a/libs/partners/ollama/langchain_ollama/chat_models.py +++ b/libs/partners/ollama/langchain_ollama/chat_models.py @@ -228,7 +228,7 @@ class ChatOllama(BaseChatModel): ("human", "Return the words Hello World!"), ] for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python diff --git a/libs/partners/openai/langchain_openai/chat_models/azure.py b/libs/partners/openai/langchain_openai/chat_models/azure.py index cb4bc88a382..04c5e4e5c1d 100644 --- a/libs/partners/openai/langchain_openai/chat_models/azure.py +++ b/libs/partners/openai/langchain_openai/chat_models/azure.py @@ -183,7 +183,7 @@ class AzureChatOpenAI(BaseChatOpenAI): .. code-block:: python for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python diff --git a/libs/partners/openai/langchain_openai/chat_models/base.py b/libs/partners/openai/langchain_openai/chat_models/base.py index 976a6b2448e..6640110d36c 100644 --- a/libs/partners/openai/langchain_openai/chat_models/base.py +++ b/libs/partners/openai/langchain_openai/chat_models/base.py @@ -1616,7 +1616,7 @@ class ChatOpenAI(BaseChatOpenAI): # type: ignore[override] .. code-block:: python for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python diff --git a/libs/partners/xai/langchain_xai/chat_models.py b/libs/partners/xai/langchain_xai/chat_models.py index a6603c5316b..0081a734cf3 100644 --- a/libs/partners/xai/langchain_xai/chat_models.py +++ b/libs/partners/xai/langchain_xai/chat_models.py @@ -90,7 +90,7 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] .. code-block:: python for chunk in llm.stream(messages): - print(chunk) + print(chunk.text(), end="") .. code-block:: python