Skip to content

core[minor]: Message transformers #5789

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
413 changes: 413 additions & 0 deletions docs/core_docs/docs/how_to/filter_messages.ipynb

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions docs/core_docs/docs/how_to/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ These are the core building blocks you can use when building applications.
- [How to: stream a response back](/docs/how_to/chat_streaming)
- [How to: track token usage](/docs/how_to/chat_token_usage_tracking)

### Messages

[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.

- [How to: trim messages](/docs/how_to/trim_messages/)
- [How to: filter messages](/docs/how_to/filter_messages/)
- [How to: merge consecutive messages of the same type](/docs/how_to/merge_message_runs/)

### LLMs

What LangChain calls [LLMs](/docs/concepts/#llms) are older forms of language models that take a string in and output a string.
Expand Down
279 changes: 279 additions & 0 deletions docs/core_docs/docs/how_to/merge_message_runs.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "ac47bfab-0f4f-42ce-8bb6-898ef22a0338",
"metadata": {},
"source": [
"# How to merge consecutive messages of the same type\n",
"\n",
":::note\n",
"The `mergeMessageRuns` function is available in `@langchain/core` version `0.2.8` and above.\n",
":::\n",
"\n",
"Certain models do not support passing in consecutive messages of the same type (a.k.a. \"runs\" of the same message type).\n",
"\n",
"The `mergeMessageRuns` utility makes it easy to merge consecutive messages of the same type.\n",
"\n",
"## Basic usage"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "1a215bbb-c05c-40b0-a6fd-d94884d517df",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"role\": \"system\",\n",
" \"content\": \"you're a good assistant.\\nyou always respond with a joke.\"\n",
"}\n",
"\n",
"{\n",
" \"role\": \"human\",\n",
" \"content\": [\n",
" {\n",
" \"type\": \"text\",\n",
" \"text\": \"i wonder why it's called langchain\"\n",
" },\n",
" {\n",
" \"type\": \"text\",\n",
" \"text\": \"and who is harrison chasing anyways\"\n",
" }\n",
" ]\n",
"}\n",
"\n",
"{\n",
" \"role\": \"ai\",\n",
" \"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",
"}\n"
]
}
],
"source": [
"import { HumanMessage, SystemMessage, AIMessage, mergeMessageRuns } from \"@langchain/core/messages\";\n",
"\n",
"const messages = [\n",
" new SystemMessage(\"you're a good assistant.\"),\n",
" new SystemMessage(\"you always respond with a joke.\"),\n",
" new HumanMessage({ content: [{\"type\": \"text\", \"text\": \"i wonder why it's called langchain\"}] }),\n",
" new HumanMessage(\"and who is harrison chasing anyways\"),\n",
" new AIMessage(\n",
" 'Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn\\'t have the same ring to it!'\n",
" ),\n",
" new AIMessage(\"Why, he's probably chasing after the last cup of coffee in the office!\"),\n",
"];\n",
"\n",
"const merged = mergeMessageRuns(messages);\n",
"console.log(merged.map((x) => JSON.stringify({\n",
" role: x._getType(),\n",
" content: x.content,\n",
"}, null, 2)).join(\"\\n\\n\"));"
]
},
{
"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",
"`mergeMessageRuns` 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": 2,
"id": "6d5a0283-11f8-435b-b27b-7b18f7693592",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"AIMessage {\n",
" lc_serializable: true,\n",
" lc_kwargs: {\n",
" content: [],\n",
" additional_kwargs: {\n",
" id: 'msg_01LsdS4bjQ3EznH7Tj4xujV1',\n",
" type: 'message',\n",
" role: 'assistant',\n",
" model: 'claude-3-sonnet-20240229',\n",
" stop_reason: 'end_turn',\n",
" stop_sequence: null,\n",
" usage: [Object]\n",
" },\n",
" tool_calls: [],\n",
" usage_metadata: { input_tokens: 84, output_tokens: 3, total_tokens: 87 },\n",
" invalid_tool_calls: [],\n",
" response_metadata: {}\n",
" },\n",
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
" content: [],\n",
" name: undefined,\n",
" additional_kwargs: {\n",
" id: 'msg_01LsdS4bjQ3EznH7Tj4xujV1',\n",
" type: 'message',\n",
" role: 'assistant',\n",
" model: 'claude-3-sonnet-20240229',\n",
" stop_reason: 'end_turn',\n",
" stop_sequence: null,\n",
" usage: { input_tokens: 84, output_tokens: 3 }\n",
" },\n",
" response_metadata: {\n",
" id: 'msg_01LsdS4bjQ3EznH7Tj4xujV1',\n",
" model: 'claude-3-sonnet-20240229',\n",
" stop_reason: 'end_turn',\n",
" stop_sequence: null,\n",
" usage: { input_tokens: 84, output_tokens: 3 }\n",
" },\n",
" id: undefined,\n",
" tool_calls: [],\n",
" invalid_tool_calls: [],\n",
" usage_metadata: { input_tokens: 84, output_tokens: 3, total_tokens: 87 }\n",
"}\n"
]
}
],
"source": [
"import { ChatAnthropic } from \"@langchain/anthropic\";\n",
"import { mergeMessageRuns } from \"@langchain/core/messages\";\n",
"\n",
"const llm = new ChatAnthropic({ model: \"claude-3-sonnet-20240229\", temperature: 0 });\n",
"// Notice we don't pass in messages. This creates\n",
"// a RunnableLambda that takes messages as input\n",
"const merger = mergeMessageRuns();\n",
"const chain = merger.pipe(llm);\n",
"await chain.invoke(messages);"
]
},
{
"cell_type": "markdown",
"id": "72e90dce-693c-4842-9526-ce6460fe956b",
"metadata": {},
"source": [
"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",
"\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": 3,
"id": "460817a6-c327-429d-958e-181a8c46059c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[\n",
" SystemMessage {\n",
" lc_serializable: true,\n",
" lc_kwargs: {\n",
" content: \"you're a good assistant.\\nyou always respond with a joke.\",\n",
" name: undefined,\n",
" additional_kwargs: {},\n",
" response_metadata: {},\n",
" id: undefined\n",
" },\n",
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
" content: \"you're a good assistant.\\nyou always respond with a joke.\",\n",
" name: undefined,\n",
" additional_kwargs: {},\n",
" response_metadata: {},\n",
" id: undefined\n",
" },\n",
" HumanMessage {\n",
" lc_serializable: true,\n",
" lc_kwargs: {\n",
" content: [Array],\n",
" name: undefined,\n",
" additional_kwargs: {},\n",
" response_metadata: {},\n",
" id: undefined\n",
" },\n",
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
" content: [ [Object], [Object] ],\n",
" name: undefined,\n",
" additional_kwargs: {},\n",
" response_metadata: {},\n",
" id: undefined\n",
" },\n",
" AIMessage {\n",
" lc_serializable: true,\n",
" lc_kwargs: {\n",
" content: `Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn't have the same ring to it!\\n` +\n",
" \"Why, he's probably chasing after the last cup of coffee in the office!\",\n",
" name: undefined,\n",
" additional_kwargs: {},\n",
" response_metadata: {},\n",
" id: undefined,\n",
" tool_calls: [],\n",
" invalid_tool_calls: [],\n",
" usage_metadata: undefined\n",
" },\n",
" lc_namespace: [ 'langchain_core', 'messages' ],\n",
" content: `Well, I guess they thought \"WordRope\" and \"SentenceString\" just didn't have the same ring to it!\\n` +\n",
" \"Why, he's probably chasing after the last cup of coffee in the office!\",\n",
" name: undefined,\n",
" additional_kwargs: {},\n",
" response_metadata: {},\n",
" id: undefined,\n",
" tool_calls: [],\n",
" invalid_tool_calls: [],\n",
" usage_metadata: undefined\n",
" }\n",
"]\n"
]
}
],
"source": [
"await merger.invoke(messages)"
]
},
{
"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://api.js.langchain.com/functions/langchain_core_messages.mergeMessageRuns.html)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "TypeScript",
"language": "typescript",
"name": "tslab"
},
"language_info": {
"codemirror_mode": {
"mode": "typescript",
"name": "javascript",
"typescript": true
},
"file_extension": ".ts",
"mimetype": "text/typescript",
"name": "typescript",
"version": "3.7.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading
Loading