langchain-core 0.4.0.dev0__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 (172) hide show
  1. langchain_core/__init__.py +1 -1
  2. langchain_core/_api/__init__.py +3 -4
  3. langchain_core/_api/beta_decorator.py +45 -70
  4. langchain_core/_api/deprecation.py +80 -80
  5. langchain_core/_api/path.py +22 -8
  6. langchain_core/_import_utils.py +10 -4
  7. langchain_core/agents.py +25 -21
  8. langchain_core/caches.py +53 -63
  9. langchain_core/callbacks/__init__.py +1 -8
  10. langchain_core/callbacks/base.py +341 -348
  11. langchain_core/callbacks/file.py +55 -44
  12. langchain_core/callbacks/manager.py +546 -683
  13. langchain_core/callbacks/stdout.py +29 -30
  14. langchain_core/callbacks/streaming_stdout.py +35 -36
  15. langchain_core/callbacks/usage.py +65 -70
  16. langchain_core/chat_history.py +48 -55
  17. langchain_core/document_loaders/base.py +46 -21
  18. langchain_core/document_loaders/langsmith.py +39 -36
  19. langchain_core/documents/__init__.py +0 -1
  20. langchain_core/documents/base.py +96 -74
  21. langchain_core/documents/compressor.py +12 -9
  22. langchain_core/documents/transformers.py +29 -28
  23. langchain_core/embeddings/fake.py +56 -57
  24. langchain_core/env.py +2 -3
  25. langchain_core/example_selectors/base.py +12 -0
  26. langchain_core/example_selectors/length_based.py +1 -1
  27. langchain_core/example_selectors/semantic_similarity.py +21 -25
  28. langchain_core/exceptions.py +15 -9
  29. langchain_core/globals.py +4 -163
  30. langchain_core/indexing/api.py +132 -125
  31. langchain_core/indexing/base.py +64 -67
  32. langchain_core/indexing/in_memory.py +26 -6
  33. langchain_core/language_models/__init__.py +15 -27
  34. langchain_core/language_models/_utils.py +267 -117
  35. langchain_core/language_models/base.py +92 -177
  36. langchain_core/language_models/chat_models.py +547 -407
  37. langchain_core/language_models/fake.py +11 -11
  38. langchain_core/language_models/fake_chat_models.py +72 -118
  39. langchain_core/language_models/llms.py +168 -242
  40. langchain_core/load/dump.py +8 -11
  41. langchain_core/load/load.py +32 -28
  42. langchain_core/load/mapping.py +2 -4
  43. langchain_core/load/serializable.py +50 -56
  44. langchain_core/messages/__init__.py +36 -51
  45. langchain_core/messages/ai.py +377 -150
  46. langchain_core/messages/base.py +239 -47
  47. langchain_core/messages/block_translators/__init__.py +111 -0
  48. langchain_core/messages/block_translators/anthropic.py +470 -0
  49. langchain_core/messages/block_translators/bedrock.py +94 -0
  50. langchain_core/messages/block_translators/bedrock_converse.py +297 -0
  51. langchain_core/messages/block_translators/google_genai.py +530 -0
  52. langchain_core/messages/block_translators/google_vertexai.py +21 -0
  53. langchain_core/messages/block_translators/groq.py +143 -0
  54. langchain_core/messages/block_translators/langchain_v0.py +301 -0
  55. langchain_core/messages/block_translators/openai.py +1010 -0
  56. langchain_core/messages/chat.py +2 -3
  57. langchain_core/messages/content.py +1423 -0
  58. langchain_core/messages/function.py +7 -7
  59. langchain_core/messages/human.py +44 -38
  60. langchain_core/messages/modifier.py +3 -2
  61. langchain_core/messages/system.py +40 -27
  62. langchain_core/messages/tool.py +160 -58
  63. langchain_core/messages/utils.py +527 -638
  64. langchain_core/output_parsers/__init__.py +1 -14
  65. langchain_core/output_parsers/base.py +68 -104
  66. langchain_core/output_parsers/json.py +13 -17
  67. langchain_core/output_parsers/list.py +11 -33
  68. langchain_core/output_parsers/openai_functions.py +56 -74
  69. langchain_core/output_parsers/openai_tools.py +68 -109
  70. langchain_core/output_parsers/pydantic.py +15 -13
  71. langchain_core/output_parsers/string.py +6 -2
  72. langchain_core/output_parsers/transform.py +17 -60
  73. langchain_core/output_parsers/xml.py +34 -44
  74. langchain_core/outputs/__init__.py +1 -1
  75. langchain_core/outputs/chat_generation.py +26 -11
  76. langchain_core/outputs/chat_result.py +1 -3
  77. langchain_core/outputs/generation.py +17 -6
  78. langchain_core/outputs/llm_result.py +15 -8
  79. langchain_core/prompt_values.py +29 -123
  80. langchain_core/prompts/__init__.py +3 -27
  81. langchain_core/prompts/base.py +48 -63
  82. langchain_core/prompts/chat.py +259 -288
  83. langchain_core/prompts/dict.py +19 -11
  84. langchain_core/prompts/few_shot.py +84 -90
  85. langchain_core/prompts/few_shot_with_templates.py +14 -12
  86. langchain_core/prompts/image.py +19 -14
  87. langchain_core/prompts/loading.py +6 -8
  88. langchain_core/prompts/message.py +7 -8
  89. langchain_core/prompts/prompt.py +42 -43
  90. langchain_core/prompts/string.py +37 -16
  91. langchain_core/prompts/structured.py +43 -46
  92. langchain_core/rate_limiters.py +51 -60
  93. langchain_core/retrievers.py +52 -192
  94. langchain_core/runnables/base.py +1727 -1683
  95. langchain_core/runnables/branch.py +52 -73
  96. langchain_core/runnables/config.py +89 -103
  97. langchain_core/runnables/configurable.py +128 -130
  98. langchain_core/runnables/fallbacks.py +93 -82
  99. langchain_core/runnables/graph.py +127 -127
  100. langchain_core/runnables/graph_ascii.py +63 -41
  101. langchain_core/runnables/graph_mermaid.py +87 -70
  102. langchain_core/runnables/graph_png.py +31 -36
  103. langchain_core/runnables/history.py +145 -161
  104. langchain_core/runnables/passthrough.py +141 -144
  105. langchain_core/runnables/retry.py +84 -68
  106. langchain_core/runnables/router.py +33 -37
  107. langchain_core/runnables/schema.py +79 -72
  108. langchain_core/runnables/utils.py +95 -139
  109. langchain_core/stores.py +85 -131
  110. langchain_core/structured_query.py +11 -15
  111. langchain_core/sys_info.py +31 -32
  112. langchain_core/tools/__init__.py +1 -14
  113. langchain_core/tools/base.py +221 -247
  114. langchain_core/tools/convert.py +144 -161
  115. langchain_core/tools/render.py +10 -10
  116. langchain_core/tools/retriever.py +12 -19
  117. langchain_core/tools/simple.py +52 -29
  118. langchain_core/tools/structured.py +56 -60
  119. langchain_core/tracers/__init__.py +1 -9
  120. langchain_core/tracers/_streaming.py +6 -7
  121. langchain_core/tracers/base.py +103 -112
  122. langchain_core/tracers/context.py +29 -48
  123. langchain_core/tracers/core.py +142 -105
  124. langchain_core/tracers/evaluation.py +30 -34
  125. langchain_core/tracers/event_stream.py +162 -117
  126. langchain_core/tracers/langchain.py +34 -36
  127. langchain_core/tracers/log_stream.py +87 -49
  128. langchain_core/tracers/memory_stream.py +3 -3
  129. langchain_core/tracers/root_listeners.py +18 -34
  130. langchain_core/tracers/run_collector.py +8 -20
  131. langchain_core/tracers/schemas.py +0 -125
  132. langchain_core/tracers/stdout.py +3 -3
  133. langchain_core/utils/__init__.py +1 -4
  134. langchain_core/utils/_merge.py +47 -9
  135. langchain_core/utils/aiter.py +70 -66
  136. langchain_core/utils/env.py +12 -9
  137. langchain_core/utils/function_calling.py +139 -206
  138. langchain_core/utils/html.py +7 -8
  139. langchain_core/utils/input.py +6 -6
  140. langchain_core/utils/interactive_env.py +6 -2
  141. langchain_core/utils/iter.py +48 -45
  142. langchain_core/utils/json.py +14 -4
  143. langchain_core/utils/json_schema.py +159 -43
  144. langchain_core/utils/mustache.py +32 -25
  145. langchain_core/utils/pydantic.py +67 -40
  146. langchain_core/utils/strings.py +5 -5
  147. langchain_core/utils/usage.py +1 -1
  148. langchain_core/utils/utils.py +104 -62
  149. langchain_core/vectorstores/base.py +131 -179
  150. langchain_core/vectorstores/in_memory.py +113 -182
  151. langchain_core/vectorstores/utils.py +23 -17
  152. langchain_core/version.py +1 -1
  153. langchain_core-1.0.0.dist-info/METADATA +68 -0
  154. langchain_core-1.0.0.dist-info/RECORD +172 -0
  155. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0.dist-info}/WHEEL +1 -1
  156. langchain_core/beta/__init__.py +0 -1
  157. langchain_core/beta/runnables/__init__.py +0 -1
  158. langchain_core/beta/runnables/context.py +0 -448
  159. langchain_core/memory.py +0 -116
  160. langchain_core/messages/content_blocks.py +0 -1435
  161. langchain_core/prompts/pipeline.py +0 -133
  162. langchain_core/pydantic_v1/__init__.py +0 -30
  163. langchain_core/pydantic_v1/dataclasses.py +0 -23
  164. langchain_core/pydantic_v1/main.py +0 -23
  165. langchain_core/tracers/langchain_v1.py +0 -23
  166. langchain_core/utils/loading.py +0 -31
  167. langchain_core/v1/__init__.py +0 -1
  168. langchain_core/v1/chat_models.py +0 -1047
  169. langchain_core/v1/messages.py +0 -755
  170. langchain_core-0.4.0.dev0.dist-info/METADATA +0 -108
  171. langchain_core-0.4.0.dev0.dist-info/RECORD +0 -177
  172. langchain_core-0.4.0.dev0.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,63 +20,162 @@ 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 ChatModels.
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.
31
103
 
