langchain-core 1.0.0a5__py3-none-any.whl → 1.0.3__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.
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 +51 -64
  5. langchain_core/_api/path.py +3 -6
  6. langchain_core/_import_utils.py +3 -4
  7. langchain_core/agents.py +20 -22
  8. langchain_core/caches.py +65 -66
  9. langchain_core/callbacks/__init__.py +1 -8
  10. langchain_core/callbacks/base.py +321 -336
  11. langchain_core/callbacks/file.py +44 -44
  12. langchain_core/callbacks/manager.py +436 -513
  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 +53 -68
  17. langchain_core/document_loaders/base.py +27 -25
  18. langchain_core/document_loaders/blob_loaders.py +1 -1
  19. langchain_core/document_loaders/langsmith.py +44 -48
  20. langchain_core/documents/__init__.py +23 -3
  21. langchain_core/documents/base.py +98 -90
  22. langchain_core/documents/compressor.py +10 -10
  23. langchain_core/documents/transformers.py +34 -35
  24. langchain_core/embeddings/fake.py +50 -54
  25. langchain_core/example_selectors/length_based.py +1 -1
  26. langchain_core/example_selectors/semantic_similarity.py +28 -32
  27. langchain_core/exceptions.py +21 -20
  28. langchain_core/globals.py +3 -151
  29. langchain_core/indexing/__init__.py +1 -1
  30. langchain_core/indexing/api.py +121 -126
  31. langchain_core/indexing/base.py +73 -75
  32. langchain_core/indexing/in_memory.py +4 -6
  33. langchain_core/language_models/__init__.py +14 -29
  34. langchain_core/language_models/_utils.py +58 -61
  35. langchain_core/language_models/base.py +53 -162
  36. langchain_core/language_models/chat_models.py +298 -387
  37. langchain_core/language_models/fake.py +11 -11
  38. langchain_core/language_models/fake_chat_models.py +42 -36
  39. langchain_core/language_models/llms.py +125 -235
  40. langchain_core/load/dump.py +9 -12
  41. langchain_core/load/load.py +18 -28
  42. langchain_core/load/mapping.py +2 -4
  43. langchain_core/load/serializable.py +42 -40
  44. langchain_core/messages/__init__.py +10 -16
  45. langchain_core/messages/ai.py +148 -148
  46. langchain_core/messages/base.py +58 -52
  47. langchain_core/messages/block_translators/__init__.py +27 -17
  48. langchain_core/messages/block_translators/anthropic.py +6 -6
  49. langchain_core/messages/block_translators/bedrock_converse.py +5 -5
  50. langchain_core/messages/block_translators/google_genai.py +505 -20
  51. langchain_core/messages/block_translators/google_vertexai.py +4 -32
  52. langchain_core/messages/block_translators/groq.py +117 -21
  53. langchain_core/messages/block_translators/langchain_v0.py +5 -5
  54. langchain_core/messages/block_translators/openai.py +11 -11
  55. langchain_core/messages/chat.py +2 -6
  56. langchain_core/messages/content.py +337 -328
  57. langchain_core/messages/function.py +6 -10
  58. langchain_core/messages/human.py +24 -31
  59. langchain_core/messages/modifier.py +2 -2
  60. langchain_core/messages/system.py +19 -29
  61. langchain_core/messages/tool.py +74 -90
  62. langchain_core/messages/utils.py +474 -504
  63. langchain_core/output_parsers/__init__.py +13 -10
  64. langchain_core/output_parsers/base.py +61 -61
  65. langchain_core/output_parsers/format_instructions.py +9 -4
  66. langchain_core/output_parsers/json.py +12 -10
  67. langchain_core/output_parsers/list.py +21 -23
  68. langchain_core/output_parsers/openai_functions.py +49 -47
  69. langchain_core/output_parsers/openai_tools.py +16 -21
  70. langchain_core/output_parsers/pydantic.py +13 -14
  71. langchain_core/output_parsers/string.py +5 -5
  72. langchain_core/output_parsers/transform.py +15 -17
  73. langchain_core/output_parsers/xml.py +35 -34
  74. langchain_core/outputs/__init__.py +1 -1
  75. langchain_core/outputs/chat_generation.py +18 -18
  76. langchain_core/outputs/chat_result.py +1 -3
  77. langchain_core/outputs/generation.py +10 -11
  78. langchain_core/outputs/llm_result.py +10 -10
  79. langchain_core/prompt_values.py +11 -17
  80. langchain_core/prompts/__init__.py +3 -27
  81. langchain_core/prompts/base.py +48 -56
  82. langchain_core/prompts/chat.py +275 -325
  83. langchain_core/prompts/dict.py +5 -5
  84. langchain_core/prompts/few_shot.py +81 -88
  85. langchain_core/prompts/few_shot_with_templates.py +11 -13
  86. langchain_core/prompts/image.py +12 -14
  87. langchain_core/prompts/loading.py +4 -6
  88. langchain_core/prompts/message.py +3 -3
  89. langchain_core/prompts/prompt.py +24 -39
  90. langchain_core/prompts/string.py +26 -10
  91. langchain_core/prompts/structured.py +49 -53
  92. langchain_core/rate_limiters.py +51 -60
  93. langchain_core/retrievers.py +61 -198
  94. langchain_core/runnables/base.py +1478 -1630
  95. langchain_core/runnables/branch.py +53 -57
  96. langchain_core/runnables/config.py +72 -89
  97. langchain_core/runnables/configurable.py +120 -137
  98. langchain_core/runnables/fallbacks.py +83 -79
  99. langchain_core/runnables/graph.py +91 -97
  100. langchain_core/runnables/graph_ascii.py +27 -28
  101. langchain_core/runnables/graph_mermaid.py +38 -50
  102. langchain_core/runnables/graph_png.py +15 -16
  103. langchain_core/runnables/history.py +135 -148
  104. langchain_core/runnables/passthrough.py +124 -150
  105. langchain_core/runnables/retry.py +46 -51
  106. langchain_core/runnables/router.py +25 -30
  107. langchain_core/runnables/schema.py +75 -80
  108. langchain_core/runnables/utils.py +60 -67
  109. langchain_core/stores.py +85 -121
  110. langchain_core/structured_query.py +8 -8
  111. langchain_core/sys_info.py +27 -29
  112. langchain_core/tools/__init__.py +1 -14
  113. langchain_core/tools/base.py +285 -229
  114. langchain_core/tools/convert.py +160 -155
  115. langchain_core/tools/render.py +10 -10
  116. langchain_core/tools/retriever.py +12 -11
  117. langchain_core/tools/simple.py +19 -24
  118. langchain_core/tools/structured.py +32 -39
  119. langchain_core/tracers/__init__.py +1 -9
  120. langchain_core/tracers/base.py +97 -99
  121. langchain_core/tracers/context.py +29 -52
  122. langchain_core/tracers/core.py +49 -53
  123. langchain_core/tracers/evaluation.py +11 -11
  124. langchain_core/tracers/event_stream.py +65 -64
  125. langchain_core/tracers/langchain.py +21 -21
  126. langchain_core/tracers/log_stream.py +45 -45
  127. langchain_core/tracers/memory_stream.py +3 -3
  128. langchain_core/tracers/root_listeners.py +16 -16
  129. langchain_core/tracers/run_collector.py +2 -4
  130. langchain_core/tracers/schemas.py +0 -129
  131. langchain_core/tracers/stdout.py +3 -3
  132. langchain_core/utils/__init__.py +1 -4
  133. langchain_core/utils/_merge.py +2 -2
  134. langchain_core/utils/aiter.py +57 -61
  135. langchain_core/utils/env.py +9 -9
  136. langchain_core/utils/function_calling.py +89 -186
  137. langchain_core/utils/html.py +7 -8
  138. langchain_core/utils/input.py +6 -6
  139. langchain_core/utils/interactive_env.py +1 -1
  140. langchain_core/utils/iter.py +36 -40
  141. langchain_core/utils/json.py +4 -3
  142. langchain_core/utils/json_schema.py +9 -9
  143. langchain_core/utils/mustache.py +8 -10
  144. langchain_core/utils/pydantic.py +33 -35
  145. langchain_core/utils/strings.py +6 -9
  146. langchain_core/utils/usage.py +1 -1
  147. langchain_core/utils/utils.py +66 -62
  148. langchain_core/vectorstores/base.py +182 -216
  149. langchain_core/vectorstores/in_memory.py +101 -176
  150. langchain_core/vectorstores/utils.py +5 -5
  151. langchain_core/version.py +1 -1
  152. langchain_core-1.0.3.dist-info/METADATA +69 -0
  153. langchain_core-1.0.3.dist-info/RECORD +172 -0
  154. {langchain_core-1.0.0a5.dist-info → langchain_core-1.0.3.dist-info}/WHEEL +1 -1
  155. langchain_core/memory.py +0 -120
  156. langchain_core/messages/block_translators/ollama.py +0 -47
  157. langchain_core/prompts/pipeline.py +0 -138
  158. langchain_core/pydantic_v1/__init__.py +0 -30
  159. langchain_core/pydantic_v1/dataclasses.py +0 -23
  160. langchain_core/pydantic_v1/main.py +0 -23
  161. langchain_core/tracers/langchain_v1.py +0 -31
  162. langchain_core/utils/loading.py +0 -35
  163. langchain_core-1.0.0a5.dist-info/METADATA +0 -77
  164. langchain_core-1.0.0a5.dist-info/RECORD +0 -181
  165. langchain_core-1.0.0a5.dist-info/entry_points.txt +0 -4
