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
@@ -2,11 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING, Any, Optional, Union, cast
5
+ from typing import TYPE_CHECKING, Any, cast, overload
6
6
 
7
7
  from pydantic import ConfigDict, Field
8
+ from typing_extensions import Self
8
9
 
10
+ from langchain_core._api.deprecation import warn_deprecated
9
11
  from langchain_core.load.serializable import Serializable
12
+ from langchain_core.messages import content as types
10
13
  from langchain_core.utils import get_bolded_text
11
14
  from langchain_core.utils._merge import merge_dicts, merge_lists
12
15
  from langchain_core.utils.interactive_env import is_interactive_env
@@ -17,14 +20,83 @@ if TYPE_CHECKING:
17
20
  from langchain_core.prompts.chat import ChatPromptTemplate
18
21
 
19
22
 
23
+ def _extract_reasoning_from_additional_kwargs(
24
+ message: BaseMessage,
25
+ ) -> types.ReasoningContentBlock | None:
26
+ """Extract `reasoning_content` from `additional_kwargs`.
27
+
28
+ Handles reasoning content stored in various formats:
29
+ - `additional_kwargs["reasoning_content"]` (string) - Ollama, DeepSeek, XAI, Groq
30
+
31
+ Args:
32
+ message: The message to extract reasoning from.
33
+
34
+ Returns:
35
+ A `ReasoningContentBlock` if reasoning content is found, None otherwise.
36
+ """
37
+ additional_kwargs = getattr(message, "additional_kwargs", {})
38
+
39
+ reasoning_content = additional_kwargs.get("reasoning_content")
40
+ if reasoning_content is not None and isinstance(reasoning_content, str):
41
+ return {"type": "reasoning", "reasoning": reasoning_content}
42
+
43
+ return None
44
+
45
+
46
+ class TextAccessor(str):
47
+ """String-like object that supports both property and method access patterns.
48
+
49
+ Exists to maintain backward compatibility while transitioning from method-based to
50
+ property-based text access in message objects. In LangChain <v1.0, message text was
51
+ accessed via `.text()` method calls. In v1.0=<, the preferred pattern is property
52
+ access via `.text`.
53
+
54
+ Rather than breaking existing code immediately, `TextAccessor` allows both
55
+ patterns:
56
+ - Modern property access: `message.text` (returns string directly)
57
+ - Legacy method access: `message.text()` (callable, emits deprecation warning)
58
+
59
+ """
60
+
61
+ __slots__ = ()
62
+
63
+ def __new__(cls, value: str) -> Self:
64
+ """Create new TextAccessor instance."""
65
+ return str.__new__(cls, value)
66
+
67
+ def __call__(self) -> str:
68
+ """Enable method-style text access for backward compatibility.
69
+
70
+ This method exists solely to support legacy code that calls `.text()`
71
+ as a method. New code should use property access (`.text`) instead.
72
+
73
+ !!! deprecated
74
+ As of `langchain-core` 1.0.0, calling `.text()` as a method is deprecated.
75
+ Use `.text` as a property instead. This method will be removed in 2.0.0.
76
+
77
+ Returns:
78
+ The string content, identical to property access.
79
+
80
+ """
81
+ warn_deprecated(
82
+ since="1.0.0",
83
+ message=(
84
+ "Calling .text() as a method is deprecated. "
85
+ "Use .text as a property instead (e.g., message.text)."
86
+ ),
87
+ removal="2.0.0",
88
+ )
89
+ return str(self)
90
+
91
+
20
92
  class BaseMessage(Serializable):
21
93
  """Base abstract message class.
22
94
 
23
- Messages are the inputs and outputs of ``ChatModel``s.
95
+ Messages are the inputs and outputs of a chat model.
24
96
  """
25
97
 
26
- content: Union[str, list[Union[str, dict]]]
27
- """The string contents of the message."""
98
+ content: str | list[str | dict]
99
+ """The contents of the message."""
28
100
 
29
101
  additional_kwargs: dict = Field(default_factory=dict)