32
104
  For example, for a message from an AI, this could include tool calls as
33
105
  encoded by the model provider.
106
+
34
107
  """
35
108
 
36
109
  response_metadata: dict = Field(default_factory=dict)
37
- """Response metadata. For example: response headers, logprobs, token counts, model
38
- name."""
110
+ """Examples: response headers, logprobs, token counts, model name."""
39
111
 
40
112
  type: str
41
113
  """The type of the message. Must be a string that is unique to the message type.
42
114
 
43
115
  The purpose of this field is to allow for easy identification of the message type
44
116
  when deserializing messages.
117
+
45
118
  """
46
119
 
47
- name: Optional[str] = None
120
+ name: str | None = None
48
121
  """An optional name for the message.
49
122
 
50
123
  This can be used to provide a human-readable name for the message.
51
124
 
52
125
  Usage of this field is optional, and whether it's used or not is up to the
53
126
  model implementation.
127
+
54
128
  """
55
129
 
56
- id: Optional[str] = Field(default=None, coerce_numbers_to_str=True)
57
- """An optional unique identifier for the message. This should ideally be
58
- provided by the provider/model which created the message."""
130
+ id: str | None = Field(default=None, coerce_numbers_to_str=True)
131
+ """An optional unique identifier for the message.
132
+
133
+ This should ideally be provided by the provider/model which created the message.
134
+
135
+ """
59
136
 
60
137
  model_config = ConfigDict(
61
138
  extra="allow",
62
139
  )
63
140
 
141
+ @overload
142
+ def __init__(
143
+ self,
144
+ content: str | list[str | dict],
145
+ **kwargs: Any,
146
+ ) -> None: ...
147
+
148
+ @overload
64
149
  def __init__(
65
- self, content: Union[str, list[Union[str, dict]]], **kwargs: Any
150
+ self,
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,
160
+ **kwargs: Any,
66
161
  ) -> None:
67
- """Pass in content as positional arg.
162
+ """Initialize a `BaseMessage`.
163
+
164
+ Specify `content` as positional arg or `content_blocks` for typing.
68
165
 
69
166
  Args:
70
- 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.
71
170
  """