@@ -4,7 +4,7 @@ import json
4
4
  import logging
5
5
  import operator
6
6
  from collections.abc import Sequence
7
- from typing import Any, Literal, Optional, Union, cast, overload
7
+ from typing import Any, Literal, cast, overload
8
8
 
9
9
  from pydantic import model_validator
10
10
  from typing_extensions import NotRequired, Self, TypedDict, override
@@ -40,18 +40,18 @@ class InputTokenDetails(TypedDict, total=False):
40
40
  Does *not* need to sum to full input token count. Does *not* need to have all keys.
41
41
 
42
42
  Example:
43
- .. code-block:: python
44
-
45
- {
46
- "audio": 10,
47
- "cache_creation": 200,
48
- "cache_read": 100,
49
- }
50
-
51
- .. versionadded:: 0.3.9
43
+ ```python
44
+ {
45
+ "audio": 10,
46
+ "cache_creation": 200,
47
+ "cache_read": 100,
48
+ }
49
+ ```
52
50
 
53
51
  May also hold extra provider-specific keys.
54
52
 
53
+ !!! version-added "Added in version 0.3.9"
54
+
55
55
  """
56
56
 
57
57
  audio: int
@@ -76,14 +76,16 @@ class OutputTokenDetails(TypedDict, total=False):
76
76
  Does *not* need to sum to full output token count. Does *not* need to have all keys.
77
77
 
78
78
  Example:
79
- .. code-block:: python
79
+ ```python
80
+ {
81
+ "audio": 10,
82
+ "reasoning": 200,
83
+ }
84
+ ```
80
85
 
81
- {
82
- "audio": 10,
83
- "reasoning": 200,
84
- }
86
+ May also hold extra provider-specific keys.
85
87
 
86
- .. versionadded:: 0.3.9
88
+ !!! version-added "Added in version 0.3.9"
87
89
 
88
90
  """
