langchain-core 0.3.79__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of langchain-core might be problematic. Click here for more details.

Files changed (165) hide show
  1. langchain_core/__init__.py +1 -1
  2. langchain_core/_api/__init__.py +3 -4
  3. langchain_core/_api/beta_decorator.py +23 -26
  4. langchain_core/_api/deprecation.py +52 -65
  5. langchain_core/_api/path.py +3 -6
  6. langchain_core/_import_utils.py +3 -4
  7. langchain_core/agents.py +19 -19
  8. langchain_core/caches.py +53 -63
  9. langchain_core/callbacks/__init__.py +1 -8
  10. langchain_core/callbacks/base.py +323 -334
  11. langchain_core/callbacks/file.py +44 -44
  12. langchain_core/callbacks/manager.py +441 -507
  13. langchain_core/callbacks/stdout.py +29 -30
  14. langchain_core/callbacks/streaming_stdout.py +32 -32
  15. langchain_core/callbacks/usage.py +60 -57
  16. langchain_core/chat_history.py +48 -63
  17. langchain_core/document_loaders/base.py +23 -23
  18. langchain_core/document_loaders/langsmith.py +37 -37
  19. langchain_core/documents/__init__.py +0 -1
  20. langchain_core/documents/base.py +62 -65
  21. langchain_core/documents/compressor.py +4 -4
  22. langchain_core/documents/transformers.py +28 -29
  23. langchain_core/embeddings/fake.py +50 -54
  24. langchain_core/example_selectors/length_based.py +1 -1
  25. langchain_core/example_selectors/semantic_similarity.py +21 -25
  26. langchain_core/exceptions.py +10 -11
  27. langchain_core/globals.py +3 -151
  28. langchain_core/indexing/api.py +61 -66
  29. langchain_core/indexing/base.py +58 -58
  30. langchain_core/indexing/in_memory.py +3 -3
  31. langchain_core/language_models/__init__.py +14 -27
  32. langchain_core/language_models/_utils.py +270 -84
  33. langchain_core/language_models/base.py +55 -162
  34. langchain_core/language_models/chat_models.py +442 -402
  35. langchain_core/language_models/fake.py +11 -11
  36. langchain_core/language_models/fake_chat_models.py +61 -39
  37. langchain_core/language_models/llms.py +123 -231
  38. langchain_core/load/dump.py +4 -5
  39. langchain_core/load/load.py +18 -28
  40. langchain_core/load/mapping.py +2 -4
  41. langchain_core/load/serializable.py +39 -40
  42. langchain_core/messages/__init__.py +61 -22
  43. langchain_core/messages/ai.py +368 -163
  44. langchain_core/messages/base.py +214 -43
  45. langchain_core/messages/block_translators/__init__.py +111 -0
  46. langchain_core/messages/block_translators/anthropic.py +470 -0
  47. langchain_core/messages/block_translators/bedrock.py +94 -0
  48. langchain_core/messages/block_translators/bedrock_converse.py +297 -0
  49. langchain_core/messages/block_translators/google_genai.py +530 -0
  50. langchain_core/messages/block_translators/google_vertexai.py +21 -0
  51. langchain_core/messages/block_translators/groq.py +143 -0
  52. langchain_core/messages/block_translators/langchain_v0.py +301 -0
  53. langchain_core/messages/block_translators/openai.py +1010 -0
  54. langchain_core/messages/chat.py +2 -6
  55. langchain_core/messages/content.py +1423 -0
  56. langchain_core/messages/function.py +6 -10
  57. langchain_core/messages/human.py +41 -38
  58. langchain_core/messages/modifier.py +2 -2
  59. langchain_core/messages/system.py +38 -28
  60. langchain_core/messages/tool.py +96 -103
  61. langchain_core/messages/utils.py +478 -504
  62. langchain_core/output_parsers/__init__.py +1 -14
  63. langchain_core/output_parsers/base.py +58 -61
  64. langchain_core/output_parsers/json.py +7 -8
  65. langchain_core/output_parsers/list.py +5 -7
  66. langchain_core/output_parsers/openai_functions.py +49 -47
  67. langchain_core/output_parsers/openai_tools.py +14 -19
  68. langchain_core/output_parsers/pydantic.py +12 -13
  69. langchain_core/output_parsers/string.py +2 -2
  70. langchain_core/output_parsers/transform.py +15 -17
  71. langchain_core/output_parsers/xml.py +8 -10
  72. langchain_core/outputs/__init__.py +1 -1
  73. langchain_core/outputs/chat_generation.py +18 -18
  74. langchain_core/outputs/chat_result.py +1 -3
  75. langchain_core/outputs/generation.py +8 -8
  76. langchain_core/outputs/llm_result.py +10 -10
  77. langchain_core/prompt_values.py +12 -12
  78. langchain_core/prompts/__init__.py +3 -27
  79. langchain_core/prompts/base.py +45 -55
  80. langchain_core/prompts/chat.py +254 -313
  81. langchain_core/prompts/dict.py +5 -5
  82. langchain_core/prompts/few_shot.py +81 -88
  83. langchain_core/prompts/few_shot_with_templates.py +11 -13
  84. langchain_core/prompts/image.py +12 -14
  85. langchain_core/prompts/loading.py +6 -8
  86. langchain_core/prompts/message.py +3 -3
  87. langchain_core/prompts/prompt.py +24 -39
  88. langchain_core/prompts/string.py +4 -4
  89. langchain_core/prompts/structured.py +42 -50
  90. langchain_core/rate_limiters.py +51 -60
  91. langchain_core/retrievers.py +49 -190
  92. langchain_core/runnables/base.py +1484 -1709
  93. langchain_core/runnables/branch.py +45 -61
  94. langchain_core/runnables/config.py +80 -88
  95. langchain_core/runnables/configurable.py +117 -134
  96. langchain_core/runnables/fallbacks.py +83 -79
  97. langchain_core/runnables/graph.py +85 -95
  98. langchain_core/runnables/graph_ascii.py +27 -28
  99. langchain_core/runnables/graph_mermaid.py +38 -50
  100. langchain_core/runnables/graph_png.py +15 -16
  101. langchain_core/runnables/history.py +135 -148
  102. langchain_core/runnables/passthrough.py +124 -150
  103. langchain_core/runnables/retry.py +46 -51
  104. langchain_core/runnables/router.py +25 -30
  105. langchain_core/runnables/schema.py +79 -74
  106. langchain_core/runnables/utils.py +62 -68
  107. langchain_core/stores.py +81 -115
  108. langchain_core/structured_query.py +8 -8
  109. langchain_core/sys_info.py +27 -29
  110. langchain_core/tools/__init__.py +1 -14
  111. langchain_core/tools/base.py +179 -187
  112. langchain_core/tools/convert.py +131 -139
  113. langchain_core/tools/render.py +10 -10
  114. langchain_core/tools/retriever.py +11 -11
  115. langchain_core/tools/simple.py +19 -24
  116. langchain_core/tools/structured.py +30 -39
  117. langchain_core/tracers/__init__.py +1 -9
  118. langchain_core/tracers/base.py +97 -99
  119. langchain_core/tracers/context.py +29 -52
  120. langchain_core/tracers/core.py +50 -60
  121. langchain_core/tracers/evaluation.py +11 -11
  122. langchain_core/tracers/event_stream.py +115 -70
  123. langchain_core/tracers/langchain.py +21 -21
  124. langchain_core/tracers/log_stream.py +43 -43
  125. langchain_core/tracers/memory_stream.py +3 -3
  126. langchain_core/tracers/root_listeners.py +16 -16
  127. langchain_core/tracers/run_collector.py +2 -4
  128. langchain_core/tracers/schemas.py +0 -129
  129. langchain_core/tracers/stdout.py +3 -3
  130. langchain_core/utils/__init__.py +1 -4
  131. langchain_core/utils/_merge.py +46 -8
  132. langchain_core/utils/aiter.py +57 -61
  133. langchain_core/utils/env.py +9 -9
  134. langchain_core/utils/function_calling.py +89 -191
  135. langchain_core/utils/html.py +7 -8
  136. langchain_core/utils/input.py +6 -6
  137. langchain_core/utils/interactive_env.py +1 -1
  138. langchain_core/utils/iter.py +37 -42
  139. langchain_core/utils/json.py +4 -3
  140. langchain_core/utils/json_schema.py +8 -8
  141. langchain_core/utils/mustache.py +9 -11
  142. langchain_core/utils/pydantic.py +33 -35
  143. langchain_core/utils/strings.py +5 -5
  144. langchain_core/utils/usage.py +1 -1
  145. langchain_core/utils/utils.py +80 -54
  146. langchain_core/vectorstores/base.py +129 -164
  147. langchain_core/vectorstores/in_memory.py +99 -174
  148. langchain_core/vectorstores/utils.py +5 -5
  149. langchain_core/version.py +1 -1
  150. {langchain_core-0.3.79.dist-info → langchain_core-1.0.0.dist-info}/METADATA +28 -27
  151. langchain_core-1.0.0.dist-info/RECORD +172 -0
  152. {langchain_core-0.3.79.dist-info → langchain_core-1.0.0.dist-info}/WHEEL +1 -1
  153. langchain_core/beta/__init__.py +0 -1
  154. langchain_core/beta/runnables/__init__.py +0 -1
  155. langchain_core/beta/runnables/context.py +0 -447
  156. langchain_core/memory.py +0 -120
  157. langchain_core/messages/content_blocks.py +0 -176
  158. langchain_core/prompts/pipeline.py +0 -138
  159. langchain_core/pydantic_v1/__init__.py +0 -30
  160. langchain_core/pydantic_v1/dataclasses.py +0 -23
  161. langchain_core/pydantic_v1/main.py +0 -23
  162. langchain_core/tracers/langchain_v1.py +0 -31
  163. langchain_core/utils/loading.py +0 -35
  164. langchain_core-0.3.79.dist-info/RECORD +0 -174
  165. langchain_core-0.3.79.dist-info/entry_points.txt +0 -4