72
- 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)
73
175
 
74
176
  @classmethod
75
177
  def is_lc_serializable(cls) -> bool:
76
- """BaseMessage is serializable.
178
+ """`BaseMessage` is serializable.
77
179
 
78
180
  Returns:
79
181
  True
@@ -82,35 +184,117 @@ class BaseMessage(Serializable):
82
184
 
83
185
  @classmethod
84
186
  def get_lc_namespace(cls) -> list[str]:
85
- """Get the namespace of the langchain object.
187
+ """Get the namespace of the LangChain object.
86
188
 
87
- Default is ["langchain", "schema", "messages"].
189
+ Returns:
190
+ `["langchain", "schema", "messages"]`
88
191
  """
89
192
  return ["langchain", "schema", "messages"]
90
193
 
91
- def text(self) -> str:
92
- """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.
93
267
 
94
268
  Returns:
95
269
  The text content of the message.
270
+
96
271
  """
97
272
  if isinstance(self.content, str):
98
- return self.content
99
-
100
- # must be a list
101
- blocks = [
102
- block
103
- for block in self.content
104
- if isinstance(block, str)
105
- or (block.get("type") == "text" and isinstance(block.get("text"), str))
106
- ]
107
- return "".join(
108
- block if isinstance(block, str) else block["text"] for block in blocks
109
- )
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)
110
286
 
111
287
  def __add__(self, other: Any) -> ChatPromptTemplate:
112
- """Concatenate this message with another message."""
113
- from langchain_core.prompts.chat import ChatPromptTemplate
288
+ """Concatenate this message with another message.
289
+
290
+ Args:
291
+ other: Another message to concatenate with this one.
292
+
293
+ Returns:
294
+ A ChatPromptTemplate containing both messages.
295
+ """
296
+ # Import locally to prevent circular imports.
297
+ from langchain_core.prompts.chat import ChatPromptTemplate # noqa: PLC0415
114
298
 
115
299
  prompt = ChatPromptTemplate(messages=[self])
116
300
  return prompt + other
@@ -122,11 +306,12 @@ class BaseMessage(Serializable):
122
306
  """Get a pretty representation of the message.