30
102
  """Reserved for additional payload data associated with the message.
@@ -45,7 +117,7 @@ class BaseMessage(Serializable):
45
117
 
46
118
  """
47
119
 
48
- name: Optional[str] = None
120
+ name: str | None = None
49
121
  """An optional name for the message.
50
122
 
51
123
  This can be used to provide a human-readable name for the message.
@@ -55,7 +127,7 @@ class BaseMessage(Serializable):
55
127
 
56
128
  """
57
129
 
58
- id: Optional[str] = Field(default=None, coerce_numbers_to_str=True)
130
+ id: str | None = Field(default=None, coerce_numbers_to_str=True)
59
131
  """An optional unique identifier for the message.
60
132
 
61
133
  This should ideally be provided by the provider/model which created the message.
@@ -66,21 +138,44 @@ class BaseMessage(Serializable):
66
138
  extra="allow",
67
139
  )
68
140
 
141
+ @overload
142
+ def __init__(
143
+ self,
144
+ content: str | list[str | dict],
145
+ **kwargs: Any,
146
+ ) -> None: ...
147
+
148
+ @overload
69
149
  def __init__(
70
150
  self,
71
- content: Union[str, list[Union[str, dict]]],
151
+ content: str | list[str | dict] | None = None,
152
+ content_blocks: list[types.ContentBlock] | None = None,
153
+ **kwargs: Any,
154
+ ) -> None: ...
155
+
156
+ def __init__(
157
+ self,
158
+ content: str | list[str | dict] | None = None,
159
+ content_blocks: list[types.ContentBlock] | None = None,
72
160
  **kwargs: Any,
73
161
  ) -> None:
74
- """Initialize ``BaseMessage``.
162
+ """Initialize a `BaseMessage`.
163
+
164
+ Specify `content` as positional arg or `content_blocks` for typing.
75
165
 
76
166
  Args:
77
- content: The string contents of the message.
167
+ content: The contents of the message.
168
+ content_blocks: Typed standard content.
169
+ **kwargs: Additional arguments to pass to the parent class.
78
170
  """
79
- super().__init__(content=content, **kwargs)
171
+ if content_blocks is not None:
172
+ super().__init__(content=content_blocks, **kwargs)
173
+ else:
174
+ super().__init__(content=content, **kwargs)
80
175
 
81
176
  @classmethod
82
177
  def is_lc_serializable(cls) -> bool:
83
- """``BaseMessage`` is serializable.
178
+ """`BaseMessage` is serializable.
84
179
 
85
180
  Returns:
86
181
  True
@@ -89,33 +184,105 @@ class BaseMessage(Serializable):
89
184
 
90
185
  @classmethod
91
186
  def get_lc_namespace(cls) -> list[str]:
92
- """Get the namespace of the langchain object.
187
+ """Get the namespace of the LangChain object.
93
188
 
94
189
  Returns:
