mirror of
https://github.com/hwchase17/langchain.git
synced 2025-10-24 20:20:50 +00:00
235 lines
9.5 KiB
Plaintext
235 lines
9.5 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ac47bfab-0f4f-42ce-8bb6-898ef22a0338",
|
|
"metadata": {},
|
|
"source": [
|
|
"# How to merge consecutive messages of the same type\n",
|
|
"\n",
|
|
"Certain models do not support passing in consecutive [messages](/docs/concepts/messages/) of the same type (a.k.a. \"runs\" of the same message type).\n",
|
|
"\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": 8,
|
|
"id": "1a215bbb-c05c-40b0-a6fd-d94884d517df",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"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'], 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!', additional_kwargs={}, response_metadata={})\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from langchain_core.messages import (\n",
|
|
" AIMessage,\n",
|
|
" HumanMessage,\n",
|
|
" SystemMessage,\n",
|
|
" merge_message_runs,\n",
|
|
")\n",
|
|
"\n",
|
|
"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(\"Why, he's probably chasing after the last cup of coffee in the office!\"),\n",
|
|
"]\n",
|
|
"\n",
|
|
"merged = merge_message_runs(messages)\n",
|
|
"print(\"\\n\\n\".join([repr(x) for x in merged]))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0544c811-7112-4b76-8877-cc897407c738",
|
|
"metadata": {},
|
|
"source": [
|
|
"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": "1b2eee74-71c8-4168-b968-bca580c25d18",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Chaining\n",
|
|
"\n",
|
|
"`merge_message_runs` can be used in an imperatively (like above) or declaratively, making it easy to compose with other components in a chain:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "6d5a0283-11f8-435b-b27b-7b18f7693592",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"AIMessage(content='\\n\\nAs for the actual answer, LangChain is named for connecting (chaining) language models together with other components. And Harrison Chase is one of the co-founders of LangChain, not someone being chased! \\n\\nBut I like to think he\\'s running after runaway tokens that escaped from the embedding space. \"Come back here, you vectors!\"', additional_kwargs={}, response_metadata={'id': 'msg_018MF8xBrM1ztw69XTx3Uxcy', 'model': 'claude-3-7-sonnet-20250219', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 84, 'output_tokens': 80, 'server_tool_use': None, 'service_tier': 'standard'}, 'model_name': 'claude-3-7-sonnet-20250219'}, id='run--caa1b9d6-a554-40ad-95cd-268938d8223b-0', usage_metadata={'input_tokens': 84, 'output_tokens': 80, 'total_tokens': 164, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})"
|
|
]
|
|
},
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"from langchain_anthropic import ChatAnthropic\n",
|
|
"\n",
|
|
"llm = ChatAnthropic(model=\"claude-3-7-sonnet-20250219\", temperature=0)\n",
|
|
"# Notice we don't pass in messages. This creates\n",
|
|
"# a RunnableLambda that takes messages as input\n",
|
|
"merger = merge_message_runs()\n",
|
|
"chain = merger | llm\n",
|
|
"chain.invoke(messages)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "72e90dce-693c-4842-9526-ce6460fe956b",
|
|
"metadata": {},
|
|
"source": [
|
|
"Looking at the LangSmith trace we can see that before the messages are passed to the model they are merged: https://smith.langchain.com/public/ab558677-cac9-4c59-9066-1ecce5bcd87c/r\n",
|
|
"\n",
|
|
"Looking at just the merger, we can see that it's a Runnable object that can be invoked like all Runnables:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"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.\", 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": 3,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"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=\"# Definition of a Convergent Series\\n\\nA series is a sum of terms in a sequence, typically written as:\\n\\n$$\\\\sum_{n=1}^{\\\\infty} a_n = a_1 + a_2 + a_3 + \\\\ldots$$\\n\\nA series is called **convergent** if the sequence of partial sums approaches a finite limit.\\n\\n## Formal Definition\\n\\nLet's define the sequence of partial sums:\\n$$S_N = \\\\sum_{n=1}^{N} a_n = a_1 + a_2 + \\\\ldots + a_N$$\\n\\nA series $\\\\sum_{n=1}^{\\\\infty} a_n$ is convergent if and only if:\\n- The limit of the partial sums exists and is finite\\n- That is, there exists a finite number $S$ such that $\\\\lim_{N \\\\to \\\\infty} S_N = S$\\n\\nIf this limit exists, we say the series converges to $S$, and we write:\\n$$\\\\sum_{n=1}^{\\\\infty} a_n = S$$\\n\\nIf the limit doesn't exist or is infinite, the series is called divergent.\", additional_kwargs={}, response_metadata={'id': 'msg_018ypyi2MTjV6S7jCydSqDn9', 'model': 'claude-3-7-sonnet-20250219', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 29, 'output_tokens': 273, 'server_tool_use': None, 'service_tier': 'standard'}, 'model_name': 'claude-3-7-sonnet-20250219'}, id='run--5de0ca29-d031-48f7-bc75-671eade20b74-0', usage_metadata={'input_tokens': 29, 'output_tokens': 273, 'total_tokens': 302, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}})"
|
|
]
|
|
},
|
|
"execution_count": 4,
|
|
"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",
|
|
"metadata": {},
|
|
"source": [
|
|
"## API reference\n",
|
|
"\n",
|
|
"For a complete description of all arguments head to the [API reference](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.merge_message_runs.html)"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "langchain",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.10.16"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|