123
307
 
124
308
  Args:
125
- html: Whether to format the message as HTML. If True, the message will be
126
- 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.
127
311
 
128
312
  Returns:
129
313
  A pretty representation of the message.
314
+
130
315
  """
131
316
  title = get_msg_title_repr(self.type.title() + " Message", bold=html)
132
317
  # TODO: handle non-string content.
@@ -140,19 +325,22 @@ class BaseMessage(Serializable):
140
325
 
141
326
 
142
327
  def merge_content(
143
- first_content: Union[str, list[Union[str, dict]]],
144
- *contents: Union[str, list[Union[str, dict]]],
145
- ) -> Union[str, list[Union[str, dict]]]:
328
+ first_content: str | list[str | dict],
329
+ *contents: str | list[str | dict],
330
+ ) -> str | list[str | dict]:
146
331
  """Merge multiple message contents.
147
332
 
148
333
  Args:
149
- first_content: The first content. Can be a string or a list.
150
- contents: The other contents. 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.
151
336
 
152
337
  Returns:
153
338
  The merged content.
339
+
154
340
  """
155
- merged = first_content
341
+ merged: str | list[str | dict]
342
+ merged = "" if first_content is None else first_content
343
+
156
344
  for content in contents:
157
345
  # If current is a string
158
346
  if isinstance(merged, str):
@@ -173,8 +361,8 @@ def merge_content(
173
361
  # If second content is an empty string, treat as a no-op
174
362
  elif content == "":
175
363
  pass
176
- else:
177
- # Otherwise, add the second content as a new element of the list
364
+ # Otherwise, add the second content as a new element of the list
365
+ elif merged:
178
366
  merged.append(content)
179
367
  return merged
180
368
 
@@ -203,6 +391,7 @@ class BaseMessageChunk(BaseMessage):
203
391
  `AIMessageChunk(content="Hello") + AIMessageChunk(content=" World")`
204
392
 
205
393
  will give `AIMessageChunk(content="Hello World")`
394
+
206
395
  """
207
396
  if isinstance(other, BaseMessageChunk):
208
397
  # If both are (subclasses of) BaseMessageChunk,
@@ -250,8 +439,9 @@ def message_to_dict(message: BaseMessage) -> dict:
250
439
  message: Message to convert.
251
440
 
252
441
  Returns:
253
- Message as a dict. The dict will have a "type" key with the message type
254
- 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.
444
+
255
445
  """
256
446
  return {"type": message.type, "data": message.model_dump()}
257
447
 
@@ -260,10 +450,11 @@ def messages_to_dict(messages: Sequence[BaseMessage]) -> list[dict]:
260
450
  """Convert a sequence of Messages to a list of dictionaries.
261
451
 
262
452
  Args:
263
- messages: Sequence of messages (as BaseMessages) to convert.
453
+ messages: Sequence of messages (as `BaseMessage`s) to convert.
264
454
 
265
455
  Returns:
266
456
  List of messages as dicts.
457
+
267
458
  """
268
459
  return [message_to_dict(m) for m in messages]
269
460
 
@@ -273,10 +464,11 @@ def get_msg_title_repr(title: str, *, bold: bool = False) -> str:
273
464
 
274
465
  Args:
275
466
  title: The title.
276
- bold: Whether to bold the title. Default is False.
467
+ bold: Whether to bold the title.
277
468
 
278
469
  Returns:
279
470
  The title representation.
471
+
280
472
  """
281
473
  padded = " " + title + " "
282
474
  sep_len = (80 - len(padded)) // 2
@@ -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()