@@ -5,7 +5,6 @@ Some examples of what you can do with these functions include:
5
5
  * Convert messages to strings (serialization)
6
6
  * Convert messages from dicts to Message objects (deserialization)
7
7
  * Filter messages from a list of messages based on name, type or id etc.
8
-
9
8
  """
10
9
 
11
10
  from __future__ import annotations
@@ -15,16 +14,13 @@ import inspect
15
14
  import json
16
15
  import logging
17
16
  import math
18
- from collections.abc import Iterable, Sequence
17
+ from collections.abc import Callable, Iterable, Sequence
19
18
  from functools import partial
20
19
  from typing import (
21
20
  TYPE_CHECKING,
22
21
  Annotated,
23
22
  Any,
24
- Callable,
25
23
  Literal,
26
- Optional,
27
- Union,
28
24
  cast,
29
25
  overload,
30
26
  )
@@ -32,10 +28,15 @@ from typing import (
32
28
  from pydantic import Discriminator, Field, Tag
33
29
 
34
30
  from langchain_core.exceptions import ErrorCode, create_message
35
- from langchain_core.messages import convert_to_openai_data_block, is_data_content_block
36
31
  from langchain_core.messages.ai import AIMessage, AIMessageChunk
37
32
  from langchain_core.messages.base import BaseMessage, BaseMessageChunk
33
+ from langchain_core.messages.block_translators.openai import (
34
+ convert_to_openai_data_block,
35
+ )
38
36
  from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
37
+ from langchain_core.messages.content import (
38
+ is_data_content_block,
39
+ )
39
40
  from langchain_core.messages.function import FunctionMessage, FunctionMessageChunk
40
41
  from langchain_core.messages.human import HumanMessage, HumanMessageChunk
41
42
  from langchain_core.messages.modifier import RemoveMessage
@@ -71,22 +72,21 @@ def _get_type(v: Any) -> str:
71
72
 
72
73
 
73
74
  AnyMessage = Annotated[
74
- Union[
75
- Annotated[AIMessage, Tag(tag="ai")],
76
- Annotated[HumanMessage, Tag(tag="human")],
77
- Annotated[ChatMessage, Tag(tag="chat")],
78
- Annotated[SystemMessage, Tag(tag="system")],
79
- Annotated[FunctionMessage, Tag(tag="function")],
80
- Annotated[ToolMessage, Tag(tag="tool")],
81
- Annotated[AIMessageChunk, Tag(tag="AIMessageChunk")],
82
- Annotated[HumanMessageChunk, Tag(tag="HumanMessageChunk")],
83
- Annotated[ChatMessageChunk, Tag(tag="ChatMessageChunk")],
84
- Annotated[SystemMessageChunk, Tag(tag="SystemMessageChunk")],
85
- Annotated[FunctionMessageChunk, Tag(tag="FunctionMessageChunk")],
86
- Annotated[ToolMessageChunk, Tag(tag="ToolMessageChunk")],
87
- ],
75
+ Annotated[AIMessage, Tag(tag="ai")]
76
+ | Annotated[HumanMessage, Tag(tag="human")]
77
+ | Annotated[ChatMessage, Tag(tag="chat")]
78
+ | Annotated[SystemMessage, Tag(tag="system")]
79
+ | Annotated[FunctionMessage, Tag(tag="function")]
80
+ | Annotated[ToolMessage, Tag(tag="tool")]
81
+ | Annotated[AIMessageChunk, Tag(tag="AIMessageChunk")]
82
+ | Annotated[HumanMessageChunk, Tag(tag="HumanMessageChunk")]
83
+ | Annotated[ChatMessageChunk, Tag(tag="ChatMessageChunk")]
84
+ | Annotated[SystemMessageChunk, Tag(tag="SystemMessageChunk")]
85
+ | Annotated[FunctionMessageChunk, Tag(tag="FunctionMessageChunk")]
86
+ | Annotated[ToolMessageChunk, Tag(tag="ToolMessageChunk")],
88
87
  Field(discriminator=Discriminator(_get_type)),
89
88
  ]
89
+ """"A type representing any defined `Message` or `MessageChunk` type."""
90
90
 
91
91
 