89
91
 
@@ -104,27 +106,30 @@ class UsageMetadata(TypedDict):
104
106
  This is a standard representation of token usage that is consistent across models.
105
107
 
106
108
  Example:
107
- .. code-block:: python
108
-
109
- {
110
- "input_tokens": 350,
111
- "output_tokens": 240,
112
- "total_tokens": 590,
113
- "input_token_details": {
114
- "audio": 10,
115
- "cache_creation": 200,
116
- "cache_read": 100,
117
- },
118
- "output_token_details": {
119
- "audio": 10,
120
- "reasoning": 200,
121
- },
122
- }
123
-
124
- .. versionchanged:: 0.3.9
109
+ ```python
110
+ {
111
+ "input_tokens": 350,
112
+ "output_tokens": 240,
113
+ "total_tokens": 590,
114
+ "input_token_details": {
115
+ "audio": 10,
116
+ "cache_creation": 200,
117
+ "cache_read": 100,
118
+ },
119
+ "output_token_details": {
120
+ "audio": 10,
121
+ "reasoning": 200,
122
+ },
123
+ }
124
+ ```
125
125
 
126
- Added ``input_token_details`` and ``output_token_details``.
126
+ !!! warning "Behavior changed in 0.3.9"
127
+ Added `input_token_details` and `output_token_details`.
127
128
 
