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.
- langchain_core/__init__.py +1 -1
- langchain_core/_api/__init__.py +3 -4
- langchain_core/_api/beta_decorator.py +45 -70
- langchain_core/_api/deprecation.py +80 -80
- langchain_core/_api/path.py +22 -8
- langchain_core/_import_utils.py +10 -4
- langchain_core/agents.py +25 -21
- langchain_core/caches.py +53 -63
- langchain_core/callbacks/__init__.py +1 -8
- langchain_core/callbacks/base.py +341 -348
- langchain_core/callbacks/file.py +55 -44
- langchain_core/callbacks/manager.py +546 -683
- langchain_core/callbacks/stdout.py +29 -30
- langchain_core/callbacks/streaming_stdout.py +35 -36
- langchain_core/callbacks/usage.py +65 -70
- langchain_core/chat_history.py +48 -55
- langchain_core/document_loaders/base.py +46 -21
- langchain_core/document_loaders/langsmith.py +39 -36
- langchain_core/documents/__init__.py +0 -1
- langchain_core/documents/base.py +96 -74
- langchain_core/documents/compressor.py +12 -9
- langchain_core/documents/transformers.py +29 -28
- langchain_core/embeddings/fake.py +56 -57
- langchain_core/env.py +2 -3
- langchain_core/example_selectors/base.py +12 -0
- langchain_core/example_selectors/length_based.py +1 -1
- langchain_core/example_selectors/semantic_similarity.py +21 -25
- langchain_core/exceptions.py +15 -9
- langchain_core/globals.py +4 -163
- langchain_core/indexing/api.py +132 -125
- langchain_core/indexing/base.py +64 -67
- langchain_core/indexing/in_memory.py +26 -6
- langchain_core/language_models/__init__.py +15 -27
- langchain_core/language_models/_utils.py +267 -117
- langchain_core/language_models/base.py +92 -177
- langchain_core/language_models/chat_models.py +547 -407
- langchain_core/language_models/fake.py +11 -11
- langchain_core/language_models/fake_chat_models.py +72 -118
- langchain_core/language_models/llms.py +168 -242
- langchain_core/load/dump.py +8 -11
- langchain_core/load/load.py +32 -28
- langchain_core/load/mapping.py +2 -4
- langchain_core/load/serializable.py +50 -56
- langchain_core/messages/__init__.py +36 -51
- langchain_core/messages/ai.py +377 -150
- langchain_core/messages/base.py +239 -47
- langchain_core/messages/block_translators/__init__.py +111 -0
- langchain_core/messages/block_translators/anthropic.py +470 -0
- langchain_core/messages/block_translators/bedrock.py +94 -0
- langchain_core/messages/block_translators/bedrock_converse.py +297 -0
- langchain_core/messages/block_translators/google_genai.py +530 -0
- langchain_core/messages/block_translators/google_vertexai.py +21 -0
- langchain_core/messages/block_translators/groq.py +143 -0
- langchain_core/messages/block_translators/langchain_v0.py +301 -0
- langchain_core/messages/block_translators/openai.py +1010 -0
- langchain_core/messages/chat.py +2 -3
- langchain_core/messages/content.py +1423 -0
- langchain_core/messages/function.py +7 -7
- langchain_core/messages/human.py +44 -38
- langchain_core/messages/modifier.py +3 -2
- langchain_core/messages/system.py +40 -27
- langchain_core/messages/tool.py +160 -58
- langchain_core/messages/utils.py +527 -638
- langchain_core/output_parsers/__init__.py +1 -14
- langchain_core/output_parsers/base.py +68 -104
- langchain_core/output_parsers/json.py +13 -17
- langchain_core/output_parsers/list.py +11 -33
- langchain_core/output_parsers/openai_functions.py +56 -74
- langchain_core/output_parsers/openai_tools.py +68 -109
- langchain_core/output_parsers/pydantic.py +15 -13
- langchain_core/output_parsers/string.py +6 -2
- langchain_core/output_parsers/transform.py +17 -60
- langchain_core/output_parsers/xml.py +34 -44
- langchain_core/outputs/__init__.py +1 -1
- langchain_core/outputs/chat_generation.py +26 -11
- langchain_core/outputs/chat_result.py +1 -3
- langchain_core/outputs/generation.py +17 -6
- langchain_core/outputs/llm_result.py +15 -8
- langchain_core/prompt_values.py +29 -123
- langchain_core/prompts/__init__.py +3 -27
- langchain_core/prompts/base.py +48 -63
- langchain_core/prompts/chat.py +259 -288
- langchain_core/prompts/dict.py +19 -11
- langchain_core/prompts/few_shot.py +84 -90
- langchain_core/prompts/few_shot_with_templates.py +14 -12
- langchain_core/prompts/image.py +19 -14
- langchain_core/prompts/loading.py +6 -8
- langchain_core/prompts/message.py +7 -8
- langchain_core/prompts/prompt.py +42 -43
- langchain_core/prompts/string.py +37 -16
- langchain_core/prompts/structured.py +43 -46
- langchain_core/rate_limiters.py +51 -60
- langchain_core/retrievers.py +52 -192
- langchain_core/runnables/base.py +1727 -1683
- langchain_core/runnables/branch.py +52 -73
- langchain_core/runnables/config.py +89 -103
- langchain_core/runnables/configurable.py +128 -130
- langchain_core/runnables/fallbacks.py +93 -82
- langchain_core/runnables/graph.py +127 -127
- langchain_core/runnables/graph_ascii.py +63 -41
- langchain_core/runnables/graph_mermaid.py +87 -70
- langchain_core/runnables/graph_png.py +31 -36
- langchain_core/runnables/history.py +145 -161
- langchain_core/runnables/passthrough.py +141 -144
- langchain_core/runnables/retry.py +84 -68
- langchain_core/runnables/router.py +33 -37
- langchain_core/runnables/schema.py +79 -72
- langchain_core/runnables/utils.py +95 -139
- langchain_core/stores.py +85 -131
- langchain_core/structured_query.py +11 -15
- langchain_core/sys_info.py +31 -32
- langchain_core/tools/__init__.py +1 -14
- langchain_core/tools/base.py +221 -247
- langchain_core/tools/convert.py +144 -161
- langchain_core/tools/render.py +10 -10
- langchain_core/tools/retriever.py +12 -19
- langchain_core/tools/simple.py +52 -29
- langchain_core/tools/structured.py +56 -60
- langchain_core/tracers/__init__.py +1 -9
- langchain_core/tracers/_streaming.py +6 -7
- langchain_core/tracers/base.py +103 -112
- langchain_core/tracers/context.py +29 -48
- langchain_core/tracers/core.py +142 -105
- langchain_core/tracers/evaluation.py +30 -34
- langchain_core/tracers/event_stream.py +162 -117
- langchain_core/tracers/langchain.py +34 -36
- langchain_core/tracers/log_stream.py +87 -49
- langchain_core/tracers/memory_stream.py +3 -3
- langchain_core/tracers/root_listeners.py +18 -34
- langchain_core/tracers/run_collector.py +8 -20
- langchain_core/tracers/schemas.py +0 -125
- langchain_core/tracers/stdout.py +3 -3
- langchain_core/utils/__init__.py +1 -4
- langchain_core/utils/_merge.py +47 -9
- langchain_core/utils/aiter.py +70 -66
- langchain_core/utils/env.py +12 -9
- langchain_core/utils/function_calling.py +139 -206
- langchain_core/utils/html.py +7 -8
- langchain_core/utils/input.py +6 -6
- langchain_core/utils/interactive_env.py +6 -2
- langchain_core/utils/iter.py +48 -45
- langchain_core/utils/json.py +14 -4
- langchain_core/utils/json_schema.py +159 -43
- langchain_core/utils/mustache.py +32 -25
- langchain_core/utils/pydantic.py +67 -40
- langchain_core/utils/strings.py +5 -5
- langchain_core/utils/usage.py +1 -1
- langchain_core/utils/utils.py +104 -62
- langchain_core/vectorstores/base.py +131 -179
- langchain_core/vectorstores/in_memory.py +113 -182
- langchain_core/vectorstores/utils.py +23 -17
- langchain_core/version.py +1 -1
- langchain_core-1.0.0.dist-info/METADATA +68 -0
- langchain_core-1.0.0.dist-info/RECORD +172 -0
- {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0.dist-info}/WHEEL +1 -1
- langchain_core/beta/__init__.py +0 -1
- langchain_core/beta/runnables/__init__.py +0 -1
- langchain_core/beta/runnables/context.py +0 -448
- langchain_core/memory.py +0 -116
- langchain_core/messages/content_blocks.py +0 -1435
- langchain_core/prompts/pipeline.py +0 -133
- langchain_core/pydantic_v1/__init__.py +0 -30
- langchain_core/pydantic_v1/dataclasses.py +0 -23
- langchain_core/pydantic_v1/main.py +0 -23
- langchain_core/tracers/langchain_v1.py +0 -23
- langchain_core/utils/loading.py +0 -31
- langchain_core/v1/__init__.py +0 -1
- langchain_core/v1/chat_models.py +0 -1047
- langchain_core/v1/messages.py +0 -755
- langchain_core-0.4.0.dev0.dist-info/METADATA +0 -108
- langchain_core-0.4.0.dev0.dist-info/RECORD +0 -177
- langchain_core-0.4.0.dev0.dist-info/entry_points.txt +0 -4
langchain_core/messages/base.py
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING, Any,
|
|
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
|
|
95
|
+
Messages are the inputs and outputs of a chat model.
|
|
24
96
|
"""
|
|
25
97
|
|
|
26
|
-
content:
|
|
27
|
-
"""The
|
|
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
|
-
"""
|
|
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:
|
|
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:
|
|
57
|
-
"""An optional unique identifier for the message.
|
|
58
|
-
|
|
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,
|
|
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
|
-
"""
|
|
162
|
+
"""Initialize a `BaseMessage`.
|
|
163
|
+
|
|
164
|
+
Specify `content` as positional arg or `content_blocks` for typing.
|
|
68
165
|
|
|
69
166
|
Args:
|
|
70
|
-
content: The
|
|
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
|
-
|
|
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
|
|
187
|
+
"""Get the namespace of the LangChain object.
|
|
86
188
|
|
|
87
|
-
|
|
189
|
+
Returns:
|
|
190
|
+
`["langchain", "schema", "messages"]`
|
|
88
191
|
"""
|
|
89
192
|
return ["langchain", "schema", "messages"]
|
|
90
193
|
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
|
126
|
-
formatted with HTML tags.
|
|
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:
|
|
144
|
-
*contents:
|
|
145
|
-
) ->
|
|
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
|
|
150
|
-
contents: The other
|
|
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
|
|
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
|
-
|
|
177
|
-
|
|
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
|
|
254
|
-
and a
|
|
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
|
|
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.
|
|
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()
|