diff --git a/libs/core/langchain_core/messages/__init__.py b/libs/core/langchain_core/messages/__init__.py index 4a28feedd8f..e8cae860bec 100644 --- a/libs/core/langchain_core/messages/__init__.py +++ b/libs/core/langchain_core/messages/__init__.py @@ -44,6 +44,7 @@ from langchain_core.messages.utils import ( _message_from_dict, convert_to_messages, filter_messages, + format_messages_as, get_buffer_string, merge_message_runs, message_chunk_to_message, @@ -83,4 +84,5 @@ __all__ = [ "filter_messages", "merge_message_runs", "trim_messages", + "format_messages_as", ] diff --git a/libs/core/langchain_core/messages/utils.py b/libs/core/langchain_core/messages/utils.py index 0a916253397..3ad3d31699a 100644 --- a/libs/core/langchain_core/messages/utils.py +++ b/libs/core/langchain_core/messages/utils.py @@ -940,17 +940,38 @@ def format_messages_as( .. code-block:: python - from langchain_core.messages import format_messages_as + from langchain_core.messages import ( + format_messages_as, + AIMessage, + HumanMessage, + SystemMessage, + ToolMessage, + ) messages = [ - SystemMessage, - {}, - (), - AIMessage(), - ToolMessage(), + SystemMessage([{"type": "text", "text": "foo"}]), + {"role": "user", "content": [{"type": "text", "text": "whats in this"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"}}]}, + AIMessage("", tool_calls=[{"name": "analyze", "args": {"baz": "buz"}, "id": "1", "type": "tool_call"}]), + ToolMessage("foobar", tool_call_id="1", name="bar"), + {"role": "assistant", "content": "thats nice"}, ] oai_strings = format_messages_as(messages, format="openai", text="string") + # -> [ + # SystemMessage(content='foo'), + # HumanMessage(content=[{'type': 'text', 'text': 'whats in this'}, {'type': 'image_url', 'image_url': {'url': "data:image/png;base64,'/9j/4AAQSk'"}}]), + # AIMessage(content='', tool_calls=[{'name': 'analyze', 'args': {'baz': 'buz'}, 'id': '1', 'type': 'tool_call'}]), + # ToolMessage(content='foobar', name='bar', tool_call_id='1'), + # AIMessage(content='thats nice') + # ] + anthropic_blocks = format_messages_as(messages, format="anthropic", text="block") + # -> [ + # SystemMessage(content=[{'type': 'text', 'text': 'foo'}]), + # HumanMessage(content=[{'type': 'text', 'text': 'whats in this'}, {'type': 'image', 'source': {'type': 'base64', 'media_type': 'image/png', 'data': "'/9j/4AAQSk'"}}]), + # AIMessage(content=[{'type': 'tool_use', 'input': {'baz': 'buz'}, 'id': '1', 'name': 'analyze'}], tool_calls=[{'name': 'analyze', 'args': {'baz': 'buz'}, 'id': '1', 'type': 'tool_call'}]), + # HumanMessage(content=[{'type': 'tool_result', 'content': 'foobar', 'tool_use_id': '1', 'is_error': False}]), + # AIMessage(content=[{'type': 'text', 'text': 'thats nice'}]) + # ] .. dropdown:: Chain usage :open: @@ -1679,7 +1700,7 @@ def _openai_image_to_anthropic(image: dict) -> Dict: And throws an error if it's not a b64 image """ regex = r"^data:(?Pimage/.+);base64,(?P.+)$" - match = re.match(regex, image["image_url"]) + match = re.match(regex, image["image_url"]["url"]) if match is None: raise ValueError( "Anthropic only supports base64-encoded images currently." diff --git a/libs/core/tests/unit_tests/messages/test_imports.py b/libs/core/tests/unit_tests/messages/test_imports.py index 531409a4261..5a30b399379 100644 --- a/libs/core/tests/unit_tests/messages/test_imports.py +++ b/libs/core/tests/unit_tests/messages/test_imports.py @@ -32,6 +32,7 @@ EXPECTED_ALL = [ "filter_messages", "merge_message_runs", "trim_messages", + "format_messages_as", ] diff --git a/libs/core/tests/unit_tests/messages/test_utils.py b/libs/core/tests/unit_tests/messages/test_utils.py index abdd0a7eb5a..9a1d34bc3ed 100644 --- a/libs/core/tests/unit_tests/messages/test_utils.py +++ b/libs/core/tests/unit_tests/messages/test_utils.py @@ -672,7 +672,7 @@ def test_format_messages_as_anthropic_image() -> None: HumanMessage( content=[ {"type": "text", "text": "Here's an image:"}, - {"type": "image_url", "image_url": base64_image}, + {"type": "image_url", "image_url": {"url": base64_image}}, ] ) ]