129
+ !!! note "LangSmith SDK"
130
+ The LangSmith SDK also has a `UsageMetadata` class. While the two share fields,
131
+ LangSmith's `UsageMetadata` has additional fields to capture cost information
132
+ used by the LangSmith platform.
128
133
  """
129
134
 
130
135
  input_tokens: int
@@ -132,7 +137,7 @@ class UsageMetadata(TypedDict):
132
137
  output_tokens: int
133
138
  """Count of output (or completion) tokens. Sum of all output token types."""
134
139
  total_tokens: int
135
- """Total token count. Sum of input_tokens + output_tokens."""
140
+ """Total token count. Sum of `input_tokens` + `output_tokens`."""
136
141
  input_token_details: NotRequired[InputTokenDetails]
137
142
  """Breakdown of input token counts.
138
143
 
@@ -142,64 +147,61 @@ class UsageMetadata(TypedDict):
142
147
  """Breakdown of output token counts.
143
148
 
144
149
  Does *not* need to sum to full output token count. Does *not* need to have all keys.
145
-
146
150
  """
147
151
 
148
152
 
149
153
  class AIMessage(BaseMessage):
150
154
  """Message from an AI.
151
155
 
152
- AIMessage is returned from a chat model as a response to a prompt.
156
+ An `AIMessage` is returned from a chat model as a response to a prompt.
153
157
 
154
158
  This message represents the output of the model and consists of both
155
- the raw output as returned by the model together standardized fields
159
+ the raw output as returned by the model and standardized fields
156
160
  (e.g., tool calls, usage metadata) added by the LangChain framework.
157
-
158
161
  """
159
162
 
160
163
  tool_calls: list[ToolCall] = []
161
- """If provided, tool calls associated with the message."""
164
+ """If present, tool calls associated with the message."""
162
165
  invalid_tool_calls: list[InvalidToolCall] = []
163
- """If provided, tool calls with parsing errors associated with the message."""
164
- usage_metadata: Optional[UsageMetadata] = None
165
- """If provided, usage metadata for a message, such as token counts.
166
+ """If present, tool calls with parsing errors associated with the message."""
167
+ usage_metadata: UsageMetadata | None = None
168
+ """If present, usage metadata for a message, such as token counts.
166
169
 
167
170
  This is a standard representation of token usage that is consistent across models.
