langchain-core 1.0.0a6__py3-none-any.whl → 1.0.4__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.
- langchain_core/__init__.py +1 -1
- langchain_core/_api/__init__.py +3 -4
- langchain_core/_api/beta_decorator.py +23 -26
- langchain_core/_api/deprecation.py +51 -64
- langchain_core/_api/path.py +3 -6
- langchain_core/_import_utils.py +3 -4
- langchain_core/agents.py +55 -48
- langchain_core/caches.py +65 -66
- langchain_core/callbacks/__init__.py +1 -8
- langchain_core/callbacks/base.py +321 -336
- langchain_core/callbacks/file.py +44 -44
- langchain_core/callbacks/manager.py +454 -514
- langchain_core/callbacks/stdout.py +29 -30
- langchain_core/callbacks/streaming_stdout.py +32 -32
- langchain_core/callbacks/usage.py +60 -57
- langchain_core/chat_history.py +53 -68
- langchain_core/document_loaders/base.py +27 -25
- langchain_core/document_loaders/blob_loaders.py +1 -1
- langchain_core/document_loaders/langsmith.py +44 -48
- langchain_core/documents/__init__.py +23 -3
- langchain_core/documents/base.py +102 -94
- langchain_core/documents/compressor.py +10 -10
- langchain_core/documents/transformers.py +34 -35
- langchain_core/embeddings/fake.py +50 -54
- langchain_core/example_selectors/length_based.py +2 -2
- langchain_core/example_selectors/semantic_similarity.py +28 -32
- langchain_core/exceptions.py +21 -20
- langchain_core/globals.py +3 -151
- langchain_core/indexing/__init__.py +1 -1
- langchain_core/indexing/api.py +121 -126
- langchain_core/indexing/base.py +73 -75
- langchain_core/indexing/in_memory.py +4 -6
- langchain_core/language_models/__init__.py +14 -29
- langchain_core/language_models/_utils.py +58 -61
- langchain_core/language_models/base.py +82 -172
- langchain_core/language_models/chat_models.py +329 -402
- langchain_core/language_models/fake.py +11 -11
- langchain_core/language_models/fake_chat_models.py +42 -36
- langchain_core/language_models/llms.py +189 -269
- langchain_core/load/dump.py +9 -12
- langchain_core/load/load.py +18 -28
- langchain_core/load/mapping.py +2 -4
- langchain_core/load/serializable.py +42 -40
- langchain_core/messages/__init__.py +10 -16
- langchain_core/messages/ai.py +148 -148
- langchain_core/messages/base.py +53 -51
- langchain_core/messages/block_translators/__init__.py +19 -22
- langchain_core/messages/block_translators/anthropic.py +6 -6
- langchain_core/messages/block_translators/bedrock_converse.py +5 -5
- langchain_core/messages/block_translators/google_genai.py +10 -7
- langchain_core/messages/block_translators/google_vertexai.py +4 -32
- langchain_core/messages/block_translators/groq.py +117 -21
- langchain_core/messages/block_translators/langchain_v0.py +5 -5
- langchain_core/messages/block_translators/openai.py +11 -11
- langchain_core/messages/chat.py +2 -6
- langchain_core/messages/content.py +339 -330
- langchain_core/messages/function.py +6 -10
- langchain_core/messages/human.py +24 -31
- langchain_core/messages/modifier.py +2 -2
- langchain_core/messages/system.py +19 -29
- langchain_core/messages/tool.py +74 -90
- langchain_core/messages/utils.py +484 -510
- langchain_core/output_parsers/__init__.py +13 -10
- langchain_core/output_parsers/base.py +61 -61
- langchain_core/output_parsers/format_instructions.py +9 -4
- langchain_core/output_parsers/json.py +12 -10
- langchain_core/output_parsers/list.py +21 -23
- langchain_core/output_parsers/openai_functions.py +49 -47
- langchain_core/output_parsers/openai_tools.py +30 -23
- langchain_core/output_parsers/pydantic.py +13 -14
- langchain_core/output_parsers/string.py +5 -5
- langchain_core/output_parsers/transform.py +15 -17
- langchain_core/output_parsers/xml.py +35 -34
- langchain_core/outputs/__init__.py +1 -1
- langchain_core/outputs/chat_generation.py +18 -18
- langchain_core/outputs/chat_result.py +1 -3
- langchain_core/outputs/generation.py +16 -16
- langchain_core/outputs/llm_result.py +10 -10
- langchain_core/prompt_values.py +13 -19
- langchain_core/prompts/__init__.py +3 -27
- langchain_core/prompts/base.py +81 -86
- langchain_core/prompts/chat.py +308 -351
- langchain_core/prompts/dict.py +6 -6
- langchain_core/prompts/few_shot.py +81 -88
- langchain_core/prompts/few_shot_with_templates.py +11 -13
- langchain_core/prompts/image.py +12 -14
- langchain_core/prompts/loading.py +4 -6
- langchain_core/prompts/message.py +7 -7
- langchain_core/prompts/prompt.py +24 -39
- langchain_core/prompts/string.py +26 -10
- langchain_core/prompts/structured.py +49 -53
- langchain_core/rate_limiters.py +51 -60
- langchain_core/retrievers.py +61 -198
- langchain_core/runnables/base.py +1551 -1656
- langchain_core/runnables/branch.py +68 -70
- langchain_core/runnables/config.py +72 -89
- langchain_core/runnables/configurable.py +145 -161
- langchain_core/runnables/fallbacks.py +102 -96
- langchain_core/runnables/graph.py +91 -97
- langchain_core/runnables/graph_ascii.py +27 -28
- langchain_core/runnables/graph_mermaid.py +42 -51
- langchain_core/runnables/graph_png.py +43 -16
- langchain_core/runnables/history.py +175 -177
- langchain_core/runnables/passthrough.py +151 -167
- langchain_core/runnables/retry.py +46 -51
- langchain_core/runnables/router.py +30 -35
- langchain_core/runnables/schema.py +75 -80
- langchain_core/runnables/utils.py +60 -67
- langchain_core/stores.py +85 -121
- langchain_core/structured_query.py +8 -8
- langchain_core/sys_info.py +29 -29
- langchain_core/tools/__init__.py +1 -14
- langchain_core/tools/base.py +306 -245
- langchain_core/tools/convert.py +160 -155
- langchain_core/tools/render.py +10 -10
- langchain_core/tools/retriever.py +12 -11
- langchain_core/tools/simple.py +19 -24
- langchain_core/tools/structured.py +32 -39
- langchain_core/tracers/__init__.py +1 -9
- langchain_core/tracers/base.py +97 -99
- langchain_core/tracers/context.py +29 -52
- langchain_core/tracers/core.py +49 -53
- langchain_core/tracers/evaluation.py +11 -11
- langchain_core/tracers/event_stream.py +65 -64
- langchain_core/tracers/langchain.py +21 -21
- langchain_core/tracers/log_stream.py +45 -45
- langchain_core/tracers/memory_stream.py +3 -3
- langchain_core/tracers/root_listeners.py +16 -16
- langchain_core/tracers/run_collector.py +2 -4
- langchain_core/tracers/schemas.py +0 -129
- langchain_core/tracers/stdout.py +3 -3
- langchain_core/utils/__init__.py +1 -4
- langchain_core/utils/_merge.py +2 -2
- langchain_core/utils/aiter.py +57 -61
- langchain_core/utils/env.py +9 -9
- langchain_core/utils/function_calling.py +94 -188
- langchain_core/utils/html.py +7 -8
- langchain_core/utils/input.py +9 -6
- langchain_core/utils/interactive_env.py +1 -1
- langchain_core/utils/iter.py +36 -40
- langchain_core/utils/json.py +4 -3
- langchain_core/utils/json_schema.py +9 -9
- langchain_core/utils/mustache.py +8 -10
- langchain_core/utils/pydantic.py +35 -37
- langchain_core/utils/strings.py +6 -9
- langchain_core/utils/usage.py +1 -1
- langchain_core/utils/utils.py +66 -62
- langchain_core/vectorstores/base.py +182 -216
- langchain_core/vectorstores/in_memory.py +101 -176
- langchain_core/vectorstores/utils.py +5 -5
- langchain_core/version.py +1 -1
- langchain_core-1.0.4.dist-info/METADATA +69 -0
- langchain_core-1.0.4.dist-info/RECORD +172 -0
- {langchain_core-1.0.0a6.dist-info → langchain_core-1.0.4.dist-info}/WHEEL +1 -1
- langchain_core/memory.py +0 -120
- langchain_core/messages/block_translators/ollama.py +0 -47
- langchain_core/prompts/pipeline.py +0 -138
- 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 -31
- langchain_core/utils/loading.py +0 -35
- langchain_core-1.0.0a6.dist-info/METADATA +0 -67
- langchain_core-1.0.0a6.dist-info/RECORD +0 -181
- langchain_core-1.0.0a6.dist-info/entry_points.txt +0 -4
langchain_core/messages/base.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
8
|
from typing_extensions import Self
|
|
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
|
|
|
22
22
|
|
|
23
23
|
def _extract_reasoning_from_additional_kwargs(
|
|
24
24
|
message: BaseMessage,
|
|
25
|
-
) ->
|
|
25
|
+
) -> types.ReasoningContentBlock | None:
|
|
26
26
|
"""Extract `reasoning_content` from `additional_kwargs`.
|
|
27
27
|
|
|
28
28
|
Handles reasoning content stored in various formats:
|
|
@@ -34,13 +34,11 @@ def _extract_reasoning_from_additional_kwargs(
|
|
|
34
34
|
Returns:
|
|
35
35
|
A `ReasoningContentBlock` if reasoning content is found, None otherwise.
|
|
36
36
|
"""
|
|
37
|
-
from langchain_core.messages.content import create_reasoning_block # noqa: PLC0415
|
|
38
|
-
|
|
39
37
|
additional_kwargs = getattr(message, "additional_kwargs", {})
|
|
40
38
|
|
|
41
39
|
reasoning_content = additional_kwargs.get("reasoning_content")
|
|
42
40
|
if reasoning_content is not None and isinstance(reasoning_content, str):
|
|
43
|
-
return
|
|
41
|
+
return {"type": "reasoning", "reasoning": reasoning_content}
|
|
44
42
|
|
|
45
43
|
return None
|
|
46
44
|
|
|
@@ -50,13 +48,13 @@ class TextAccessor(str):
|
|
|
50
48
|
|
|
51
49
|
Exists to maintain backward compatibility while transitioning from method-based to
|
|
52
50
|
property-based text access in message objects. In LangChain <v1.0, message text was
|
|
53
|
-
accessed via
|
|
54
|
-
access via
|
|
51
|
+
accessed via `.text()` method calls. In v1.0=<, the preferred pattern is property
|
|
52
|
+
access via `.text`.
|
|
55
53
|
|
|
56
|
-
Rather than breaking existing code immediately,
|
|
54
|
+
Rather than breaking existing code immediately, `TextAccessor` allows both
|
|
57
55
|
patterns:
|
|
58
|
-
- Modern property access:
|
|
59
|
-
- Legacy method access:
|
|
56
|
+
- Modern property access: `message.text` (returns string directly)
|
|
57
|
+
- Legacy method access: `message.text()` (callable, emits deprecation warning)
|
|
60
58
|
|
|
61
59
|
"""
|
|
62
60
|
|
|
@@ -69,12 +67,12 @@ class TextAccessor(str):
|
|
|
69
67
|
def __call__(self) -> str:
|
|
70
68
|
"""Enable method-style text access for backward compatibility.
|
|
71
69
|
|
|
72
|
-
This method exists solely to support legacy code that calls
|
|
73
|
-
as a method. New code should use property access (
|
|
70
|
+
This method exists solely to support legacy code that calls `.text()`
|
|
71
|
+
as a method. New code should use property access (`.text`) instead.
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
instead. This method will be removed in 2.0.0.
|
|
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.
|
|
78
76
|
|
|
79
77
|
Returns:
|
|
80
78
|
The string content, identical to property access.
|
|
@@ -94,11 +92,15 @@ class TextAccessor(str):
|
|
|
94
92
|
class BaseMessage(Serializable):
|
|
95
93
|
"""Base abstract message class.
|
|
96
94
|
|
|
97
|
-
Messages are the inputs and outputs of
|
|
95
|
+
Messages are the inputs and outputs of a chat model.
|
|
96
|
+
|
|
97
|
+
Examples include [`HumanMessage`][langchain.messages.HumanMessage],
|
|
98
|
+
[`AIMessage`][langchain.messages.AIMessage], and
|
|
99
|
+
[`SystemMessage`][langchain.messages.SystemMessage].
|
|
98
100
|
"""
|
|
99
101
|
|
|
100
|
-
content:
|
|
101
|
-
"""The
|
|
102
|
+
content: str | list[str | dict]
|
|
103
|
+
"""The contents of the message."""
|
|
102
104
|
|
|
103
105
|
additional_kwargs: dict = Field(default_factory=dict)
|
|
104
106
|
"""Reserved for additional payload data associated with the message.
|
|
@@ -119,7 +121,7 @@ class BaseMessage(Serializable):
|
|
|
119
121
|
|
|
120
122
|
"""
|
|
121
123
|
|
|
122
|
-
name:
|
|
124
|
+
name: str | None = None
|
|
123
125
|
"""An optional name for the message.
|
|
124
126
|
|
|
125
127
|
This can be used to provide a human-readable name for the message.
|
|
@@ -129,7 +131,7 @@ class BaseMessage(Serializable):
|
|
|
129
131
|
|
|
130
132
|
"""
|
|
131
133
|
|
|
132
|
-
id:
|
|
134
|
+
id: str | None = Field(default=None, coerce_numbers_to_str=True)
|
|
133
135
|
"""An optional unique identifier for the message.
|
|
134
136
|
|
|
135
137
|
This should ideally be provided by the provider/model which created the message.
|
|
@@ -143,32 +145,32 @@ class BaseMessage(Serializable):
|
|
|
143
145
|
@overload
|
|
144
146
|
def __init__(
|
|
145
147
|
self,
|
|
146
|
-
content:
|
|
148
|
+
content: str | list[str | dict],
|
|
147
149
|
**kwargs: Any,
|
|
148
150
|
) -> None: ...
|
|
149
151
|
|
|
150
152
|
@overload
|
|
151
153
|
def __init__(
|
|
152
154
|
self,
|
|
153
|
-
content:
|
|
154
|
-
content_blocks:
|
|
155
|
+
content: str | list[str | dict] | None = None,
|
|
156
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
155
157
|
**kwargs: Any,
|
|
156
158
|
) -> None: ...
|
|
157
159
|
|
|
158
160
|
def __init__(
|
|
159
161
|
self,
|
|
160
|
-
content:
|
|
161
|
-
content_blocks:
|
|
162
|
+
content: str | list[str | dict] | None = None,
|
|
163
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
162
164
|
**kwargs: Any,
|
|
163
165
|
) -> None:
|
|
164
|
-
"""Initialize
|
|
166
|
+
"""Initialize a `BaseMessage`.
|
|
165
167
|
|
|
166
|
-
Specify
|
|
168
|
+
Specify `content` as positional arg or `content_blocks` for typing.
|
|
167
169
|
|
|
168
170
|
Args:
|
|
169
|
-
content: The
|
|
171
|
+
content: The contents of the message.
|
|
170
172
|
content_blocks: Typed standard content.
|
|
171
|
-
kwargs: Additional arguments to pass to the parent class.
|
|
173
|
+
**kwargs: Additional arguments to pass to the parent class.
|
|
172
174
|
"""
|
|
173
175
|
if content_blocks is not None:
|
|
174
176
|
super().__init__(content=content_blocks, **kwargs)
|
|
@@ -177,7 +179,7 @@ class BaseMessage(Serializable):
|
|
|
177
179
|
|
|
178
180
|
@classmethod
|
|
179
181
|
def is_lc_serializable(cls) -> bool:
|
|
180
|
-
"""
|
|
182
|
+
"""`BaseMessage` is serializable.
|
|
181
183
|
|
|
182
184
|
Returns:
|
|
183
185
|
True
|
|
@@ -186,10 +188,10 @@ class BaseMessage(Serializable):
|
|
|
186
188
|
|
|
187
189
|
@classmethod
|
|
188
190
|
def get_lc_namespace(cls) -> list[str]:
|
|
189
|
-
"""Get the namespace of the
|
|
191
|
+
"""Get the namespace of the LangChain object.
|
|
190
192
|
|
|
191
193
|
Returns:
|
|
192
|
-
|
|
194
|
+
`["langchain", "schema", "messages"]`
|
|
193
195
|
"""
|
|
194
196
|
return ["langchain", "schema", "messages"]
|
|
195
197
|
|
|
@@ -197,7 +199,7 @@ class BaseMessage(Serializable):
|
|
|
197
199
|
def content_blocks(self) -> list[types.ContentBlock]:
|
|
198
200
|
r"""Load content blocks from the message content.
|
|
199
201
|
|
|
200
|
-
|
|
202
|
+
!!! version-added "Added in `langchain-core` 1.0.0"
|
|
201
203
|
|
|
202
204
|
"""
|
|
203
205
|
# Needed here to avoid circular import, as these classes import BaseMessages
|
|
@@ -261,11 +263,11 @@ class BaseMessage(Serializable):
|
|
|
261
263
|
def text(self) -> TextAccessor:
|
|
262
264
|
"""Get the text content of the message as a string.
|
|
263
265
|
|
|
264
|
-
Can be used as both property (
|
|
266
|
+
Can be used as both property (`message.text`) and method (`message.text()`).
|
|
265
267
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
instead. This method will be removed in 2.0.0.
|
|
268
|
+
!!! deprecated
|
|
269
|
+
As of `langchain-core` 1.0.0, calling `.text()` as a method is deprecated.
|
|
270
|
+
Use `.text` as a property instead. This method will be removed in 2.0.0.
|
|
269
271
|
|
|
270
272
|
Returns:
|
|
271
273
|
The text content of the message.
|
|
@@ -308,8 +310,8 @@ class BaseMessage(Serializable):
|
|
|
308
310
|
"""Get a pretty representation of the message.
|
|
309
311
|
|
|
310
312
|
Args:
|
|
311
|
-
html: Whether to format the message as HTML. If True
|
|
312
|
-
formatted with HTML tags.
|
|
313
|
+
html: Whether to format the message as HTML. If `True`, the message will be
|
|
314
|
+
formatted with HTML tags.
|
|
313
315
|
|
|
314
316
|
Returns:
|
|
315
317
|
A pretty representation of the message.
|
|
@@ -327,20 +329,20 @@ class BaseMessage(Serializable):
|
|
|
327
329
|
|
|
328
330
|
|
|
329
331
|
def merge_content(
|
|
330
|
-
first_content:
|
|
331
|
-
*contents:
|
|
332
|
-
) ->
|
|
332
|
+
first_content: str | list[str | dict],
|
|
333
|
+
*contents: str | list[str | dict],
|
|
334
|
+
) -> str | list[str | dict]:
|
|
333
335
|
"""Merge multiple message contents.
|
|
334
336
|
|
|
335
337
|
Args:
|
|
336
|
-
first_content: The first
|
|
337
|
-
contents: The other
|
|
338
|
+
first_content: The first `content`. Can be a string or a list.
|
|
339
|
+
contents: The other `content`s. Can be a string or a list.
|
|
338
340
|
|
|
339
341
|
Returns:
|
|
340
342
|
The merged content.
|
|
341
343
|
|
|
342
344
|
"""
|
|
343
|
-
merged:
|
|
345
|
+
merged: str | list[str | dict]
|
|
344
346
|
merged = "" if first_content is None else first_content
|
|
345
347
|
|
|
346
348
|
for content in contents:
|
|
@@ -390,9 +392,9 @@ class BaseMessageChunk(BaseMessage):
|
|
|
390
392
|
|
|
391
393
|
For example,
|
|
392
394
|
|
|
393
|
-
|
|
395
|
+
`AIMessageChunk(content="Hello") + AIMessageChunk(content=" World")`
|
|
394
396
|
|
|
395
|
-
will give
|
|
397
|
+
will give `AIMessageChunk(content="Hello World")`
|
|
396
398
|
|
|
397
399
|
"""
|
|
398
400
|
if isinstance(other, BaseMessageChunk):
|
|
@@ -441,8 +443,8 @@ def message_to_dict(message: BaseMessage) -> dict:
|
|
|
441
443
|
message: Message to convert.
|
|
442
444
|
|
|
443
445
|
Returns:
|
|
444
|
-
Message as a dict. The dict will have a
|
|
445
|
-
and a
|
|
446
|
+
Message as a dict. The dict will have a `type` key with the message type
|
|
447
|
+
and a `data` key with the message data as a dict.
|
|
446
448
|
|
|
447
449
|
"""
|
|
448
450
|
return {"type": message.type, "data": message.model_dump()}
|
|
@@ -452,7 +454,7 @@ def messages_to_dict(messages: Sequence[BaseMessage]) -> list[dict]:
|
|
|
452
454
|
"""Convert a sequence of Messages to a list of dictionaries.
|
|
453
455
|
|
|
454
456
|
Args:
|
|
455
|
-
messages: Sequence of messages (as
|
|
457
|
+
messages: Sequence of messages (as `BaseMessage`s) to convert.
|
|
456
458
|
|
|
457
459
|
Returns:
|
|
458
460
|
List of messages as dicts.
|
|
@@ -466,7 +468,7 @@ def get_msg_title_repr(title: str, *, bold: bool = False) -> str:
|
|
|
466
468
|
|
|
467
469
|
Args:
|
|
468
470
|
title: The title.
|
|
469
|
-
bold: Whether to bold the title.
|
|
471
|
+
bold: Whether to bold the title.
|
|
470
472
|
|
|
471
473
|
Returns:
|
|
472
474
|
The title representation.
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"""Derivations of standard content blocks from provider content.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
5
|
provider translator must handle all possible content response types from the provider,
|
|
6
6
|
including text.
|
|
7
7
|
|
|
8
8
|
If no provider is set, or if the provider does not have a registered translator,
|
|
9
|
-
|
|
10
|
-
the implementation in
|
|
9
|
+
`AIMessage` will fall back to best-effort parsing of the content into blocks using
|
|
10
|
+
the implementation in `BaseMessage`.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
-
from
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from langchain_core.messages import AIMessage, AIMessageChunk
|
|
@@ -22,15 +23,15 @@ if TYPE_CHECKING:
|
|
|
22
23
|
PROVIDER_TRANSLATORS: dict[str, dict[str, Callable[..., list[types.ContentBlock]]]] = {}
|
|
23
24
|
"""Map model provider names to translator functions.
|
|
24
25
|
|
|
25
|
-
The dictionary maps provider names (e.g.
|
|
26
|
+
The dictionary maps provider names (e.g. `'openai'`, `'anthropic'`) to another
|
|
26
27
|
dictionary with two keys:
|
|
27
|
-
-
|
|
28
|
-
-
|
|
28
|
+
- `'translate_content'`: Function to translate `AIMessage` content.
|
|
29
|
+
- `'translate_content_chunk'`: Function to translate `AIMessageChunk` content.
|
|
29
30
|
|
|
30
|
-
When calling
|
|
31
|
-
|
|
31
|
+
When calling `content_blocks` on an `AIMessage` or `AIMessageChunk`, if
|
|
32
|
+
`model_provider` is set in `response_metadata`, the corresponding translator
|
|
32
33
|
functions will be used to parse the content into blocks. Otherwise, best-effort parsing
|
|
33
|
-
in
|
|
34
|
+
in `BaseMessage` will be used.
|
|
34
35
|
"""
|
|
35
36
|
|
|
36
37
|
|
|
@@ -42,9 +43,9 @@ def register_translator(
|
|
|
42
43
|
"""Register content translators for a provider in `PROVIDER_TRANSLATORS`.
|
|
43
44
|
|
|
44
45
|
Args:
|
|
45
|
-
provider: The model provider name (e.g.
|
|
46
|
-
translate_content: Function to translate
|
|
47
|
-
translate_content_chunk: Function to translate
|
|
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.
|
|
48
49
|
"""
|
|
49
50
|
PROVIDER_TRANSLATORS[provider] = {
|
|
50
51
|
"translate_content": translate_content,
|
|
@@ -61,9 +62,9 @@ def get_translator(
|
|
|
61
62
|
provider: The model provider name.
|
|
62
63
|
|
|
63
64
|
Returns:
|
|
64
|
-
Dictionary with
|
|
65
|
+
Dictionary with `'translate_content'` and `'translate_content_chunk'`
|
|
65
66
|
functions, or None if no translator is registered for the provider. In such
|
|
66
|
-
case, best-effort parsing in
|
|
67
|
+
case, best-effort parsing in `BaseMessage` will be used.
|
|
67
68
|
"""
|
|
68
69
|
return PROVIDER_TRANSLATORS.get(provider)
|
|
69
70
|
|
|
@@ -71,10 +72,10 @@ def get_translator(
|
|
|
71
72
|
def _register_translators() -> None:
|
|
72
73
|
"""Register all translators in langchain-core.
|
|
73
74
|
|
|
74
|
-
A unit test ensures all modules in
|
|
75
|
+
A unit test ensures all modules in `block_translators` are represented here.
|
|
75
76
|
|
|
76
77
|
For translators implemented outside langchain-core, they can be registered by
|
|
77
|
-
calling
|
|
78
|
+
calling `register_translator` from within the integration package.
|
|
78
79
|
"""
|
|
79
80
|
from langchain_core.messages.block_translators.anthropic import ( # noqa: PLC0415
|
|
80
81
|
_register_anthropic_translator,
|
|
@@ -94,9 +95,6 @@ def _register_translators() -> None:
|
|
|
94
95
|
from langchain_core.messages.block_translators.groq import ( # noqa: PLC0415
|
|
95
96
|
_register_groq_translator,
|
|
96
97
|
)
|
|
97
|
-
from langchain_core.messages.block_translators.ollama import ( # noqa: PLC0415
|
|
98
|
-
_register_ollama_translator,
|
|
99
|
-
)
|
|
100
98
|
from langchain_core.messages.block_translators.openai import ( # noqa: PLC0415
|
|
101
99
|
_register_openai_translator,
|
|
102
100
|
)
|
|
@@ -107,7 +105,6 @@ def _register_translators() -> None:
|
|
|
107
105
|
_register_google_genai_translator()
|
|
108
106
|
_register_google_vertexai_translator()
|
|
109
107
|
_register_groq_translator()
|
|
110
|
-
_register_ollama_translator()
|
|
111
108
|
_register_openai_translator()
|
|
112
109
|
|
|
113
110
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from collections.abc import Iterable
|
|
5
|
-
from typing import Any,
|
|
5
|
+
from typing import Any, cast
|
|
6
6
|
|
|
7
7
|
from langchain_core.messages import AIMessage, AIMessageChunk
|
|
8
8
|
from langchain_core.messages import content as types
|
|
@@ -31,12 +31,12 @@ def _convert_to_v1_from_anthropic_input(
|
|
|
31
31
|
) -> list[types.ContentBlock]:
|
|
32
32
|
"""Convert Anthropic format blocks to v1 format.
|
|
33
33
|
|
|
34
|
-
During the
|
|
35
|
-
block as a
|
|
34
|
+
During the `content_blocks` parsing process, we wrap blocks not recognized as a v1
|
|
35
|
+
block as a `'non_standard'` block with the original block stored in the `value`
|
|
36
36
|
field. This function attempts to unpack those blocks and convert any blocks that
|
|
37
37
|
might be Anthropic format to v1 ContentBlocks.
|
|
38
38
|
|
|
39
|
-
If conversion fails, the block is left as a
|
|
39
|
+
If conversion fails, the block is left as a `'non_standard'` block.
|
|
40
40
|
|
|
41
41
|
Args:
|
|
42
42
|
content: List of content blocks to process.
|
|
@@ -200,7 +200,7 @@ def _convert_citation_to_v1(citation: dict[str, Any]) -> types.Annotation:
|
|
|
200
200
|
def _convert_to_v1_from_anthropic(message: AIMessage) -> list[types.ContentBlock]:
|
|
201
201
|
"""Convert Anthropic message content to v1 format."""
|
|
202
202
|
if isinstance(message.content, str):
|
|
203
|
-
content: list[
|
|
203
|
+
content: list[str | dict] = [{"type": "text", "text": message.content}]
|
|
204
204
|
else:
|
|
205
205
|
content = message.content
|
|
206
206
|
|
|
@@ -252,7 +252,7 @@ def _convert_to_v1_from_anthropic(message: AIMessage) -> list[types.ContentBlock
|
|
|
252
252
|
tool_call_chunk["type"] = "tool_call_chunk"
|
|
253
253
|
yield tool_call_chunk
|
|
254
254
|
else:
|
|
255
|
-
tool_call_block:
|
|
255
|
+
tool_call_block: types.ToolCall | None = None
|
|
256
256
|
# Non-streaming or gathered chunk
|
|
257
257
|
if len(message.tool_calls) == 1:
|
|
258
258
|
tool_call_block = {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
4
|
from collections.abc import Iterable
|
|
5
|
-
from typing import Any,
|
|
5
|
+
from typing import Any, cast
|
|
6
6
|
|
|
7
7
|
from langchain_core.messages import AIMessage, AIMessageChunk
|
|
8
8
|
from langchain_core.messages import content as types
|
|
@@ -35,12 +35,12 @@ def _convert_to_v1_from_converse_input(
|
|
|
35
35
|
) -> list[types.ContentBlock]:
|
|
36
36
|
"""Convert Bedrock Converse format blocks to v1 format.
|
|
37
37
|
|
|
38
|
-
During the
|
|
39
|
-
block as a
|
|
38
|
+
During the `content_blocks` parsing process, we wrap blocks not recognized as a v1
|
|
39
|
+
block as a `'non_standard'` block with the original block stored in the `value`
|
|
40
40
|
field. This function attempts to unpack those blocks and convert any blocks that
|
|
41
41
|
might be Converse format to v1 ContentBlocks.
|
|
42
42
|
|
|
43
|
-
If conversion fails, the block is left as a
|
|
43
|
+
If conversion fails, the block is left as a `'non_standard'` block.
|
|
44
44
|
|
|
45
45
|
Args:
|
|
46
46
|
content: List of content blocks to process.
|
|
@@ -216,7 +216,7 @@ def _convert_to_v1_from_converse(message: AIMessage) -> list[types.ContentBlock]
|
|
|
216
216
|
tool_call_chunk["type"] = "tool_call_chunk"
|
|
217
217
|
yield tool_call_chunk
|
|
218
218
|
else:
|
|
219
|
-
tool_call_block:
|
|
219
|
+
tool_call_block: types.ToolCall | None = None
|
|
220
220
|
# Non-streaming or gathered chunk
|
|
221
221
|
if len(message.tool_calls) == 1:
|
|
222
222
|
tool_call_block = {
|
|
@@ -105,12 +105,12 @@ def _convert_to_v1_from_genai_input(
|
|
|
105
105
|
Called when message isn't an `AIMessage` or `model_provider` isn't set on
|
|
106
106
|
`response_metadata`.
|
|
107
107
|
|
|
108
|
-
During the
|
|
109
|
-
block as a
|
|
108
|
+
During the `content_blocks` parsing process, we wrap blocks not recognized as a v1
|
|
109
|
+
block as a `'non_standard'` block with the original block stored in the `value`
|
|
110
110
|
field. This function attempts to unpack those blocks and convert any blocks that
|
|
111
111
|
might be GenAI format to v1 ContentBlocks.
|
|
112
112
|
|
|
113
|
-
If conversion fails, the block is left as a
|
|
113
|
+
If conversion fails, the block is left as a `'non_standard'` block.
|
|
114
114
|
|
|
115
115
|
Args:
|
|
116
116
|
content: List of content blocks to process.
|
|
@@ -282,7 +282,7 @@ def _convert_to_v1_from_genai(message: AIMessage) -> list[types.ContentBlock]:
|
|
|
282
282
|
standard content blocks for returning.
|
|
283
283
|
|
|
284
284
|
Args:
|
|
285
|
-
message: The AIMessage or AIMessageChunk to convert.
|
|
285
|
+
message: The `AIMessage` or `AIMessageChunk` to convert.
|
|
286
286
|
|
|
287
287
|
Returns:
|
|
288
288
|
List of standard content blocks derived from the message content.
|
|
@@ -368,7 +368,7 @@ def _convert_to_v1_from_genai(message: AIMessage) -> list[types.ContentBlock]:
|
|
|
368
368
|
else:
|
|
369
369
|
# Assume it's raw base64 without data URI
|
|
370
370
|
try:
|
|
371
|
-
# Validate base64 and decode for
|
|
371
|
+
# Validate base64 and decode for MIME type detection
|
|
372
372
|
decoded_bytes = base64.b64decode(url, validate=True)
|
|
373
373
|
|
|
374
374
|
image_url_b64_block = {
|
|
@@ -379,7 +379,7 @@ def _convert_to_v1_from_genai(message: AIMessage) -> list[types.ContentBlock]:
|
|
|
379
379
|
try:
|
|
380
380
|
import filetype # type: ignore[import-not-found] # noqa: PLC0415
|
|
381
381
|
|
|
382
|
-
# Guess
|
|
382
|
+
# Guess MIME type based on file bytes
|
|
383
383
|
mime_type = None
|
|
384
384
|
kind = filetype.guess(decoded_bytes)
|
|
385
385
|
if kind:
|
|
@@ -453,10 +453,13 @@ def _convert_to_v1_from_genai(message: AIMessage) -> list[types.ContentBlock]:
|
|
|
453
453
|
"status": status, # type: ignore[typeddict-item]
|
|
454
454
|
"output": item.get("code_execution_result", ""),
|
|
455
455
|
}
|
|
456
|
+
server_tool_result_block["extras"] = {"block_type": item_type}
|
|
456
457
|
# Preserve original outcome in extras
|
|
457
458
|
if outcome is not None:
|
|
458
|
-
server_tool_result_block["extras"]
|
|
459
|
+
server_tool_result_block["extras"]["outcome"] = outcome
|
|
459
460
|
converted_blocks.append(server_tool_result_block)
|
|
461
|
+
elif item_type == "text":
|
|
462
|
+
converted_blocks.append(cast("types.TextContentBlock", item))
|
|
460
463
|
else:
|
|
461
464
|
# Unknown type, preserve as non-standard
|
|
462
465
|
converted_blocks.append({"type": "non_standard", "value": item})
|
|
@@ -1,37 +1,9 @@
|
|
|
1
1
|
"""Derivations of standard content blocks from Google (VertexAI) content."""
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
WARNED = False
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def translate_content(message: AIMessage) -> list[types.ContentBlock]: # noqa: ARG001
|
|
12
|
-
"""Derive standard content blocks from a message with Google (VertexAI) content."""
|
|
13
|
-
global WARNED # noqa: PLW0603
|
|
14
|
-
if not WARNED:
|
|
15
|
-
warning_message = (
|
|
16
|
-
"Content block standardization is not yet fully supported for Google "
|
|
17
|
-
"VertexAI."
|
|
18
|
-
)
|
|
19
|
-
warnings.warn(warning_message, stacklevel=2)
|
|
20
|
-
WARNED = True
|
|
21
|
-
raise NotImplementedError
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def translate_content_chunk(message: AIMessageChunk) -> list[types.ContentBlock]: # noqa: ARG001
|
|
25
|
-
"""Derive standard content blocks from a chunk with Google (VertexAI) content."""
|
|
26
|
-
global WARNED # noqa: PLW0603
|
|
27
|
-
if not WARNED:
|
|
28
|
-
warning_message = (
|
|
29
|
-
"Content block standardization is not yet fully supported for Google "
|
|
30
|
-
"VertexAI."
|
|
31
|
-
)
|
|
32
|
-
warnings.warn(warning_message, stacklevel=2)
|
|
33
|
-
WARNED = True
|
|
34
|
-
raise NotImplementedError
|
|
3
|
+
from langchain_core.messages.block_translators.google_genai import (
|
|
4
|
+
translate_content,
|
|
5
|
+
translate_content_chunk,
|
|
6
|
+
)
|
|
35
7
|
|
|
36
8
|
|
|
37
9
|
def _register_google_vertexai_translator() -> None:
|