92
92
  def get_buffer_string(
@@ -96,10 +96,8 @@ def get_buffer_string(
96
96
 
97
97
  Args:
98
98
  messages: Messages to be converted to strings.
99
- human_prefix: The prefix to prepend to contents of ``HumanMessage``s.
100
- Default is ``'Human'``.
101
- ai_prefix: The prefix to prepend to contents of ``AIMessage``. Default is
102
- ``'AI'``.
99
+ human_prefix: The prefix to prepend to contents of `HumanMessage`s.
100
+ ai_prefix: The prefix to prepend to contents of `AIMessage`.
103
101
 
104
102
  Returns:
105
103
  A single string concatenation of all input messages.
@@ -108,17 +106,16 @@ def get_buffer_string(
108
106
  ValueError: If an unsupported message type is encountered.
109
107
 
110
108
  Example:
111
- .. code-block:: python
112
-
113
- from langchain_core import AIMessage, HumanMessage
114
-
115
- messages = [
116
- HumanMessage(content="Hi, how are you?"),
117
- AIMessage(content="Good, how are you?"),
118
- ]
119
- get_buffer_string(messages)
120
- # -> "Human: Hi, how are you?\nAI: Good, how are you?"
121
-
109
+ ```python
110
+ from langchain_core import AIMessage, HumanMessage
111
+
112
+ messages = [
113
+ HumanMessage(content="Hi, how are you?"),
114
+ AIMessage(content="Good, how are you?"),
115
+ ]
116
+ get_buffer_string(messages)
117
+ # -> "Human: Hi, how are you?\nAI: Good, how are you?"
118
+ ```
122
119
  """
123
120
  string_messages = []
124
121
  for m in messages:
@@ -137,7 +134,7 @@ def get_buffer_string(
137
134
  else:
138
135
  msg = f"Got unsupported message type: {m}"
139
136
  raise ValueError(msg) # noqa: TRY004
140
- message = f"{role}: {m.text()}"
137
+ message = f"{role}: {m.text}"
141
138
  if isinstance(m, AIMessage) and "function_call" in m.additional_kwargs:
142
139
  message += f"{m.additional_kwargs['function_call']}"
143
140
  string_messages.append(message)
@@ -178,7 +175,7 @@ def _message_from_dict(message: dict) -> BaseMessage:
178
175
 
179
176
 
180
177
  def messages_from_dict(messages: Sequence[dict]) -> list[BaseMessage]:
181
- """Convert a sequence of messages from dicts to ``Message`` objects.
178
+ """Convert a sequence of messages from dicts to `Message` objects.
182
179
 
183
180
  Args:
184
181
  messages: Sequence of messages (as dicts) to convert.
@@ -191,7 +188,7 @@ def messages_from_dict(messages: Sequence[dict]) -> list[BaseMessage]:
191
188
 
192
189
 
193
190
  def message_chunk_to_message(chunk: BaseMessage) -> BaseMessage:
194
- """Convert a message chunk to a ``Message``.
191
+ """Convert a message chunk to a `Message`.
195
192
 
196
193
  Args:
197
194
  chunk: Message chunk to convert.
@@ -204,44 +201,45 @@ def message_chunk_to_message(chunk: BaseMessage) -> BaseMessage:
204
201
  # chunk classes always have the equivalent non-chunk class as their first parent
205
202
  ignore_keys = ["type"]
206
203
  if isinstance(chunk, AIMessageChunk):
207
- ignore_keys.append("tool_call_chunks")
204
+ ignore_keys.extend(["tool_call_chunks", "chunk_position"])
208
205
  return chunk.__class__.__mro__[1](
209
206
  **{k: v for k, v in chunk.__dict__.items() if k not in ignore_keys}
210
207
  )
211
208
 
212
209
 
213
- MessageLikeRepresentation = Union[
214
- BaseMessage, list[str], tuple[str, str], str, dict[str, Any]
215
- ]
210
+ MessageLikeRepresentation = (
211
+ BaseMessage | list[str] | tuple[str, str] | str | dict[str, Any]
212
+ )
213
+ """A type representing the various ways a message can be represented."""
216
214
 
217
215
 
218
216
  def _create_message_from_message_type(
219
217
  message_type: str,
220
218
  content: str,
221
- name: Optional[str] = None,
222
- tool_call_id: Optional[str] = None,
223
- tool_calls: Optional[list[dict[str, Any]]] = None,
224
- id: Optional[str] = None,
219
+ name: str | None = None,
220
+ tool_call_id: str | None = None,
221
+ tool_calls: list[dict[str, Any]] | None = None,
222
+ id: str | None = None,
225
223
  **additional_kwargs: Any,
226
224
  ) -> BaseMessage:
227
- """Create a message from a ``Message`` type and content string.
225
+ """Create a message from a `Message` type and content string.
228
226
 
229
227
  Args:
230
- message_type: (str) the type of the message (e.g., ``'human'``, ``'ai'``, etc.).
228
+ message_type: (str) the type of the message (e.g., `'human'`, `'ai'`, etc.).
231
229
  content: (str) the content string.
232
- name: (str) the name of the message. Default is None.
233
- tool_call_id: (str) the tool call id. Default is None.
234
- tool_calls: (list[dict[str, Any]]) the tool calls. Default is None.
235
- id: (str) the id of the message. Default is None.
230
+ name: (str) the name of the message.
231
+ tool_call_id: (str) the tool call id.
232
+ tool_calls: (list[dict[str, Any]]) the tool calls.
233
+ id: (str) the id of the message.
236
234
  additional_kwargs: (dict[str, Any]) additional keyword arguments.
237
235
 
238
236
  Returns:
239
237
  a message of the appropriate type.
240
238
 
241
239
  Raises:
242
- ValueError: if the message type is not one of ``'human'``, ``'user'``, ``'ai'``,
243
- ``'assistant'``, ``'function'``, ``'tool'``, ``'system'``, or
244
- ``'developer'``.
240
+ ValueError: if the message type is not one of `'human'`, `'user'`, `'ai'`,
241
+ `'assistant'`, `'function'`, `'tool'`, `'system'`, or
242
+ `'developer'`.
245
243
  """
246
244
  kwargs: dict[str, Any] = {}
247
245
  if name is not None:
@@ -307,21 +305,21 @@ def _create_message_from_message_type(
307
305
 
308
306
 
309
307
  def _convert_to_message(message: MessageLikeRepresentation) -> BaseMessage:
310
- """Instantiate a ``Message`` from a variety of message formats.
308
+ """Instantiate a `Message` from a variety of message formats.
311
309
 
312
310
  The message format can be one of the following:
313
311
 
314
- - ``BaseMessagePromptTemplate``
315
- - ``BaseMessage``
316
- - 2-tuple of (role string, template); e.g., (``'human'``, ``'{user_input}'``)
312
+ - `BaseMessagePromptTemplate`
313
+ - `BaseMessage`
314
+ - 2-tuple of (role string, template); e.g., (`'human'`, `'{user_input}'`)
317
315
  - dict: a message dict with role and content keys
318
- - string: shorthand for (``'human'``, template); e.g., ``'{user_input}'``
316
+ - string: shorthand for (`'human'`, template); e.g., `'{user_input}'`
319
317
 
320
318
  Args:
321
319
  message: a representation of a message in one of the supported formats.
322
320
 
323
321
  Returns:
324
- an instance of a message or a message template.
322
+ An instance of a message or a message template.
325
323
 
326
324
  Raises:
327
325
  NotImplementedError: if the message type is not supported.
@@ -363,7 +361,7 @@ def _convert_to_message(message: MessageLikeRepresentation) -> BaseMessage:
363
361
 
364
362
 
365
363
  def convert_to_messages(
366
- messages: Union[Iterable[MessageLikeRepresentation], PromptValue],
364
+ messages: Iterable[MessageLikeRepresentation] | PromptValue,
367
365
  ) -> list[BaseMessage]:
368
366
  """Convert a sequence of messages to a list of messages.
369
367
 
@@ -394,12 +392,12 @@ def _runnable_support(func: Callable) -> Callable:
394
392
  ) -> list[BaseMessage]: ...
395
393
 
396
394
  def wrapped(
397
- messages: Union[Sequence[MessageLikeRepresentation], None] = None,
395
+ messages: Sequence[MessageLikeRepresentation] | None = None,
398
396
  **kwargs: Any,
399
- ) -> Union[
400
- list[BaseMessage],
401
- Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]],
402
- ]:
397
+ ) -> (
398
+ list[BaseMessage]
399
+ | Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]]
400
+ ):
403
401
  # Import locally to prevent circular import.
404
402
  from langchain_core.runnables.base import RunnableLambda # noqa: PLC0415
405
403
 
@@ -413,89 +411,88 @@ def _runnable_support(func: Callable) -> Callable:
413
411
 
414
412
  @_runnable_support
415
413
  def filter_messages(
416
- messages: Union[Iterable[MessageLikeRepresentation], PromptValue],
414
+ messages: Iterable[MessageLikeRepresentation] | PromptValue,
417
415
  *,
418
- include_names: Optional[Sequence[str]] = None,
419
- exclude_names: Optional[Sequence[str]] = None,
420
- include_types: Optional[Sequence[Union[str, type[BaseMessage]]]] = None,
421
- exclude_types: Optional[Sequence[Union[str, type[BaseMessage]]]] = None,
422
- include_ids: Optional[Sequence[str]] = None,
423
- exclude_ids: Optional[Sequence[str]] = None,
424
- exclude_tool_calls: Optional[Sequence[str] | bool] = None,
416
+ include_names: Sequence[str] | None = None,
417
+ exclude_names: Sequence[str] | None = None,
418
+ include_types: Sequence[str | type[BaseMessage]] | None = None,
419
+ exclude_types: Sequence[str | type[BaseMessage]] | None = None,
420
+ include_ids: Sequence[str] | None = None,
421
+ exclude_ids: Sequence[str] | None = None,
422
+ exclude_tool_calls: Sequence[str] | bool | None = None,
425
423
  ) -> list[BaseMessage]:
426
- """Filter messages based on ``name``, ``type`` or ``id``.
424
+ """Filter messages based on `name`, `type` or `id`.
427
425
 
428
426
  Args:
429
427
  messages: Sequence Message-like objects to filter.
430
- include_names: Message names to include. Default is None.
431
- exclude_names: Messages names to exclude. Default is None.
428
+ include_names: Message names to include.
429
+ exclude_names: Messages names to exclude.
432
430
  include_types: Message types to include. Can be specified as string names
433
- (e.g. ``'system'``, ``'human'``, ``'ai'``, ...) or as ``BaseMessage``
434
- classes (e.g. ``SystemMessage``, ``HumanMessage``, ``AIMessage``, ...).
435
- Default is None.
431
+ (e.g. `'system'`, `'human'`, `'ai'`, ...) or as `BaseMessage`
432
+ classes (e.g. `SystemMessage`, `HumanMessage`, `AIMessage`, ...).
433
+
436
434
  exclude_types: Message types to exclude. Can be specified as string names
437
- (e.g. ``'system'``, ``'human'``, ``'ai'``, ...) or as ``BaseMessage``
438
- classes (e.g. ``SystemMessage``, ``HumanMessage``, ``AIMessage``, ...).
439
- Default is None.
440
- include_ids: Message IDs to include. Default is None.
441
- exclude_ids: Message IDs to exclude. Default is None.
442
- exclude_tool_calls: Tool call IDs to exclude. Default is None.
435
+ (e.g. `'system'`, `'human'`, `'ai'`, ...) or as `BaseMessage`
436
+ classes (e.g. `SystemMessage`, `HumanMessage`, `AIMessage`, ...).
437
+
438
+ include_ids: Message IDs to include.
439
+ exclude_ids: Message IDs to exclude.
440
+ exclude_tool_calls: Tool call IDs to exclude.
443
441
  Can be one of the following:
444
- - ``True``: all ``AIMessage``s with tool calls and all
445
- ``ToolMessage``s will be excluded.
442
+ - `True`: all `AIMessage`s with tool calls and all
443
+ `ToolMessage` objects will be excluded.
446
444
  - a sequence of tool call IDs to exclude:
447
- - ``ToolMessage``s with the corresponding tool call ID will be
448
- excluded.
449
- - The ``tool_calls`` in the AIMessage will be updated to exclude
450
- matching tool calls. If all ``tool_calls`` are filtered from an
451
- AIMessage, the whole message is excluded.
445
+ - `ToolMessage` objects with the corresponding tool call ID will be
446
+ excluded.
447
+ - The `tool_calls` in the AIMessage will be updated to exclude
448
+ matching tool calls. If all `tool_calls` are filtered from an
449
+ AIMessage, the whole message is excluded.
452
450
 
453
451
  Returns:
454
- A list of Messages that meets at least one of the ``incl_*`` conditions and none
455
- of the ``excl_*`` conditions. If not ``incl_*`` conditions are specified then
452
+ A list of Messages that meets at least one of the `incl_*` conditions and none
453
+ of the `excl_*` conditions. If not `incl_*` conditions are specified then
456
454
  anything that is not explicitly excluded will be included.
457
455
 
458
456
  Raises:
459
- ValueError if two incompatible arguments are provided.
457
+ ValueError: If two incompatible arguments are provided.
460
458
 
461
459
  Example:
462
- .. code-block:: python
463
-
464
- from langchain_core.messages import (
465
- filter_messages,
466
- AIMessage,
467
- HumanMessage,
468
- SystemMessage,
469
- )
470
-
471
- messages = [
472
- SystemMessage("you're a good assistant."),
473
- HumanMessage("what's your name", id="foo", name="example_user"),
474
- AIMessage("steve-o", id="bar", name="example_assistant"),
475
- HumanMessage(
476
- "what's your favorite color",
477
- id="baz",
478
- ),
479
- AIMessage(
480
- "silicon blue",
481
- id="blah",
482
- ),
483
- ]
484
-
485
- filter_messages(
486
- messages,
487
- incl_names=("example_user", "example_assistant"),
488
- incl_types=("system",),
489
- excl_ids=("bar",),
490
- )
491
-
492
- .. code-block:: python
493
-
494
- [
495
- SystemMessage("you're a good assistant."),
496
- HumanMessage("what's your name", id="foo", name="example_user"),
497
- ]
460
+ ```python
461
+ from langchain_core.messages import (
462
+ filter_messages,
463
+ AIMessage,
464
+ HumanMessage,
465
+ SystemMessage,
466
+ )
498
467
 
468
+ messages = [
469
+ SystemMessage("you're a good assistant."),
470
+ HumanMessage("what's your name", id="foo", name="example_user"),
471
+ AIMessage("steve-o", id="bar", name="example_assistant"),
472
+ HumanMessage(
473
+ "what's your favorite color",
474
+ id="baz",
475
+ ),
476
+ AIMessage(
477
+ "silicon blue",
478
+ id="blah",
479
+ ),
480
+ ]
481
+
482
+ filter_messages(
483
+ messages,
484
+ incl_names=("example_user", "example_assistant"),
485
+ incl_types=("system",),
486
+ excl_ids=("bar",),
487
+ )
488
+ ```
489
+
490
+ ```python
491
+ [
492
+ SystemMessage("you're a good assistant."),
493
+ HumanMessage("what's your name", id="foo", name="example_user"),
494
+ ]
495
+ ```
499
496
  """
500
497
  messages = convert_to_messages(messages)
501
498
  filtered: list[BaseMessage] = []
@@ -558,20 +555,19 @@ def filter_messages(
558
555
 
559
556
  @_runnable_support
560
557
  def merge_message_runs(
561
- messages: Union[Iterable[MessageLikeRepresentation], PromptValue],
558
+ messages: Iterable[MessageLikeRepresentation] | PromptValue,
562
559
  *,
563
560
  chunk_separator: str = "\n",
564
561
  ) -> list[BaseMessage]:
565
562
  r"""Merge consecutive Messages of the same type.
566
563
 
567
- .. note::
568
- ToolMessages are not merged, as each has a distinct tool call id that can't be
569
- merged.
564
+ !!! note
565
+ `ToolMessage` objects are not merged, as each has a distinct tool call id that
566
+ can't be merged.
570
567
 
571
568
  Args:
572
569
  messages: Sequence Message-like objects to merge.
573
570
  chunk_separator: Specify the string to be inserted between message chunks.
574
- Default is ``'\n'``.
575
571
 
576
572
  Returns:
577
573
  list of BaseMessages with consecutive runs of message types merged into single
@@ -579,87 +575,86 @@ def merge_message_runs(
579
575
  the merged content is a concatenation of the two strings with a new-line
580
576
  separator.
581
577
  The separator inserted between message chunks can be controlled by specifying
582
- any string with ``chunk_separator``. If at least one of the messages has a list
578
+ any string with `chunk_separator`. If at least one of the messages has a list
583
579
  of content blocks, the merged content is a list of content blocks.
584
580
 
585
581
  Example:
582
+ ```python
583
+ from langchain_core.messages import (
584
+ merge_message_runs,
585
+ AIMessage,
586
+ HumanMessage,
587
+ SystemMessage,
588
+ ToolCall,
589
+ )
586
590
 
587
- .. code-block:: python
588
-
589
- from langchain_core.messages import (
590
- merge_message_runs,
591
- AIMessage,
592
- HumanMessage,
593
- SystemMessage,
594
- ToolCall,
595
- )
596
-
597
- messages = [
598
- SystemMessage("you're a good assistant."),
599
- HumanMessage(
600
- "what's your favorite color",
601
- id="foo",
602
- ),
603
- HumanMessage(
604
- "wait your favorite food",
605
- id="bar",
606
- ),
607
- AIMessage(
591
+ messages = [
592
+ SystemMessage("you're a good assistant."),
593
+ HumanMessage(
594
+ "what's your favorite color",
595
+ id="foo",
596
+ ),
597
+ HumanMessage(
598
+ "wait your favorite food",
599
+ id="bar",
600
+ ),
601
+ AIMessage(
602
+ "my favorite colo",
603
+ tool_calls=[
604
+ ToolCall(
605
+ name="blah_tool", args={"x": 2}, id="123", type="tool_call"
606
+ )
607
+ ],
608
+ id="baz",
609
+ ),
610
+ AIMessage(
611
+ [{"type": "text", "text": "my favorite dish is lasagna"}],
612
+ tool_calls=[
613
+ ToolCall(
614
+ name="blah_tool",
615
+ args={"x": -10},
616
+ id="456",
617
+ type="tool_call",
618
+ )
619
+ ],
620
+ id="blur",
621
+ ),
622
+ ]
623
+
624
+ merge_message_runs(messages)
625
+ ```
626
+
627
+ ```python
628
+ [
629
+ SystemMessage("you're a good assistant."),
630
+ HumanMessage(
631
+ "what's your favorite color\\n"
632
+ "wait your favorite food", id="foo",
633
+ ),
634
+ AIMessage(
635
+ [
608
636
  "my favorite colo",
609
- tool_calls=[
610
- ToolCall(
611
- name="blah_tool", args={"x": 2}, id="123", type="tool_call"
612
- )
613
- ],
614
- id="baz",
615
- ),
616
- AIMessage(
617
- [{"type": "text", "text": "my favorite dish is lasagna"}],
618
- tool_calls=[
619
- ToolCall(
620
- name="blah_tool",
621
- args={"x": -10},
622
- id="456",
623
- type="tool_call",
624
- )
625
- ],
626
- id="blur",
627
- ),
628
- ]
629
-
630
- merge_message_runs(messages)
631
-
632
- .. code-block:: python
633
-
634
- [
635
- SystemMessage("you're a good assistant."),
636
- HumanMessage(
637
- "what's your favorite color\\n"
638
- "wait your favorite food", id="foo",
639
- ),
640
- AIMessage(
641
- [
642
- "my favorite colo",
643
- {"type": "text", "text": "my favorite dish is lasagna"}
644
- ],
645
- tool_calls=[
646
- ToolCall({
647
- "name": "blah_tool",
648
- "args": {"x": 2},
649
- "id": "123",
650
- "type": "tool_call"
651
- }),
652
- ToolCall({
653
- "name": "blah_tool",
654
- "args": {"x": -10},
655
- "id": "456",
656
- "type": "tool_call"
657
- })
658
- ]
659
- id="baz"
660
- ),
661
- ]
637
+ {"type": "text", "text": "my favorite dish is lasagna"}
638
+ ],
639
+ tool_calls=[
640
+ ToolCall({
641
+ "name": "blah_tool",
642
+ "args": {"x": 2},
643
+ "id": "123",
644
+ "type": "tool_call"
645
+ }),
646
+ ToolCall({
647
+ "name": "blah_tool",
648
+ "args": {"x": -10},
649
+ "id": "456",
650
+ "type": "tool_call"
651
+ })
652
+ ]
653
+ id="baz"
654
+ ),
655
+ ]
662
656
 
657
+ ```
663
658
  """
664
659
  if not messages:
665
660
  return []
@@ -691,174 +686,161 @@ def merge_message_runs(
691
686
  # init not at runtime.
692
687
  @_runnable_support
693
688
  def trim_messages(
694
- messages: Union[Iterable[MessageLikeRepresentation], PromptValue],
689
+ messages: Iterable[MessageLikeRepresentation] | PromptValue,
695
690
  *,
696
691
  max_tokens: int,
697
- token_counter: Union[
698
- Callable[[list[BaseMessage]], int],
699
- Callable[[BaseMessage], int],
700
- BaseLanguageModel,
701
- ],
692
+ token_counter: Callable[[list[BaseMessage]], int]
693
+ | Callable[[BaseMessage], int]
694
+ | BaseLanguageModel,
702
695
  strategy: Literal["first", "last"] = "last",
703
696
  allow_partial: bool = False,
704
- end_on: Optional[
705
- Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
706
- ] = None,
707
- start_on: Optional[
708
- Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
709
- ] = None,
697
+ end_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
698
+ start_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
710
699
  include_system: bool = False,
711
- text_splitter: Optional[Union[Callable[[str], list[str]], TextSplitter]] = None,
700
+ text_splitter: Callable[[str], list[str]] | TextSplitter | None = None,
712
701
  ) -> list[BaseMessage]:
713
702
  r"""Trim messages to be below a token count.
714
703
 
715
- ``trim_messages`` can be used to reduce the size of a chat history to a specified
716
- token count or specified message count.
704
+ `trim_messages` can be used to reduce the size of a chat history to a specified
705
+ token or message count.
717
706
 
718
707
  In either case, if passing the trimmed chat history back into a chat model
719
708
  directly, the resulting chat history should usually satisfy the following
720
709
  properties:
721
710
 
722
711
  1. The resulting chat history should be valid. Most chat models expect that chat
723
- history starts with either (1) a ``HumanMessage`` or (2) a ``SystemMessage``
724
- followed by a ``HumanMessage``. To achieve this, set ``start_on='human'``.
725
- In addition, generally a ``ToolMessage`` can only appear after an ``AIMessage``
726
- that involved a tool call.
727
- Please see the following link for more information about messages:
728
- https://python.langchain.com/docs/concepts/#messages
712
+ history starts with either (1) a `HumanMessage` or (2) a `SystemMessage`
713
+ followed by a `HumanMessage`. To achieve this, set `start_on='human'`.
714
+ In addition, generally a `ToolMessage` can only appear after an `AIMessage`
715
+ that involved a tool call.
729
716
  2. It includes recent messages and drops old messages in the chat history.
730
- To achieve this set the ``strategy='last'``.
731
- 3. Usually, the new chat history should include the ``SystemMessage`` if it
732
- was present in the original chat history since the ``SystemMessage`` includes
733
- special instructions to the chat model. The ``SystemMessage`` is almost always
734
- the first message in the history if present. To achieve this set the
735
- ``include_system=True``.
736
-
737
- .. note::
738
- The examples below show how to configure ``trim_messages`` to achieve a behavior
717
+ To achieve this set the `strategy='last'`.
718
+ 3. Usually, the new chat history should include the `SystemMessage` if it
719
+ was present in the original chat history since the `SystemMessage` includes
720
+ special instructions to the chat model. The `SystemMessage` is almost always
721
+ the first message in the history if present. To achieve this set the
722
+ `include_system=True`.
723
+
724
+ !!! note
725
+ The examples below show how to configure `trim_messages` to achieve a behavior
739
726
  consistent with the above properties.
740
727
 
741
728
  Args:
742
729
  messages: Sequence of Message-like objects to trim.
743
730
  max_tokens: Max token count of trimmed messages.
744
- token_counter: Function or llm for counting tokens in a ``BaseMessage`` or a
745
- list of ``BaseMessage``. If a ``BaseLanguageModel`` is passed in then
746
- ``BaseLanguageModel.get_num_tokens_from_messages()`` will be used.
747
- Set to ``len`` to count the number of **messages** in the chat history.
731
+ token_counter: Function or llm for counting tokens in a `BaseMessage` or a
732
+ list of `BaseMessage`. If a `BaseLanguageModel` is passed in then
733
+ `BaseLanguageModel.get_num_tokens_from_messages()` will be used.
734
+ Set to `len` to count the number of **messages** in the chat history.
748
735
 
749
- .. note::
750
- Use ``count_tokens_approximately`` to get fast, approximate token
736
+ !!! note
737
+ Use `count_tokens_approximately` to get fast, approximate token
751
738
  counts.
752
- This is recommended for using ``trim_messages`` on the hot path, where
739
+ This is recommended for using `trim_messages` on the hot path, where
753
740
  exact token counting is not necessary.
754
741
 
755
742
  strategy: Strategy for trimming.
756
- - ``'first'``: Keep the first ``<= n_count`` tokens of the messages.
757
- - ``'last'``: Keep the last ``<= n_count`` tokens of the messages.
758
- Default is ``'last'``.
743
+ - `'first'`: Keep the first `<= n_count` tokens of the messages.
744
+ - `'last'`: Keep the last `<= n_count` tokens of the messages.
759
745
  allow_partial: Whether to split a message if only part of the message can be
760
- included. If ``strategy='last'`` then the last partial contents of a message
761
- are included. If ``strategy='first'`` then the first partial contents of a
746
+ included. If `strategy='last'` then the last partial contents of a message
747
+ are included. If `strategy='first'` then the first partial contents of a
762
748
  message are included.
763
- Default is False.
764
749
  end_on: The message type to end on. If specified then every message after the
765
- last occurrence of this type is ignored. If ``strategy='last'`` then this
766
- is done before we attempt to get the last ``max_tokens``. If
767
- ``strategy='first'`` then this is done after we get the first
768
- ``max_tokens``. Can be specified as string names (e.g. ``'system'``,
769
- ``'human'``, ``'ai'``, ...) or as ``BaseMessage`` classes (e.g.
770
- ``SystemMessage``, ``HumanMessage``, ``AIMessage``, ...). Can be a single
750
+ last occurrence of this type is ignored. If `strategy='last'` then this
751
+ is done before we attempt to get the last `max_tokens`. If
752
+ `strategy='first'` then this is done after we get the first
753
+ `max_tokens`. Can be specified as string names (e.g. `'system'`,
754
+ `'human'`, `'ai'`, ...) or as `BaseMessage` classes (e.g.
755
+ `SystemMessage`, `HumanMessage`, `AIMessage`, ...). Can be a single
771
756
  type or a list of types.
772
- Default is None.
757
+
773
758
  start_on: The message type to start on. Should only be specified if
774
- ``strategy='last'``. If specified then every message before
759
+ `strategy='last'`. If specified then every message before
775
760
  the first occurrence of this type is ignored. This is done after we trim
776
- the initial messages to the last ``max_tokens``. Does not
777
- apply to a ``SystemMessage`` at index 0 if ``include_system=True``. Can be
778
- specified as string names (e.g. ``'system'``, ``'human'``, ``'ai'``, ...) or
779
- as ``BaseMessage`` classes (e.g. ``SystemMessage``, ``HumanMessage``,
780
- ``AIMessage``, ...). Can be a single type or a list of types.
781
- Default is None.
782
- include_system: Whether to keep the SystemMessage if there is one at index 0.
783
- Should only be specified if ``strategy="last"``.
784
- Default is False.
785
- text_splitter: Function or ``langchain_text_splitters.TextSplitter`` for
761
+ the initial messages to the last `max_tokens`. Does not
762
+ apply to a `SystemMessage` at index 0 if `include_system=True`. Can be
763
+ specified as string names (e.g. `'system'`, `'human'`, `'ai'`, ...) or
764
+ as `BaseMessage` classes (e.g. `SystemMessage`, `HumanMessage`,
765
+ `AIMessage`, ...). Can be a single type or a list of types.
766
+
767
+ include_system: Whether to keep the `SystemMessage` if there is one at index
768
+ `0`. Should only be specified if `strategy="last"`.
769
+ text_splitter: Function or `langchain_text_splitters.TextSplitter` for
786
770
  splitting the string contents of a message. Only used if
787
- ``allow_partial=True``. If ``strategy='last'`` then the last split tokens
788
- from a partial message will be included. if ``strategy='first'`` then the
771
+ `allow_partial=True`. If `strategy='last'` then the last split tokens
772
+ from a partial message will be included. if `strategy='first'` then the
789
773
  first split tokens from a partial message will be included. Token splitter
790
774
  assumes that separators are kept, so that split contents can be directly
791
775
  concatenated to recreate the original text. Defaults to splitting on
792
776
  newlines.
793
777
 
794
778
  Returns:
795
- list of trimmed ``BaseMessage``.
779
+ List of trimmed `BaseMessage`.
796
780
 
797
781
  Raises:
798
782
  ValueError: if two incompatible arguments are specified or an unrecognized
799
- ``strategy`` is specified.
783
+ `strategy` is specified.
800
784
 
801
785
  Example:
802
- Trim chat history based on token count, keeping the ``SystemMessage`` if
803
- present, and ensuring that the chat history starts with a ``HumanMessage`` (
804
- or a ``SystemMessage`` followed by a ``HumanMessage``).
805
-
806
- .. code-block:: python
807
-
808
- from langchain_core.messages import (
809
- AIMessage,
810
- HumanMessage,
811
- BaseMessage,
812
- SystemMessage,
813
- trim_messages,
814
- )
815
-
816
- messages = [
817
- SystemMessage(
818
- "you're a good assistant, you always respond with a joke."
819
- ),
820
- HumanMessage("i wonder why it's called langchain"),
821
- AIMessage(
822
- 'Well, I guess they thought "WordRope" and "SentenceString" just '
823
- "didn't have the same ring to it!"
824
- ),
825
- HumanMessage("and who is harrison chasing anyways"),
826
- AIMessage(
827
- "Hmmm let me think.\n\nWhy, he's probably chasing after the last "
828
- "cup of coffee in the office!"
829
- ),
830
- HumanMessage("what do you call a speechless parrot"),
831
- ]
832
-
833
-
834
- trim_messages(
835
- messages,
836
- max_tokens=45,
837
- strategy="last",
838
- token_counter=ChatOpenAI(model="gpt-4o"),
839
- # Most chat models expect that chat history starts with either:
840
- # (1) a HumanMessage or
841
- # (2) a SystemMessage followed by a HumanMessage
842
- start_on="human",
843
- # Usually, we want to keep the SystemMessage
844
- # if it's present in the original history.
845
- # The SystemMessage has special instructions for the model.
846
- include_system=True,
847
- allow_partial=False,
848
- )
786
+ Trim chat history based on token count, keeping the `SystemMessage` if
787
+ present, and ensuring that the chat history starts with a `HumanMessage` (
788
+ or a `SystemMessage` followed by a `HumanMessage`).
789
+
790
+ ```python
791
+ from langchain_core.messages import (
792
+ AIMessage,
793
+ HumanMessage,
794
+ BaseMessage,
795
+ SystemMessage,
796
+ trim_messages,
797
+ )
849
798
 
850
- .. code-block:: python
799
+ messages = [
800
+ SystemMessage("you're a good assistant, you always respond with a joke."),
801
+ HumanMessage("i wonder why it's called langchain"),
802
+ AIMessage(
803
+ 'Well, I guess they thought "WordRope" and "SentenceString" just '
804
+ "didn't have the same ring to it!"
805
+ ),
806
+ HumanMessage("and who is harrison chasing anyways"),
807
+ AIMessage(
808
+ "Hmmm let me think.\n\nWhy, he's probably chasing after the last "
809
+ "cup of coffee in the office!"
810
+ ),
811
+ HumanMessage("what do you call a speechless parrot"),
812
+ ]
813
+
814
+
815
+ trim_messages(
816
+ messages,
817
+ max_tokens=45,
818
+ strategy="last",
819
+ token_counter=ChatOpenAI(model="gpt-4o"),
820
+ # Most chat models expect that chat history starts with either:
821
+ # (1) a HumanMessage or
822
+ # (2) a SystemMessage followed by a HumanMessage
823
+ start_on="human",
824
+ # Usually, we want to keep the SystemMessage
825
+ # if it's present in the original history.
826
+ # The SystemMessage has special instructions for the model.
827
+ include_system=True,
828
+ allow_partial=False,
829
+ )
830
+ ```
851
831
 
852
- [
853
- SystemMessage(
854
- content="you're a good assistant, you always respond with a joke."
855
- ),
856
- HumanMessage(content="what do you call a speechless parrot"),
857
- ]
832
+ ```python
833
+ [
834
+ SystemMessage(
835
+ content="you're a good assistant, you always respond with a joke."
836
+ ),
837
+ HumanMessage(content="what do you call a speechless parrot"),
838
+ ]
839
+ ```
858
840
 
859
- Trim chat history based on the message count, keeping the ``SystemMessage`` if
860
- present, and ensuring that the chat history starts with a ``HumanMessage`` (
861
- or a ``SystemMessage`` followed by a ``HumanMessage``).
841
+ Trim chat history based on the message count, keeping the `SystemMessage` if
842
+ present, and ensuring that the chat history starts with a `HumanMessage` (
843
+ or a `SystemMessage` followed by a `HumanMessage`).
862
844
 
863
845
  trim_messages(
864
846
  messages,
@@ -880,100 +862,95 @@ def trim_messages(
880
862
  allow_partial=False,
881
863
  )
882
864
 
883
- .. code-block:: python
884
-
885
- [
886
- SystemMessage(
887
- content="you're a good assistant, you always respond with a joke."
888
- ),
889
- HumanMessage(content="and who is harrison chasing anyways"),
890
- AIMessage(
891
- content="Hmmm let me think.\n\nWhy, he's probably chasing after "
892
- "the last cup of coffee in the office!"
893
- ),
894
- HumanMessage(content="what do you call a speechless parrot"),
895
- ]
896
-
897
-
865
+ ```python
866
+ [
867
+ SystemMessage(
868
+ content="you're a good assistant, you always respond with a joke."
869
+ ),
870
+ HumanMessage(content="and who is harrison chasing anyways"),
871
+ AIMessage(
872
+ content="Hmmm let me think.\n\nWhy, he's probably chasing after "
873
+ "the last cup of coffee in the office!"
874
+ ),
875
+ HumanMessage(content="what do you call a speechless parrot"),
876
+ ]
877
+ ```
898
878
  Trim chat history using a custom token counter function that counts the
899
879
  number of tokens in each message.
900
880
 
901
- .. code-block:: python
902
-
903
- messages = [
904
- SystemMessage("This is a 4 token text. The full message is 10 tokens."),
905
- HumanMessage(
906
- "This is a 4 token text. The full message is 10 tokens.", id="first"
907
- ),
908
- AIMessage(
909
- [
910
- {"type": "text", "text": "This is the FIRST 4 token block."},
911
- {"type": "text", "text": "This is the SECOND 4 token block."},
912
- ],
913
- id="second",
914
- ),
915
- HumanMessage(
916
- "This is a 4 token text. The full message is 10 tokens.", id="third"
917
- ),
918
- AIMessage(
919
- "This is a 4 token text. The full message is 10 tokens.",
920
- id="fourth",
921
- ),
922
- ]
923
-
924
-
925
- def dummy_token_counter(messages: list[BaseMessage]) -> int:
926
- # treat each message like it adds 3 default tokens at the beginning
927
- # of the message and at the end of the message. 3 + 4 + 3 = 10 tokens
928
- # per message.
929
-
930
- default_content_len = 4
931
- default_msg_prefix_len = 3
932
- default_msg_suffix_len = 3
933
-
934
- count = 0
935
- for msg in messages:
936
- if isinstance(msg.content, str):
937
- count += (
938
- default_msg_prefix_len
939
- + default_content_len
940
- + default_msg_suffix_len
941
- )
942
- if isinstance(msg.content, list):
943
- count += (
944
- default_msg_prefix_len
945
- + len(msg.content) * default_content_len
946
- + default_msg_suffix_len
947
- )
948
- return count
949
-
950
- First 30 tokens, allowing partial messages:
951
- .. code-block:: python
952
-
953
- trim_messages(
954
- messages,
955
- max_tokens=30,
956
- token_counter=dummy_token_counter,
957
- strategy="first",
958
- allow_partial=True,
959
- )
960
-
961
- .. code-block:: python
962
-
881
+ ```python
882
+ messages = [
883
+ SystemMessage("This is a 4 token text. The full message is 10 tokens."),
884
+ HumanMessage(
885
+ "This is a 4 token text. The full message is 10 tokens.", id="first"
886
+ ),
887
+ AIMessage(
963
888
  [
964
- SystemMessage(
965
- "This is a 4 token text. The full message is 10 tokens."
966
- ),
967
- HumanMessage(
968
- "This is a 4 token text. The full message is 10 tokens.",
969
- id="first",
970
- ),
971
- AIMessage(
972
- [{"type": "text", "text": "This is the FIRST 4 token block."}],
973
- id="second",
974
- ),
975
- ]
889
+ {"type": "text", "text": "This is the FIRST 4 token block."},
890
+ {"type": "text", "text": "This is the SECOND 4 token block."},
891
+ ],
892
+ id="second",
893
+ ),
894
+ HumanMessage(
895
+ "This is a 4 token text. The full message is 10 tokens.", id="third"
896
+ ),
897
+ AIMessage(
898
+ "This is a 4 token text. The full message is 10 tokens.",
899
+ id="fourth",
900
+ ),
901
+ ]
902
+
903
+
904
+ def dummy_token_counter(messages: list[BaseMessage]) -> int:
905
+ # treat each message like it adds 3 default tokens at the beginning
906
+ # of the message and at the end of the message. 3 + 4 + 3 = 10 tokens
907
+ # per message.
908
+
909
+ default_content_len = 4
910
+ default_msg_prefix_len = 3
911
+ default_msg_suffix_len = 3
912
+
913
+ count = 0
914
+ for msg in messages:
915
+ if isinstance(msg.content, str):
916
+ count += (
917
+ default_msg_prefix_len
918
+ + default_content_len
919
+ + default_msg_suffix_len
920
+ )
921
+ if isinstance(msg.content, list):
922
+ count += (
923
+ default_msg_prefix_len
924
+ + len(msg.content) * default_content_len
925
+ + default_msg_suffix_len
926
+ )
927
+ return count
928
+ ```
976
929
 
930
+ First 30 tokens, allowing partial messages:
931
+ ```python
932
+ trim_messages(
933
+ messages,
934
+ max_tokens=30,
935
+ token_counter=dummy_token_counter,
936
+ strategy="first",
937
+ allow_partial=True,
938
+ )
939
+ ```
940
+
941
+ ```python
942
+ [
943
+ SystemMessage("This is a 4 token text. The full message is 10 tokens."),
944
+ HumanMessage(
945
+ "This is a 4 token text. The full message is 10 tokens.",
946
+ id="first",
947
+ ),
948
+ AIMessage(
949
+ [{"type": "text", "text": "This is the FIRST 4 token block."}],
950
+ id="second",
951
+ ),
952
+ ]
953
+ ```
977
954
  """
978
955
  # Validate arguments
979
956
  if start_on and strategy == "first":
@@ -1037,91 +1014,90 @@ def trim_messages(
1037
1014
 
1038
1015
 
1039
1016
  def convert_to_openai_messages(
1040
- messages: Union[MessageLikeRepresentation, Sequence[MessageLikeRepresentation]],
1017
+ messages: MessageLikeRepresentation | Sequence[MessageLikeRepresentation],
1041
1018
  *,
1042
1019
  text_format: Literal["string", "block"] = "string",
1043
1020
  include_id: bool = False,
1044
- ) -> Union[dict, list[dict]]:
1021
+ ) -> dict | list[dict]:
1045
1022
  """Convert LangChain messages into OpenAI message dicts.
1046
1023
 
1047
1024
  Args:
1048
1025
  messages: Message-like object or iterable of objects whose contents are
1049
1026
  in OpenAI, Anthropic, Bedrock Converse, or VertexAI formats.
1050
1027
  text_format: How to format string or text block contents:
1051
- - ``'string'``:
1028
+ - `'string'`:
1052
1029
  If a message has a string content, this is left as a string. If
1053
- a message has content blocks that are all of type ``'text'``, these
1030
+ a message has content blocks that are all of type `'text'`, these
1054
1031
  are joined with a newline to make a single string. If a message has
1055
- content blocks and at least one isn't of type ``'text'``, then
1032
+ content blocks and at least one isn't of type `'text'`, then
1056
1033
  all blocks are left as dicts.
1057
- - ``'block'``:
1034
+ - `'block'`:
1058
1035
  If a message has a string content, this is turned into a list
1059
- with a single content block of type ``'text'``. If a message has
1036
+ with a single content block of type `'text'`. If a message has
1060
1037
  content blocks these are left as is.
1061
1038
  include_id: Whether to include message ids in the openai messages, if they
1062
1039
  are present in the source messages.
1063
1040
 
1064
1041
  Raises:
1065
- ValueError: if an unrecognized ``text_format`` is specified, or if a message
1042
+ ValueError: if an unrecognized `text_format` is specified, or if a message
1066
1043
  content block is missing expected keys.
1067
1044
 
1068
1045
  Returns:
1069
1046
  The return type depends on the input type:
1070
1047
 
1071
1048
  - dict:
1072
- If a single message-like object is passed in, a single OpenAI message
1073
- dict is returned.
1049
+ If a single message-like object is passed in, a single OpenAI message
1050
+ dict is returned.
1074
1051
  - list[dict]:
1075
- If a sequence of message-like objects are passed in, a list of OpenAI
1076
- message dicts is returned.
1052
+ If a sequence of message-like objects are passed in, a list of OpenAI
1053
+ message dicts is returned.
1077
1054
 
1078
1055
  Example:
1056
+ ```python
1057
+ from langchain_core.messages import (
1058
+ convert_to_openai_messages,
1059
+ AIMessage,
1060
+ SystemMessage,
1061
+ ToolMessage,
1062
+ )
1079
1063
 
1080
- .. code-block:: python
1081
-
1082
- from langchain_core.messages import (
1083
- convert_to_openai_messages,
1084
- AIMessage,
1085
- SystemMessage,
1086
- ToolMessage,
1087
- )
1088
-
1089
- messages = [
1090
- SystemMessage([{"type": "text", "text": "foo"}]),
1091
- {
1092
- "role": "user",
1093
- "content": [
1094
- {"type": "text", "text": "whats in this"},
1095
- {
1096
- "type": "image_url",
1097
- "image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"},
1098
- },
1099
- ],
1100
- },
1101
- AIMessage(
1102
- "",
1103
- tool_calls=[
1104
- {
1105
- "name": "analyze",
1106
- "args": {"baz": "buz"},
1107
- "id": "1",
1108
- "type": "tool_call",
1109
- }
1110
- ],
1111
- ),
1112
- ToolMessage("foobar", tool_call_id="1", name="bar"),
1113
- {"role": "assistant", "content": "thats nice"},
1114
- ]
1115
- oai_messages = convert_to_openai_messages(messages)
1116
- # -> [
1117
- # {'role': 'system', 'content': 'foo'},
1118
- # {'role': 'user', 'content': [{'type': 'text', 'text': 'whats in this'}, {'type': 'image_url', 'image_url': {'url': "data:image/png;base64,'/9j/4AAQSk'"}}]},
1119
- # {'role': 'assistant', 'tool_calls': [{'type': 'function', 'id': '1','function': {'name': 'analyze', 'arguments': '{"baz": "buz"}'}}], 'content': ''},
1120
- # {'role': 'tool', 'name': 'bar', 'content': 'foobar'},
1121
- # {'role': 'assistant', 'content': 'thats nice'}
1122
- # ]
1123
-
1124
- .. versionadded:: 0.3.11
1064
+ messages = [
1065
+ SystemMessage([{"type": "text", "text": "foo"}]),
1066
+ {
1067
+ "role": "user",
1068
+ "content": [
1069
+ {"type": "text", "text": "whats in this"},
1070
+ {
1071
+ "type": "image_url",
1072
+ "image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"},
1073
+ },
1074
+ ],
1075
+ },
1076
+ AIMessage(
1077
+ "",
1078
+ tool_calls=[
1079
+ {
1080
+ "name": "analyze",
1081
+ "args": {"baz": "buz"},
1082
+ "id": "1",
1083
+ "type": "tool_call",
1084
+ }
1085
+ ],
1086
+ ),
1087
+ ToolMessage("foobar", tool_call_id="1", name="bar"),
1088
+ {"role": "assistant", "content": "thats nice"},
1089
+ ]
1090
+ oai_messages = convert_to_openai_messages(messages)
1091
+ # -> [
1092
+ # {'role': 'system', 'content': 'foo'},
1093
+ # {'role': 'user', 'content': [{'type': 'text', 'text': 'whats in this'}, {'type': 'image_url', 'image_url': {'url': "data:image/png;base64,'/9j/4AAQSk'"}}]},
1094
+ # {'role': 'assistant', 'tool_calls': [{'type': 'function', 'id': '1','function': {'name': 'analyze', 'arguments': '{"baz": "buz"}'}}], 'content': ''},
1095
+ # {'role': 'tool', 'name': 'bar', 'content': 'foobar'},
1096
+ # {'role': 'assistant', 'content': 'thats nice'}
1097
+ # ]
1098
+ ```
1099
+
1100
+ !!! version-added "Added in version 0.3.11"
1125
1101
 
1126
1102
  """ # noqa: E501
1127
1103
  if text_format not in {"string", "block"}:
@@ -1138,7 +1114,7 @@ def convert_to_openai_messages(
1138
1114
  for i, message in enumerate(messages):
1139
1115
  oai_msg: dict = {"role": _get_message_openai_role(message)}
1140
1116
  tool_messages: list = []
1141
- content: Union[str, list[dict]]
1117
+ content: str | list[dict]
1142
1118
 
1143
1119
  if message.name:
1144
1120
  oai_msg["name"] = message.name
@@ -1421,10 +1397,8 @@ def _first_max_tokens(
1421
1397
  max_tokens: int,
1422
1398
  token_counter: Callable[[list[BaseMessage]], int],
1423
1399
  text_splitter: Callable[[str], list[str]],
1424
- partial_strategy: Optional[Literal["first", "last"]] = None,
1425
- end_on: Optional[
1426
- Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
1427
- ] = None,
1400
+ partial_strategy: Literal["first", "last"] | None = None,
1401
+ end_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
1428
1402
  ) -> list[BaseMessage]:
1429
1403
  messages = list(messages)
1430
1404
  if not messages:
@@ -1541,12 +1515,8 @@ def _last_max_tokens(
1541
1515
  text_splitter: Callable[[str], list[str]],
1542
1516
  allow_partial: bool = False,
1543
1517
  include_system: bool = False,
1544
- start_on: Optional[
1545
- Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
1546
- ] = None,
1547
- end_on: Optional[
1548
- Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
1549
- ] = None,
1518
+ start_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
1519
+ end_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
1550
1520
  ) -> list[BaseMessage]:
1551
1521
  messages = list(messages)
1552
1522
  if len(messages) == 0:
@@ -1622,11 +1592,15 @@ def _msg_to_chunk(message: BaseMessage) -> BaseMessageChunk:
1622
1592
  def _chunk_to_msg(chunk: BaseMessageChunk) -> BaseMessage:
1623
1593
  if chunk.__class__ in _CHUNK_MSG_MAP:
1624
1594
  return _CHUNK_MSG_MAP[chunk.__class__](
1625
- **chunk.model_dump(exclude={"type", "tool_call_chunks"})
1595
+ **chunk.model_dump(exclude={"type", "tool_call_chunks", "chunk_position"})
1626
1596
  )
1627
1597
  for chunk_cls, msg_cls in _CHUNK_MSG_MAP.items():
1628
1598
  if isinstance(chunk, chunk_cls):
1629
- return msg_cls(**chunk.model_dump(exclude={"type", "tool_call_chunks"}))
1599
+ return msg_cls(
1600
+ **chunk.model_dump(
1601
+ exclude={"type", "tool_call_chunks", "chunk_position"}
1602
+ )
1603
+ )
1630
1604
 
1631
1605
  msg = (
1632
1606
  f"Unrecognized message chunk class {chunk.__class__}. Supported classes are "
@@ -1643,7 +1617,7 @@ def _default_text_splitter(text: str) -> list[str]:
1643
1617
 
1644
1618
  def _is_message_type(
1645
1619
  message: BaseMessage,
1646
- type_: Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]],
1620
+ type_: str | type[BaseMessage] | Sequence[str | type[BaseMessage]],
1647
1621
  ) -> bool:
1648
1622
  types = [type_] if isinstance(type_, (str, type)) else type_
1649
1623
  types_str = [t for t in types if isinstance(t, str)]
@@ -1703,27 +1677,27 @@ def count_tokens_approximately(
1703
1677
  Args:
1704
1678
  messages: List of messages to count tokens for.
1705
1679
  chars_per_token: Number of characters per token to use for the approximation.
1706
- Default is 4 (one token corresponds to ~4 chars for common English text).
1707
- You can also specify float values for more fine-grained control.
1708
- `See more here. <https://platform.openai.com/tokenizer>`__
1709
- extra_tokens_per_message: Number of extra tokens to add per message.
1710
- Default is 3 (special tokens, including beginning/end of message).
1711
- You can also specify float values for more fine-grained control.
1712
- `See more here. <https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb>`__
1680
+ One token corresponds to ~4 chars for common English text.
1681
+ You can also specify `float` values for more fine-grained control.
1682
+ [See more here](https://platform.openai.com/tokenizer).
1683
+ extra_tokens_per_message: Number of extra tokens to add per message, e.g.
1684
+ special tokens, including beginning/end of message.
1685
+ You can also specify `float` values for more fine-grained control.
1686
+ [See more here](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb).
1713
1687
  count_name: Whether to include message names in the count.
1714
1688
  Enabled by default.
1715
1689
 
1716
1690
  Returns:
1717
1691
  Approximate number of tokens in the messages.
1718
1692
 
1719
- .. note::
1693
+ !!! note
1720
1694
  This is a simple approximation that may not match the exact token count used by
1721
1695
  specific models. For accurate counts, use model-specific tokenizers.
1722
1696
 
1723
1697
  Warning:
1724
1698
  This function does not currently support counting image tokens.
1725
1699
 
1726
- .. versionadded:: 0.3.46
1700
+ !!! version-added "Added in version 0.3.46"
1727
1701
 
1728
1702
  """
1729
1703
  token_count = 0.0