168
-
169
171
  """
170
172
 
171
173
  type: Literal["ai"] = "ai"
172
- """The type of the message (used for deserialization). Defaults to "ai"."""
174
+ """The type of the message (used for deserialization)."""
173
175
 
174
176
  @overload
175
177
  def __init__(
176
178
  self,
177
- content: Union[str, list[Union[str, dict]]],
179
+ content: str | list[str | dict],
178
180
  **kwargs: Any,
179
181
  ) -> None: ...
180
182
 
181
183
  @overload
182
184
  def __init__(
183
185
  self,
184
- content: Optional[Union[str, list[Union[str, dict]]]] = None,
185
- content_blocks: Optional[list[types.ContentBlock]] = None,
186
+ content: str | list[str | dict] | None = None,
187
+ content_blocks: list[types.ContentBlock] | None = None,
186
188
  **kwargs: Any,
187
189
  ) -> None: ...
188
190
 
189
191
  def __init__(
190
192
  self,
191
- content: Optional[Union[str, list[Union[str, dict]]]] = None,
192
- content_blocks: Optional[list[types.ContentBlock]] = None,
193
+ content: str | list[str | dict] | None = None,
194
+ content_blocks: list[types.ContentBlock] | None = None,
193
195
  **kwargs: Any,
194
196
  ) -> None:
195
- """Initialize ``AIMessage``.
197
+ """Initialize an `AIMessage`.
196
198
 
197
- Specify ``content`` as positional arg or ``content_blocks`` for typing.
199
+ Specify `content` as positional arg or `content_blocks` for typing.
198
200
 
199
201
  Args:
200
202
  content: The content of the message.
201
203
  content_blocks: Typed standard content.
202
- kwargs: Additional arguments to pass to the parent class.
204
+ **kwargs: Additional arguments to pass to the parent class.
203
205
  """
204
206
  if content_blocks is not None:
205
207
  # If there are tool calls in content_blocks, but not in tool_calls, add them
@@ -210,7 +212,7 @@ class AIMessage(BaseMessage):
210
212
  kwargs["tool_calls"] = content_tool_calls
211
213
 
212
214
  super().__init__(
213
- content=cast("Union[str, list[Union[str, dict]]]", content_blocks),
215
+ content=cast("str | list[str | dict]", content_blocks),
214
216
  **kwargs,
215
217
  )
216
218
  else:
@@ -218,7 +220,11 @@ class AIMessage(BaseMessage):
218
220
 
219
221
  @property
220
222
  def lc_attributes(self) -> dict:
