Skip to content

Commit 91ec5ba

Browse files
authored
core[minor]: Message transformers (#5789)
* core[minor]: Message transformers * chore: lint files * mergeMessageRuns * use concat on messages to concat * cleanup tests * newline string content * implemented rest of code and tests * fixed another test * cr * use factory for shared test vars/funcs * added entrypoint, fixed circ dep * test circ dep * format and drop unnecessary entrypoint * docs and allow for returning runnables in each func * ported another doc * ported last doc * add missing md link * cr * added min core version notes to docs
1 parent 0a178dc commit 91ec5ba

18 files changed

+3028
-24
lines changed

docs/core_docs/docs/how_to/filter_messages.ipynb

+413
Large diffs are not rendered by default.

docs/core_docs/docs/how_to/index.mdx

+8
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ These are the core building blocks you can use when building applications.
7474
- [How to: stream a response back](/docs/how_to/chat_streaming)
7575
- [How to: track token usage](/docs/how_to/chat_token_usage_tracking)
7676

77+
### Messages
78+
79+
[Messages](/docs/concepts/##message-types) are the input and output of chat models. They have some `content` and a `role`, which describes the source of the message.
80+
81+
- [How to: trim messages](/docs/how_to/trim_messages/)
82+
- [How to: filter messages](/docs/how_to/filter_messages/)
83+
- [How to: merge consecutive messages of the same type](/docs/how_to/merge_message_runs/)
84+
7785
### LLMs
7886

7987
What LangChain calls [LLMs](/docs/concepts/#llms) are older forms of language models that take a string in and output a string.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "ac47bfab-0f4f-42ce-8bb6-898ef22a0338",
6+
"metadata": {},
7+
"source": [
8+
"# How to merge consecutive messages of the same type\n",
9+
"\n",
10+
":::note\n",
11+
"The `mergeMessageRuns` function is available in `@langchain/core` version `0.2.8` and above.\n",
12+
":::\n",
13+
"\n",
14+
"Certain models do not support passing in consecutive messages of the same type (a.k.a. \"runs\" of the same message type).\n",
15+
"\n",
16+
"The `mergeMessageRuns` utility makes it easy to merge consecutive messages of the same type.\n",
17+
"\n",
18+
"## Basic usage"
19+
]
20+
},
21+
{
22+
"cell_type": "code",
23+
"execution_count": 1,
24+
"id": "1a215bbb-c05c-40b0-a6fd-d94884d517df",
25+
"metadata": {},
26+
"outputs": [
27+
{
28+
"name": "stdout",
29+
"output_type": "stream",
30+
"text": [
31+
"{\n",
32+
" \"role\": \"system\",\n",
33+
" \"content\": \"you're a good assistant.\\nyou always respond with a joke.\"\n",
34+
"}\n",
35+
"\n",
36+
"{\n",
37+
" \"role\": \"human\",\n",
38+
" \"content\": [\n",
39+
" {\n",
40+
" \"type\": \"text\",\n",
41+
" \"text\": \"i wonder why it's called langchain\"\n",
42+
" },\n",
43+
" {\n",
44+
" \"type\": \"text\",\n",
45+
" \"text\": \"and who is harrison chasing anyways\"\n",
46+
" }\n",
47+
" ]\n",
48+
"}\n",
49+
"\n",
50+
"{\n",
51+
" \"role\": \"ai\",\n",
52+
" \"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",
53+
"}\n"
54+
]
55+
}
56+
],
57+
"source": [
58+
"import { HumanMessage, SystemMessage, AIMessage, mergeMessageRuns } from \"@langchain/core/messages\";\n",
59+
"\n",
60+
"const messages = [\n",
61+
" new SystemMessage(\"you're a good assistant.\"),\n",
62+
" new SystemMessage(\"you always respond with a joke.\"),\n",
63+
" new HumanMessage({ content: [{\"type\": \"text\", \"text\": \"i wonder why it's called langchain\"}] }),\n",
64+
" new HumanMessage(\"and who is harrison chasing anyways\"),\n",
65+
" new AIMessage(\n",
66+
" 'Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!'\n",
67+
" ),\n",
68+
" new AIMessage(\"Why, he's probably chasing after the last cup of coffee in the office!\"),\n",
69+
"];\n",
70+
"\n",
71+
"const merged = mergeMessageRuns(messages);\n",
72+
"console.log(merged.map((x) => JSON.stringify({\n",
73+
" role: x._getType(),\n",
74+
" content: x.content,\n",
75+
"}, null, 2)).join(\"\\n\\n\"));"
76+
]
77+
},
78+
{
79+
"cell_type": "markdown",
80+
"id": "0544c811-7112-4b76-8877-cc897407c738",
81+
"metadata": {},
82+
"source": [
83+
"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."
84+
]
85+
},
86+
{
87+
"cell_type": "markdown",
88+
"id": "1b2eee74-71c8-4168-b968-bca580c25d18",
89+
"metadata": {},
90+
"source": [
91+
"## Chaining\n",
92+
"\n",
93+
"`mergeMessageRuns` can be used in an imperatively (like above) or declaratively, making it easy to compose with other components in a chain:"
94+
]
95+
},
96+
{
97+
"cell_type": "code",
98+
"execution_count": 2,
99+
"id": "6d5a0283-11f8-435b-b27b-7b18f7693592",
100+
"metadata": {},
101+
"outputs": [
102+
{
103+
"name": "stdout",
104+
"output_type": "stream",
105+
"text": [
106+
"AIMessage {\n",
107+
" lc_serializable: true,\n",
108+
" lc_kwargs: {\n",
109+
" content: [],\n",
110+
" additional_kwargs: {\n",
111+
" id: 'msg_01LsdS4bjQ3EznH7Tj4xujV1',\n",
112+
" type: 'message',\n",
113+
" role: 'assistant',\n",
114+
" model: 'claude-3-sonnet-20240229',\n",
115+
" stop_reason: 'end_turn',\n",
116+
" stop_sequence: null,\n",
117+
" usage: [Object]\n",
118+
" },\n",
119+
" tool_calls: [],\n",
120+
" usage_metadata: { input_tokens: 84, output_tokens: 3, total_tokens: 87 },\n",
121+
" invalid_tool_calls: [],\n",
122+
" response_metadata: {}\n",
123+
" },\n",
124+
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
125+
" content: [],\n",
126+
" name: undefined,\n",
127+
" additional_kwargs: {\n",
128+
" id: 'msg_01LsdS4bjQ3EznH7Tj4xujV1',\n",
129+
" type: 'message',\n",
130+
" role: 'assistant',\n",
131+
" model: 'claude-3-sonnet-20240229',\n",
132+
" stop_reason: 'end_turn',\n",
133+
" stop_sequence: null,\n",
134+
" usage: { input_tokens: 84, output_tokens: 3 }\n",
135+
" },\n",
136+
" response_metadata: {\n",
137+
" id: 'msg_01LsdS4bjQ3EznH7Tj4xujV1',\n",
138+
" model: 'claude-3-sonnet-20240229',\n",
139+
" stop_reason: 'end_turn',\n",
140+
" stop_sequence: null,\n",
141+
" usage: { input_tokens: 84, output_tokens: 3 }\n",
142+
" },\n",
143+
" id: undefined,\n",
144+
" tool_calls: [],\n",
145+
" invalid_tool_calls: [],\n",
146+
" usage_metadata: { input_tokens: 84, output_tokens: 3, total_tokens: 87 }\n",
147+
"}\n"
148+
]
149+
}
150+
],
151+
"source": [
152+
"import { ChatAnthropic } from \"@langchain/anthropic\";\n",
153+
"import { mergeMessageRuns } from \"@langchain/core/messages\";\n",
154+
"\n",
155+
"const llm = new ChatAnthropic({ model: \"claude-3-sonnet-20240229\", temperature: 0 });\n",
156+
"// Notice we don't pass in messages. This creates\n",
157+
"// a RunnableLambda that takes messages as input\n",
158+
"const merger = mergeMessageRuns();\n",
159+
"const chain = merger.pipe(llm);\n",
160+
"await chain.invoke(messages);"
161+
]
162+
},
163+
{
164+
"cell_type": "markdown",
165+
"id": "72e90dce-693c-4842-9526-ce6460fe956b",
166+
"metadata": {},
167+
"source": [
168+
"Looking at [the LangSmith trace](https://smith.langchain.com/public/48d256fb-fd7e-48a0-bdfd-217ab74ad01d/r) we can see that before the messages are passed to the model they are merged.\n",
169+
"\n",
170+
"Looking at just the merger, we can see that it's a Runnable object that can be invoked like all Runnables:"
171+
]
172+
},
173+
{
174+
"cell_type": "code",
175+
"execution_count": 3,
176+
"id": "460817a6-c327-429d-958e-181a8c46059c",
177+
"metadata": {},
178+
"outputs": [
179+
{
180+
"name": "stdout",
181+
"output_type": "stream",
182+
"text": [
183+
"[\n",
184+
" SystemMessage {\n",
185+
" lc_serializable: true,\n",
186+
" lc_kwargs: {\n",
187+
" content: \"you're a good assistant.\\nyou always respond with a joke.\",\n",
188+
" name: undefined,\n",
189+
" additional_kwargs: {},\n",
190+
" response_metadata: {},\n",
191+
" id: undefined\n",
192+
" },\n",
193+
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
194+
" content: \"you're a good assistant.\\nyou always respond with a joke.\",\n",
195+
" name: undefined,\n",
196+
" additional_kwargs: {},\n",
197+
" response_metadata: {},\n",
198+
" id: undefined\n",
199+
" },\n",
200+
" HumanMessage {\n",
201+
" lc_serializable: true,\n",
202+
" lc_kwargs: {\n",
203+
" content: [Array],\n",
204+
" name: undefined,\n",
205+
" additional_kwargs: {},\n",
206+
" response_metadata: {},\n",
207+
" id: undefined\n",
208+
" },\n",
209+
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
210+
" content: [ [Object], [Object] ],\n",
211+
" name: undefined,\n",
212+
" additional_kwargs: {},\n",
213+
" response_metadata: {},\n",
214+
" id: undefined\n",
215+
" },\n",
216+
" AIMessage {\n",
217+
" lc_serializable: true,\n",
218+
" lc_kwargs: {\n",
219+
" content: `Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn't have the same ring to it!\\n` +\n",
220+
" \"Why, he's probably chasing after the last cup of coffee in the office!\",\n",
221+
" name: undefined,\n",
222+
" additional_kwargs: {},\n",
223+
" response_metadata: {},\n",
224+
" id: undefined,\n",
225+
" tool_calls: [],\n",
226+
" invalid_tool_calls: [],\n",
227+
" usage_metadata: undefined\n",
228+
" },\n",
229+
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
230+
" content: `Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn't have the same ring to it!\\n` +\n",
231+
" \"Why, he's probably chasing after the last cup of coffee in the office!\",\n",
232+
" name: undefined,\n",
233+
" additional_kwargs: {},\n",
234+
" response_metadata: {},\n",
235+
" id: undefined,\n",
236+
" tool_calls: [],\n",
237+
" invalid_tool_calls: [],\n",
238+
" usage_metadata: undefined\n",
239+
" }\n",
240+
"]\n"
241+
]
242+
}
243+
],
244+
"source": [
245+
"await merger.invoke(messages)"
246+
]
247+
},
248+
{
249+
"cell_type": "markdown",
250+
"id": "4548d916-ce21-4dc6-8f19-eedb8003ace6",
251+
"metadata": {},
252+
"source": [
253+
"## API reference\n",
254+
"\n",
255+
"For a complete description of all arguments head to the [API reference](https://api.js.langchain.com/functions/langchain_core_messages.mergeMessageRuns.html)."
256+
]
257+
}
258+
],
259+
"metadata": {
260+
"kernelspec": {
261+
"display_name": "TypeScript",
262+
"language": "typescript",
263+
"name": "tslab"
264+
},
265+
"language_info": {
266+
"codemirror_mode": {
267+
"mode": "typescript",
268+
"name": "javascript",
269+
"typescript": true
270+
},
271+
"file_extension": ".ts",
272+
"mimetype": "text/typescript",
273+
"name": "typescript",
274+
"version": "3.7.2"
275+
}
276+
},
277+
"nbformat": 4,
278+
"nbformat_minor": 5
279+
}

0 commit comments

Comments
 (0)