diff --git a/libs/core/langchain_core/outputs/chat_generation.py b/libs/core/langchain_core/outputs/chat_generation.py index bd61727219b..b104d9b6133 100644 --- a/libs/core/langchain_core/outputs/chat_generation.py +++ b/libs/core/langchain_core/outputs/chat_generation.py @@ -54,22 +54,33 @@ class ChatGeneration(Generation): Raises: ValueError: If the message is not a string or a list. """ - text = "" - if isinstance(self.message.content, str): - text = self.message.content - # Extracts first text block from content blocks. - # Skips blocks with explicit non-text type (e.g., thinking, reasoning). - elif isinstance(self.message.content, list): - for block in self.message.content: - if isinstance(block, str): - text = block - break - if isinstance(block, dict) and "text" in block: - block_type = block.get("type") - if block_type is None or block_type == "text": - text = block["text"] - break - self.text = text + # Check for legacy blocks with "text" key but no "type" field. + # Otherwise, delegate to `message.text`. + if isinstance(self.message.content, list): + has_legacy_blocks = any( + isinstance(block, dict) + and "text" in block + and block.get("type") is None + for block in self.message.content + ) + + if has_legacy_blocks: + blocks = [] + for block in self.message.content: + if isinstance(block, str): + blocks.append(block) + elif isinstance(block, dict): + block_type = block.get("type") + if block_type == "text" or ( + block_type is None and "text" in block + ): + blocks.append(block.get("text", "")) + self.text = "".join(blocks) + else: + self.text = self.message.text + else: + self.text = self.message.text + return self diff --git a/libs/core/tests/unit_tests/outputs/test_chat_generation.py b/libs/core/tests/unit_tests/outputs/test_chat_generation.py index b4b63c1dab5..2dd27d4a6bc 100644 --- a/libs/core/tests/unit_tests/outputs/test_chat_generation.py +++ b/libs/core/tests/unit_tests/outputs/test_chat_generation.py @@ -7,20 +7,27 @@ from langchain_core.outputs import ChatGeneration @pytest.mark.parametrize( - "content", + ("content", "expected"), [ - "foo", - ["foo"], - [{"text": "foo", "type": "text"}], - [ - {"tool_use": {}, "type": "tool_use"}, - {"text": "foo", "type": "text"}, - "bar", - ], + ("foo", "foo"), + (["foo"], "foo"), + (["foo", "bar"], "foobar"), + ([{"text": "foo", "type": "text"}], "foo"), + ( + [ + {"type": "text", "text": "foo"}, + {"type": "reasoning", "reasoning": "..."}, + {"type": "text", "text": "bar"}, + ], + "foobar", + ), + ([{"text": "foo"}], "foo"), + ([{"text": "foo"}, "bar"], "foobar"), ], ) -def test_msg_with_text(content: str | list[str | dict[str, Any]]) -> None: - expected = "foo" +def test_msg_with_text( + content: str | list[str | dict[str, Any]], expected: str +) -> None: actual = ChatGeneration(message=AIMessage(content=content)).text assert actual == expected