95
- ``["langchain", "schema", "messages"]``
190
+ `["langchain", "schema", "messages"]`
96
191
  """
97
192
  return ["langchain", "schema", "messages"]
98
193
 
99
- def text(self) -> str:
100
- """Get the text ``content`` of the message.
194
+ @property
195
+ def content_blocks(self) -> list[types.ContentBlock]:
196
+ r"""Load content blocks from the message content.
197
+
198
+ !!! version-added "Added in version 1.0.0"
199
+
200
+ """
201
+ # Needed here to avoid circular import, as these classes import BaseMessages
202
+ from langchain_core.messages import content as types # noqa: PLC0415
203
+ from langchain_core.messages.block_translators.anthropic import ( # noqa: PLC0415
204
+ _convert_to_v1_from_anthropic_input,
205
+ )
206
+ from langchain_core.messages.block_translators.bedrock_converse import ( # noqa: PLC0415
207
+ _convert_to_v1_from_converse_input,
208
+ )
209
+ from langchain_core.messages.block_translators.google_genai import ( # noqa: PLC0415
210
+ _convert_to_v1_from_genai_input,
211
+ )
212
+ from langchain_core.messages.block_translators.langchain_v0 import ( # noqa: PLC0415
213
+ _convert_v0_multimodal_input_to_v1,
214
+ )
215
+ from langchain_core.messages.block_translators.openai import ( # noqa: PLC0415
216
+ _convert_to_v1_from_chat_completions_input,
217
+ )
218
+
219
+ blocks: list[types.ContentBlock] = []
220
+ content = (
221
+ # Transpose string content to list, otherwise assumed to be list
222
+ [self.content]
223
+ if isinstance(self.content, str) and self.content
224
+ else self.content
225
+ )
226
+ for item in content:
227
+ if isinstance(item, str):
228
+ # Plain string content is treated as a text block
229
+ blocks.append({"type": "text", "text": item})
230
+ elif isinstance(item, dict):
231
+ item_type = item.get("type")
232
+ if item_type not in types.KNOWN_BLOCK_TYPES:
233
+ # Handle all provider-specific or None type blocks as non-standard -
234
+ # we'll come back to these later
235
+ blocks.append({"type": "non_standard", "value": item})
236
+ else:
237
+ # Guard against v0 blocks that share the same `type` keys
238
+ if "source_type" in item:
239
+ blocks.append({"type": "non_standard", "value": item})
240
+ continue
241
+
242
+ # This can't be a v0 block (since they require `source_type`),
243
+ # so it's a known v1 block type
244
+ blocks.append(cast("types.ContentBlock", item))
245
+
246
+ # Subsequent passes: attempt to unpack non-standard blocks.
247
+ # This is the last stop - if we can't parse it here, it is left as non-standard
248
+ for parsing_step in [
249
+ _convert_v0_multimodal_input_to_v1,
250
+ _convert_to_v1_from_chat_completions_input,
251
+ _convert_to_v1_from_anthropic_input,
252
+ _convert_to_v1_from_genai_input,
253
+ _convert_to_v1_from_converse_input,
254
+ ]:
255
+ blocks = parsing_step(blocks)
256
+ return blocks
257
+
258
+ @property
259
+ def text(self) -> TextAccessor:
260
+ """Get the text content of the message as a string.
261
+
262
+ Can be used as both property (`message.text`) and method (`message.text()`).
263
+
264
+ !!! deprecated
265
+ As of `langchain-core` 1.0.0, calling `.text()` as a method is deprecated.
266
+ Use `.text` as a property instead. This method will be removed in 2.0.0.
101
267
 
102
268
  Returns:
103
269
  The text content of the message.
104
270
 
105
271
  """
106
272
  if isinstance(self.content, str):
107
- return self.content
108
-
109
- # must be a list
110
- blocks = [
111
- block
112
- for block in self.content
113
- if isinstance(block, str)
114
- or (block.get("type") == "text" and isinstance(block.get("text"), str))
115
- ]
116
- return "".join(
117
- block if isinstance(block, str) else block["text"] for block in blocks
118
- )
273
+ text_value = self.content
274
+ else:
275
+ # must be a list
276
+ blocks = [
277
+ block
278
+ for block in self.content
279
+ if isinstance(block, str)
280
+ or (block.get("type") == "text" and isinstance(block.get("text"), str))
281
+ ]
282
+ text_value = "".join(
283
+ block if isinstance(block, str) else block["text"] for block in blocks
284
+ )
285
+ return TextAccessor(text_value)
119
286
 
120
287
  def __add__(self, other: Any) -> ChatPromptTemplate:
121
288
  """Concatenate this message with another message.
@@ -139,8 +306,8 @@ class BaseMessage(Serializable):
139
306
  """Get a pretty representation of the message.
140
307
 
141
308
  Args:
142
- html: Whether to format the message as HTML. If True, the message will be
143
- formatted with HTML tags. Default is False.
309
+ html: Whether to format the message as HTML. If `True`, the message will be
310
+ formatted with HTML tags.
144
311
 
145
312
  Returns:
146
313
  A pretty representation of the message.
@@ -158,20 +325,22 @@ class BaseMessage(Serializable):
158
325
 
159
326
 
