From d67e1dfe32d16df9c8abd613c5e51e0da500d8f2 Mon Sep 17 00:00:00 2001 From: Bagatur <22008038+baskaryan@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:29:35 -0700 Subject: [PATCH] docs: fix merge messages how-to (#26351) --- docs/docs/how_to/merge_message_runs.ipynb | 132 ++++++++++++++------- libs/core/langchain_core/messages/utils.py | 2 +- 2 files changed, 87 insertions(+), 47 deletions(-) diff --git a/docs/docs/how_to/merge_message_runs.ipynb b/docs/docs/how_to/merge_message_runs.ipynb index 2481390fb0e..074c79bfe1a 100644 --- a/docs/docs/how_to/merge_message_runs.ipynb +++ b/docs/docs/how_to/merge_message_runs.ipynb @@ -11,12 +11,30 @@ "\n", "The `merge_message_runs` utility makes it easy to merge consecutive messages of the same type.\n", "\n", + "### Setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "198ce37f-4466-45a2-8878-d75cd01a5d23", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -qU langchain-core langchain-anthropic" + ] + }, + { + "cell_type": "markdown", + "id": "b5c3ca6e-e5b3-4151-8307-9101713a20ae", + "metadata": {}, + "source": [ "## Basic usage" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 8, "id": "1a215bbb-c05c-40b0-a6fd-d94884d517df", "metadata": {}, "outputs": [ @@ -24,11 +42,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "SystemMessage(content=\"you're a good assistant.\\nyou always respond with a joke.\")\n", + "SystemMessage(content=\"you're a good assistant.\\nyou always respond with a joke.\", additional_kwargs={}, response_metadata={})\n", "\n", - "HumanMessage(content=[{'type': 'text', 'text': \"i wonder why it's called langchain\"}, 'and who is harrison chasing anyways'])\n", + "HumanMessage(content=[{'type': 'text', 'text': \"i wonder why it's called langchain\"}, 'and who is harrison chasing anyways'], additional_kwargs={}, response_metadata={})\n", "\n", - "AIMessage(content='Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!\\nWhy, he\\'s probably chasing after the last cup of coffee in the office!')\n" + "AIMessage(content='Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!\\nWhy, he\\'s probably chasing after the last cup of coffee in the office!', additional_kwargs={}, response_metadata={})\n" ] } ], @@ -63,38 +81,6 @@ "Notice that if the contents of one of the messages to merge is a list of content blocks then the merged message will have a list of content blocks. And if both messages to merge have string contents then those are concatenated with a newline character." ] }, - { - "cell_type": "markdown", - "id": "11f7e8d3", - "metadata": {}, - "source": [ - "The `merge_message_runs` utility also works with messages composed together using the overloaded `+` operation:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b51855c5", - "metadata": {}, - "outputs": [], - "source": [ - "messages = (\n", - " SystemMessage(\"you're a good assistant.\")\n", - " + SystemMessage(\"you always respond with a joke.\")\n", - " + HumanMessage([{\"type\": \"text\", \"text\": \"i wonder why it's called langchain\"}])\n", - " + HumanMessage(\"and who is harrison chasing anyways\")\n", - " + AIMessage(\n", - " 'Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!'\n", - " )\n", - " + AIMessage(\n", - " \"Why, he's probably chasing after the last cup of coffee in the office!\"\n", - " )\n", - ")\n", - "\n", - "merged = merge_message_runs(messages)\n", - "print(\"\\n\\n\".join([repr(x) for x in merged]))" - ] - }, { "cell_type": "markdown", "id": "1b2eee74-71c8-4168-b968-bca580c25d18", @@ -107,23 +93,30 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 9, "id": "6d5a0283-11f8-435b-b27b-7b18f7693592", "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + }, { "data": { "text/plain": [ - "AIMessage(content=[], response_metadata={'id': 'msg_01D6R8Naum57q8qBau9vLBUX', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 84, 'output_tokens': 3}}, id='run-ac0c465b-b54f-4b8b-9295-e5951250d653-0', usage_metadata={'input_tokens': 84, 'output_tokens': 3, 'total_tokens': 87})" + "AIMessage(content=[], additional_kwargs={}, response_metadata={'id': 'msg_01KNGUMTuzBVfwNouLDpUMwf', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 84, 'output_tokens': 3}}, id='run-b908b198-9c24-450b-9749-9d4a8182937b-0', usage_metadata={'input_tokens': 84, 'output_tokens': 3, 'total_tokens': 87})" ] }, - "execution_count": 3, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# pip install -U langchain-anthropic\n", + "%pip install -qU langchain-anthropic\n", "from langchain_anthropic import ChatAnthropic\n", "\n", "llm = ChatAnthropic(model=\"claude-3-sonnet-20240229\", temperature=0)\n", @@ -146,19 +139,19 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 10, "id": "460817a6-c327-429d-958e-181a8c46059c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[SystemMessage(content=\"you're a good assistant.\\nyou always respond with a joke.\"),\n", - " HumanMessage(content=[{'type': 'text', 'text': \"i wonder why it's called langchain\"}, 'and who is harrison chasing anyways']),\n", - " AIMessage(content='Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!\\nWhy, he\\'s probably chasing after the last cup of coffee in the office!')]" + "[SystemMessage(content=\"you're a good assistant.\\nyou always respond with a joke.\", additional_kwargs={}, response_metadata={}),\n", + " HumanMessage(content=[{'type': 'text', 'text': \"i wonder why it's called langchain\"}, 'and who is harrison chasing anyways'], additional_kwargs={}, response_metadata={}),\n", + " AIMessage(content='Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!\\nWhy, he\\'s probably chasing after the last cup of coffee in the office!', additional_kwargs={}, response_metadata={})]" ] }, - "execution_count": 4, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -167,6 +160,53 @@ "merger.invoke(messages)" ] }, + { + "cell_type": "markdown", + "id": "4178837d-b155-492d-9404-d567accc1fa0", + "metadata": {}, + "source": [ + "`merge_message_runs` can also be placed after a prompt:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "620530ab-ed05-4899-b984-bfa4cd738465", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='A convergent series is an infinite series whose partial sums approach a finite value as more terms are added. In other words, the sequence of partial sums has a limit.\\n\\nMore formally, an infinite series Σ an (where an are the terms of the series) is said to be convergent if the sequence of partial sums:\\n\\nS1 = a1\\nS2 = a1 + a2 \\nS3 = a1 + a2 + a3\\n...\\nSn = a1 + a2 + a3 + ... + an\\n...\\n\\nconverges to some finite number S as n goes to infinity. We write:\\n\\nlim n→∞ Sn = S\\n\\nThe finite number S is called the sum of the convergent infinite series.\\n\\nIf the sequence of partial sums does not approach any finite limit, the infinite series is said to be divergent.\\n\\nSome key properties:\\n- A series converges if and only if the sequence of its partial sums is a Cauchy sequence.\\n- Absolute/conditional convergence criteria help determine if a given series converges.\\n- Convergent series have many important applications in mathematics, physics, engineering etc.', additional_kwargs={}, response_metadata={'id': 'msg_01MfV6y2hep7ZNvDz24A36U4', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 29, 'output_tokens': 267}}, id='run-9d925f58-021e-4bd0-94fc-f8f5e91010a4-0', usage_metadata={'input_tokens': 29, 'output_tokens': 267, 'total_tokens': 296})" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate\n", + "\n", + "prompt = ChatPromptTemplate(\n", + " [\n", + " (\"system\", \"You're great a {skill}\"),\n", + " (\"system\", \"You're also great at explaining things\"),\n", + " (\"human\", \"{query}\"),\n", + " ]\n", + ")\n", + "chain = prompt | merger | llm\n", + "chain.invoke({\"skill\": \"math\", \"query\": \"what's the definition of a convergent series\"})" + ] + }, + { + "cell_type": "markdown", + "id": "51ba533a-43c7-4e5f-bd91-a4ec23ceeb34", + "metadata": {}, + "source": [ + "LangSmith Trace: https://smith.langchain.com/public/432150b6-9909-40a7-8ae7-944b7e657438/r/f4ad5fb2-4d38-42a6-b780-25f62617d53f" + ] + }, { "cell_type": "markdown", "id": "4548d916-ce21-4dc6-8f19-eedb8003ace6", @@ -194,7 +234,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.1" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/libs/core/langchain_core/messages/utils.py b/libs/core/langchain_core/messages/utils.py index ef3eb6e5039..c18a85a1356 100644 --- a/libs/core/langchain_core/messages/utils.py +++ b/libs/core/langchain_core/messages/utils.py @@ -272,7 +272,7 @@ def _create_message_from_message_type( message = RemoveMessage(**kwargs) else: raise ValueError( - f"Unexpected message type: {message_type}. Use one of 'human'," + f"Unexpected message type: '{message_type}'. Use one of 'human'," f" 'user', 'ai', 'assistant', 'function', 'tool', or 'system'." ) return message