221
- """Attrs to be serialized even if they are derived from other init args."""
223
+ """Attributes to be serialized.
224
+
225
+ Includes all attributes, even if they are derived from other initialization
226
+ arguments.
227
+ """
222
228
  return {
223
229
  "tool_calls": self.tool_calls,
224
230
  "invalid_tool_calls": self.invalid_tool_calls,
@@ -226,11 +232,11 @@ class AIMessage(BaseMessage):
226
232
 
227
233
  @property
228
234
  def content_blocks(self) -> list[types.ContentBlock]:
229
- """Return content blocks of the message.
235
+ """Return standard, typed `ContentBlock` dicts from the message.
230
236
 
231
237
  If the message has a known model provider, use the provider-specific translator
232
238
  first before falling back to best-effort parsing. For details, see the property
233
- on ``BaseMessage``.
239
+ on `BaseMessage`.
234
240
  """
235
241
  if self.response_metadata.get("output_version") == "v1":
236
242
  return cast("list[types.ContentBlock]", self.content)
@@ -332,11 +338,10 @@ class AIMessage(BaseMessage):
332
338
 
333
339
  @override
334
340
  def pretty_repr(self, html: bool = False) -> str:
335
- """Return a pretty representation of the message.
341
+ """Return a pretty representation of the message for display.
336
342
 
337
343
  Args:
338
344
  html: Whether to return an HTML-formatted string.
339
- Defaults to False.
340
345
 
341
346
  Returns:
342
347
  A pretty representation of the message.
@@ -345,7 +350,7 @@ class AIMessage(BaseMessage):
345
350
  base = super().pretty_repr(html=html)
346
351
  lines = []
347
352
 
348
- def _format_tool_args(tc: Union[ToolCall, InvalidToolCall]) -> list[str]:
353
+ def _format_tool_args(tc: ToolCall | InvalidToolCall) -> list[str]:
349
354
  lines = [
350
355
  f" {tc.get('name', 'Tool')} ({tc.get('id')})",
351
356
  f" Call ID: {tc.get('id')}",
@@ -373,31 +378,27 @@ class AIMessage(BaseMessage):
373
378
 
374
379
 
375
380
  class AIMessageChunk(AIMessage, BaseMessageChunk):
376
- """Message chunk from an AI."""
381
+ """Message chunk from an AI (yielded when streaming)."""
377
382
 
378
383
  # Ignoring mypy re-assignment here since we're overriding the value
379
384
  # to make sure that the chunk variant can be discriminated from the
380
385
  # non-chunk variant.
381
386
  type: Literal["AIMessageChunk"] = "AIMessageChunk" # type: ignore[assignment]
382
- """The type of the message (used for deserialization).
383
-
384
- Defaults to ``AIMessageChunk``.
385
-
386
- """
387
+ """The type of the message (used for deserialization)."""
387
388
 
388
389
  tool_call_chunks: list[ToolCallChunk] = []
389
390
  """If provided, tool call chunks associated with the message."""
390
391
 
391
- chunk_position: Optional[Literal["last"]] = None
392
- """Optional span represented by an aggregated AIMessageChunk.
392
+ chunk_position: Literal["last"] | None = None
393
+ """Optional span represented by an aggregated `AIMessageChunk`.
393
394
 
394
- If a chunk with ``chunk_position="last"`` is aggregated into a stream,
395
- ``tool_call_chunks`` in message content will be parsed into ``tool_calls``.
395
+ If a chunk with `chunk_position="last"` is aggregated into a stream,
396
+ `tool_call_chunks` in message content will be parsed into `tool_calls`.
396
397
  """
397
398
 
398
399
  @property
399
400
  def lc_attributes(self) -> dict:
400
- """Attrs to be serialized even if they are derived from other init args."""
401
+ """Attributes to be serialized, even if they are derived from other initialization args.""" # noqa: E501
401
402
  return {
402
403
  "tool_calls": self.tool_calls,
403
404
  "invalid_tool_calls": self.invalid_tool_calls,
@@ -405,7 +406,7 @@ class AIMessageChunk(AIMessage, BaseMessageChunk):
405
406
 
406
407
  @property
407
408
  def content_blocks(self) -> list[types.ContentBlock]:
408
- """Return content blocks of the message."""
409
+ """Return standard, typed `ContentBlock` dicts from the message."""
409
410
  if self.response_metadata.get("output_version") == "v1":
410
411
  return cast("list[types.ContentBlock]", self.content)
411
412
 
@@ -546,12 +547,15 @@ class AIMessageChunk(AIMessage, BaseMessageChunk):
546
547
  and call_id in id_to_tc
547
548
  ):
548
549
  self.content[idx] = cast("dict[str, Any]", id_to_tc[call_id])
550
+ if "extras" in block:
551
+ # mypy does not account for instance check for dict above
552
+ self.content[idx]["extras"] = block["extras"] # type: ignore[index]
549
553
 
550
554
  return self
551
555
 
552
556
  @model_validator(mode="after")
553
557
  def init_server_tool_calls(self) -> Self:
554
- """Parse server_tool_call_chunks."""
558
+ """Parse `server_tool_call_chunks`."""
555
559
  if (
556
560
  self.chunk_position == "last"
557
561
  and self.response_metadata.get("output_version") == "v1"
@@ -597,14 +601,14 @@ class AIMessageChunk(AIMessage, BaseMessageChunk):
597
601
  def add_ai_message_chunks(
598
602
  left: AIMessageChunk, *others: AIMessageChunk
599
603
  ) -> AIMessageChunk:
600
- """Add multiple ``AIMessageChunk``s together.
604
+ """Add multiple `AIMessageChunk`s together.
601
605
 
602
606
  Args:
603
- left: The first ``AIMessageChunk``.
604
- *others: Other ``AIMessageChunk``s to add.
607
+ left: The first `AIMessageChunk`.
608
+ *others: Other `AIMessageChunk`s to add.
605
609
 
606
610
  Returns:
607
- The resulting ``AIMessageChunk``.
611
+ The resulting `AIMessageChunk`.
608
612
 
609
613
  """
610
614
  content = merge_content(left.content, *(o.content for o in others))
@@ -633,7 +637,7 @@ def add_ai_message_chunks(
633
637
 
634
638
  # Token usage
635
639
  if left.usage_metadata or any(o.usage_metadata is not None for o in others):
636
- usage_metadata: Optional[UsageMetadata] = left.usage_metadata
640
+ usage_metadata: UsageMetadata | None = left.usage_metadata
637
641
  for other in others:
638
642
  usage_metadata = add_usage(usage_metadata, other.usage_metadata)
639
643
  else:
@@ -651,19 +655,19 @@ def add_ai_message_chunks(
651
655
  chunk_id = id_
652
656
  break
653
657
  else:
654
- # second pass: prefer lc_run-* ids over lc_* ids
658
+ # second pass: prefer lc_run-* IDs over lc_* IDs
655
659
  for id_ in candidates:
656
660
  if id_ and id_.startswith(LC_ID_PREFIX):
657
661
  chunk_id = id_
658
662
  break
659
663
  else:
660
- # third pass: take any remaining id (auto-generated lc_* ids)
664
+ # third pass: take any remaining ID (auto-generated lc_* IDs)
661
665
  for id_ in candidates:
662
666
  if id_:
663
667
  chunk_id = id_
664
668
  break
665
669
 
666
- chunk_position: Optional[Literal["last"]] = (
670
+ chunk_position: Literal["last"] | None = (
667
671
  "last" if any(x.chunk_position == "last" for x in [left, *others]) else None
668
672
  )
669
673
 
@@ -678,49 +682,46 @@ def add_ai_message_chunks(
678
682
  )
679
683
 
680
684
 
681
- def add_usage(
682
- left: Optional[UsageMetadata], right: Optional[UsageMetadata]
683
- ) -> UsageMetadata:
685
+ def add_usage(left: UsageMetadata | None, right: UsageMetadata | None) -> UsageMetadata:
684
686
  """Recursively add two UsageMetadata objects.
685
687
 
686
688
  Example:
687
- .. code-block:: python
688
-
689
- from langchain_core.messages.ai import add_usage
690
-
691
- left = UsageMetadata(
692
- input_tokens=5,
693
- output_tokens=0,
694
- total_tokens=5,
695
- input_token_details=InputTokenDetails(cache_read=3),
696
- )
697
- right = UsageMetadata(
698
- input_tokens=0,
699
- output_tokens=10,
700
- total_tokens=10,
701
- output_token_details=OutputTokenDetails(reasoning=4),
702
- )
689
+ ```python
690
+ from langchain_core.messages.ai import add_usage
691
+
692
+ left = UsageMetadata(
693
+ input_tokens=5,
694
+ output_tokens=0,
695
+ total_tokens=5,
696
+ input_token_details=InputTokenDetails(cache_read=3),
697
+ )
698
+ right = UsageMetadata(
699
+ input_tokens=0,
700
+ output_tokens=10,
701
+ total_tokens=10,
702
+ output_token_details=OutputTokenDetails(reasoning=4),
703
+ )
703
704
 
704
- add_usage(left, right)
705
+ add_usage(left, right)
706
+ ```
705
707
 
706
708
  results in
707
709
 
708
- .. code-block:: python
709
-
710
- UsageMetadata(
711
- input_tokens=5,
712
- output_tokens=10,
713
- total_tokens=15,
714
- input_token_details=InputTokenDetails(cache_read=3),
715
- output_token_details=OutputTokenDetails(reasoning=4),
716
- )
717
-
710
+ ```python
711
+ UsageMetadata(
712
+ input_tokens=5,
713
+ output_tokens=10,
714
+ total_tokens=15,
715
+ input_token_details=InputTokenDetails(cache_read=3),
716
+ output_token_details=OutputTokenDetails(reasoning=4),
717
+ )
718
+ ```
718
719
  Args:
719
- left: The first ``UsageMetadata`` object.
720
- right: The second ``UsageMetadata`` object.
720
+ left: The first `UsageMetadata` object.
721
+ right: The second `UsageMetadata` object.
721
722
 
722
723
  Returns:
723
- The sum of the two ``UsageMetadata`` objects.
724
+ The sum of the two `UsageMetadata` objects.
724
725
 
725
726
  """
726
727
  if not (left or right):
@@ -741,50 +742,49 @@ def add_usage(
741
742
 
742
743
 
743
744
  def subtract_usage(
744
- left: Optional[UsageMetadata], right: Optional[UsageMetadata]
745
+ left: UsageMetadata | None, right: UsageMetadata | None
745
746
  ) -> UsageMetadata:
746
- """Recursively subtract two ``UsageMetadata`` objects.
747
+ """Recursively subtract two `UsageMetadata` objects.
747
748
 
748
- Token counts cannot be negative so the actual operation is ``max(left - right, 0)``.
749
+ Token counts cannot be negative so the actual operation is `max(left - right, 0)`.
749
750
 
750
751
  Example:
751
- .. code-block:: python
752
-
753
- from langchain_core.messages.ai import subtract_usage
754
-
755
- left = UsageMetadata(
756
- input_tokens=5,
757
- output_tokens=10,
758
- total_tokens=15,
759
- input_token_details=InputTokenDetails(cache_read=4),
760
- )
761
- right = UsageMetadata(
762
- input_tokens=3,
763
- output_tokens=8,
764
- total_tokens=11,
765
- output_token_details=OutputTokenDetails(reasoning=4),
766
- )
752
+ ```python
753
+ from langchain_core.messages.ai import subtract_usage
754
+
755
+ left = UsageMetadata(
756
+ input_tokens=5,
757
+ output_tokens=10,
758
+ total_tokens=15,
759
+ input_token_details=InputTokenDetails(cache_read=4),
760
+ )
761
+ right = UsageMetadata(
762
+ input_tokens=3,
763
+ output_tokens=8,
764
+ total_tokens=11,
765
+ output_token_details=OutputTokenDetails(reasoning=4),
766
+ )
767
767
 
768
- subtract_usage(left, right)
768
+ subtract_usage(left, right)
769
+ ```
769
770
 
770
771
  results in
771
772
 
772
- .. code-block:: python
773
-
774
- UsageMetadata(
775
- input_tokens=2,
776
- output_tokens=2,
777
- total_tokens=4,
778
- input_token_details=InputTokenDetails(cache_read=4),
779
- output_token_details=OutputTokenDetails(reasoning=0),
780
- )
781
-
773
+ ```python
774
+ UsageMetadata(
775
+ input_tokens=2,
776
+ output_tokens=2,
777
+ total_tokens=4,
778
+ input_token_details=InputTokenDetails(cache_read=4),
779
+ output_token_details=OutputTokenDetails(reasoning=0),
780
+ )
781
+ ```
782
782
  Args:
783
- left: The first ``UsageMetadata`` object.
784
- right: The second ``UsageMetadata`` object.
783
+ left: The first `UsageMetadata` object.
784
+ right: The second `UsageMetadata` object.
785
785
 
786
786
  Returns:
787
- The resulting ``UsageMetadata`` after subtraction.
787
+ The resulting `UsageMetadata` after subtraction.
788
788
 
789
789
  """
790
790
  if not (left or right):