160
327
  def merge_content(
161
- first_content: Union[str, list[Union[str, dict]]],
162
- *contents: Union[str, list[Union[str, dict]]],
163
- ) -> Union[str, list[Union[str, dict]]]:
328
+ first_content: str | list[str | dict],
329
+ *contents: str | list[str | dict],
330
+ ) -> str | list[str | dict]:
164
331
  """Merge multiple message contents.
165
332
 
166
333
  Args:
167
- first_content: The first ``content``. Can be a string or a list.
168
- contents: The other ``content``s. Can be a string or a list.
334
+ first_content: The first `content`. Can be a string or a list.
335
+ contents: The other `content`s. Can be a string or a list.
169
336
 
170
337
  Returns:
171
338
  The merged content.
172
339
 
173
340
  """
174
- merged = first_content
341
+ merged: str | list[str | dict]
342
+ merged = "" if first_content is None else first_content
343
+
175
344
  for content in contents:
176
345
  # If current is a string
177
346
  if isinstance(merged, str):
@@ -190,8 +359,10 @@ def merge_content(
190
359
  elif merged and isinstance(merged[-1], str):
191
360
  merged[-1] += content
192
361
  # If second content is an empty string, treat as a no-op
193
- elif content:
194
- # Otherwise, add the second content as a new element of the list
362
+ elif content == "":
363
+ pass
364
+ # Otherwise, add the second content as a new element of the list
365
+ elif merged:
195
366
  merged.append(content)
196
367
  return merged
197
368
 
@@ -217,9 +388,9 @@ class BaseMessageChunk(BaseMessage):
217
388
 
218
389
  For example,
219
390
 
220
- ``AIMessageChunk(content="Hello") + AIMessageChunk(content=" World")``
391
+ `AIMessageChunk(content="Hello") + AIMessageChunk(content=" World")`
221
392
 
222
- will give ``AIMessageChunk(content="Hello World")``
393
+ will give `AIMessageChunk(content="Hello World")`
223
394
 
224
395
  """
225
396
  if isinstance(other, BaseMessageChunk):
@@ -268,8 +439,8 @@ def message_to_dict(message: BaseMessage) -> dict:
268
439
  message: Message to convert.
269
440
 
270
441
  Returns:
271
- Message as a dict. The dict will have a ``type`` key with the message type
272
- and a ``data`` key with the message data as a dict.
442
+ Message as a dict. The dict will have a `type` key with the message type
443
+ and a `data` key with the message data as a dict.
273
444
 
274
445
  """
275
446
  return {"type": message.type, "data": message.model_dump()}
@@ -279,7 +450,7 @@ def messages_to_dict(messages: Sequence[BaseMessage]) -> list[dict]:
279
450
  """Convert a sequence of Messages to a list of dictionaries.
280
451
 
281
452
  Args:
282
- messages: Sequence of messages (as ``BaseMessage``s) to convert.
453
+ messages: Sequence of messages (as `BaseMessage`s) to convert.
283
454
 
284
455
  Returns:
285
456
  List of messages as dicts.
@@ -293,7 +464,7 @@ def get_msg_title_repr(title: str, *, bold: bool = False) -> str:
293
464
 
294
465
  Args:
295
466
  title: The title.
296
- bold: Whether to bold the title. Default is False.
467
+ bold: Whether to bold the title.
297
468
 
298
469
  Returns:
299
470
  The title representation.
@@ -0,0 +1,111 @@
1
+ """Derivations of standard content blocks from provider content.
2
+
3
+ `AIMessage` will first attempt to use a provider-specific translator if
4
+ `model_provider` is set in `response_metadata` on the message. Consequently, each
5
+ provider translator must handle all possible content response types from the provider,
6
+ including text.
7
+
8
+ If no provider is set, or if the provider does not have a registered translator,
9
+ `AIMessage` will fall back to best-effort parsing of the content into blocks using
10
+ the implementation in `BaseMessage`.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from collections.abc import Callable
16
+ from typing import TYPE_CHECKING
17
+
18
+ if TYPE_CHECKING:
19
+ from langchain_core.messages import AIMessage, AIMessageChunk
20
+ from langchain_core.messages import content as types
21
+
22
+ # Provider to translator mapping
23
+ PROVIDER_TRANSLATORS: dict[str, dict[str, Callable[..., list[types.ContentBlock]]]] = {}
24
+ """Map model provider names to translator functions.
25
+
26
+ The dictionary maps provider names (e.g. `'openai'`, `'anthropic'`) to another
27
+ dictionary with two keys:
28
+ - `'translate_content'`: Function to translate `AIMessage` content.
29
+ - `'translate_content_chunk'`: Function to translate `AIMessageChunk` content.
30
+
31
+ When calling `content_blocks` on an `AIMessage` or `AIMessageChunk`, if
32
+ `model_provider` is set in `response_metadata`, the corresponding translator
33
+ functions will be used to parse the content into blocks. Otherwise, best-effort parsing
34
+ in `BaseMessage` will be used.
35
+ """
36
+
37
+
38
+ def register_translator(
39
+ provider: str,
40
+ translate_content: Callable[[AIMessage], list[types.ContentBlock]],
41
+ translate_content_chunk: Callable[[AIMessageChunk], list[types.ContentBlock]],
42
+ ) -> None:
43
+ """Register content translators for a provider in `PROVIDER_TRANSLATORS`.
44
+
45
+ Args:
46
+ provider: The model provider name (e.g. `'openai'`, `'anthropic'`).
47
+ translate_content: Function to translate `AIMessage` content.
48
+ translate_content_chunk: Function to translate `AIMessageChunk` content.
49
+ """
50
+ PROVIDER_TRANSLATORS[provider] = {
51
+ "translate_content": translate_content,
52
+ "translate_content_chunk": translate_content_chunk,
53
+ }
54
+
55
+
56
+ def get_translator(
57
+ provider: str,
58
+ ) -> dict[str, Callable[..., list[types.ContentBlock]]] | None:
59
+ """Get the translator functions for a provider.
60
+
61
+ Args:
62
+ provider: The model provider name.
63
+
64
+ Returns:
65
+ Dictionary with `'translate_content'` and `'translate_content_chunk'`
66
+ functions, or None if no translator is registered for the provider. In such
67
+ case, best-effort parsing in `BaseMessage` will be used.
68
+ """
69
+ return PROVIDER_TRANSLATORS.get(provider)
70
+
71
+
72
+ def _register_translators() -> None:
73
+ """Register all translators in langchain-core.
74
+
75
+ A unit test ensures all modules in `block_translators` are represented here.
76
+
77
+ For translators implemented outside langchain-core, they can be registered by
78
+ calling `register_translator` from within the integration package.
79
+ """
80
+ from langchain_core.messages.block_translators.anthropic import ( # noqa: PLC0415
81
+ _register_anthropic_translator,
82
+ )
83
+ from langchain_core.messages.block_translators.bedrock import ( # noqa: PLC0415
84
+ _register_bedrock_translator,
85
+ )
86
+ from langchain_core.messages.block_translators.bedrock_converse import ( # noqa: PLC0415
87
+ _register_bedrock_converse_translator,
88
+ )
89
+ from langchain_core.messages.block_translators.google_genai import ( # noqa: PLC0415
90
+ _register_google_genai_translator,
91
+ )
92
+ from langchain_core.messages.block_translators.google_vertexai import ( # noqa: PLC0415
93
+ _register_google_vertexai_translator,
94
+ )
95
+ from langchain_core.messages.block_translators.groq import ( # noqa: PLC0415
96
+ _register_groq_translator,
97
+ )
98
+ from langchain_core.messages.block_translators.openai import ( # noqa: PLC0415
99
+ _register_openai_translator,
100
+ )
101
+
102
+ _register_bedrock_translator()
103
+ _register_bedrock_converse_translator()
104
+ _register_anthropic_translator()
105
+ _register_google_genai_translator()
106
+ _register_google_vertexai_translator()
107
+ _register_groq_translator()
108
+ _register_openai_translator()
109
+
110
+
111
+ _register_translators()