langchain-core 1.0.0a4__py3-none-any.whl → 1.0.0a5__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/_api/beta_decorator.py +6 -5
- langchain_core/_api/deprecation.py +11 -11
- langchain_core/callbacks/manager.py +2 -2
- langchain_core/callbacks/usage.py +2 -2
- langchain_core/document_loaders/langsmith.py +1 -1
- langchain_core/indexing/api.py +30 -30
- langchain_core/language_models/chat_models.py +1 -1
- langchain_core/language_models/fake_chat_models.py +5 -2
- langchain_core/load/serializable.py +1 -1
- langchain_core/messages/__init__.py +9 -15
- langchain_core/messages/ai.py +75 -9
- langchain_core/messages/base.py +75 -37
- langchain_core/messages/block_translators/__init__.py +11 -1
- langchain_core/messages/block_translators/anthropic.py +143 -128
- langchain_core/messages/block_translators/bedrock_converse.py +15 -1
- langchain_core/messages/block_translators/langchain_v0.py +180 -43
- langchain_core/messages/block_translators/openai.py +224 -42
- langchain_core/messages/chat.py +4 -1
- langchain_core/messages/content.py +56 -112
- langchain_core/messages/function.py +9 -5
- langchain_core/messages/human.py +6 -2
- langchain_core/messages/modifier.py +1 -0
- langchain_core/messages/system.py +9 -2
- langchain_core/messages/tool.py +31 -14
- langchain_core/messages/utils.py +89 -83
- langchain_core/outputs/chat_generation.py +10 -6
- langchain_core/prompt_values.py +6 -2
- langchain_core/prompts/chat.py +6 -3
- langchain_core/prompts/few_shot.py +4 -1
- langchain_core/runnables/base.py +4 -1
- langchain_core/runnables/graph_ascii.py +1 -1
- langchain_core/tools/base.py +1 -2
- langchain_core/tools/convert.py +1 -1
- langchain_core/utils/aiter.py +1 -1
- langchain_core/utils/function_calling.py +5 -6
- langchain_core/utils/iter.py +1 -1
- langchain_core/vectorstores/in_memory.py +5 -5
- langchain_core/version.py +1 -1
- {langchain_core-1.0.0a4.dist-info → langchain_core-1.0.0a5.dist-info}/METADATA +8 -8
- {langchain_core-1.0.0a4.dist-info → langchain_core-1.0.0a5.dist-info}/RECORD +42 -42
- {langchain_core-1.0.0a4.dist-info → langchain_core-1.0.0a5.dist-info}/WHEEL +0 -0
- {langchain_core-1.0.0a4.dist-info → langchain_core-1.0.0a5.dist-info}/entry_points.txt +0 -0
|
@@ -6,32 +6,36 @@ from langchain_core.messages import content as types
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def _convert_v0_multimodal_input_to_v1(
|
|
9
|
-
|
|
9
|
+
content: list[types.ContentBlock],
|
|
10
10
|
) -> list[types.ContentBlock]:
|
|
11
11
|
"""Convert v0 multimodal blocks to v1 format.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
During the `.content_blocks` parsing process, we wrap blocks not recognized as a v1
|
|
14
|
+
block as a ``'non_standard'`` block with the original block stored in the ``value``
|
|
15
|
+
field. This function attempts to unpack those blocks and convert any v0 format
|
|
16
|
+
blocks to v1 format.
|
|
17
|
+
|
|
18
|
+
If conversion fails, the block is left as a ``'non_standard'`` block.
|
|
15
19
|
|
|
16
20
|
Args:
|
|
17
|
-
|
|
21
|
+
content: List of content blocks to process.
|
|
18
22
|
|
|
19
23
|
Returns:
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
v1 content blocks.
|
|
22
25
|
"""
|
|
23
26
|
converted_blocks = []
|
|
24
27
|
unpacked_blocks: list[dict[str, Any]] = [
|
|
25
28
|
cast("dict[str, Any]", block)
|
|
26
29
|
if block.get("type") != "non_standard"
|
|
27
30
|
else block["value"] # type: ignore[typeddict-item] # this is only non-standard blocks
|
|
28
|
-
for block in
|
|
31
|
+
for block in content
|
|
29
32
|
]
|
|
30
33
|
for block in unpacked_blocks:
|
|
31
34
|
if block.get("type") in {"image", "audio", "file"} and "source_type" in block:
|
|
32
35
|
converted_block = _convert_legacy_v0_content_block_to_v1(block)
|
|
33
36
|
converted_blocks.append(cast("types.ContentBlock", converted_block))
|
|
34
37
|
elif block.get("type") in types.KNOWN_BLOCK_TYPES:
|
|
38
|
+
# Guard in case this function is used outside of the .content_blocks flow
|
|
35
39
|
converted_blocks.append(cast("types.ContentBlock", block))
|
|
36
40
|
else:
|
|
37
41
|
converted_blocks.append({"type": "non_standard", "value": block})
|
|
@@ -47,11 +51,18 @@ def _convert_legacy_v0_content_block_to_v1(
|
|
|
47
51
|
Preserves unknown keys as extras to avoid data loss.
|
|
48
52
|
|
|
49
53
|
Returns the original block unchanged if it's not in v0 format.
|
|
50
|
-
|
|
51
54
|
"""
|
|
52
55
|
|
|
53
56
|
def _extract_v0_extras(block_dict: dict, known_keys: set[str]) -> dict[str, Any]:
|
|
54
|
-
"""Extract unknown keys from v0 block to preserve as extras.
|
|
57
|
+
"""Extract unknown keys from v0 block to preserve as extras.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
block_dict: The original v0 block dictionary.
|
|
61
|
+
known_keys: Set of keys known to be part of the v0 format for this block.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
A dictionary of extra keys not part of the known v0 format.
|
|
65
|
+
"""
|
|
55
66
|
return {k: v for k, v in block_dict.items() if k not in known_keys}
|
|
56
67
|
|
|
57
68
|
# Check if this is actually a v0 format block
|
|
@@ -63,7 +74,8 @@ def _convert_legacy_v0_content_block_to_v1(
|
|
|
63
74
|
if block.get("type") == "image":
|
|
64
75
|
source_type = block.get("source_type")
|
|
65
76
|
if source_type == "url":
|
|
66
|
-
|
|
77
|
+
# image-url
|
|
78
|
+
known_keys = {"mime_type", "type", "source_type", "url"}
|
|
67
79
|
extras = _extract_v0_extras(block, known_keys)
|
|
68
80
|
if "id" in block:
|
|
69
81
|
return types.create_image_block(
|
|
@@ -74,17 +86,21 @@ def _convert_legacy_v0_content_block_to_v1(
|
|
|
74
86
|
)
|
|
75
87
|
|
|
76
88
|
# Don't construct with an ID if not present in original block
|
|
77
|
-
|
|
89
|
+
v1_image_url = types.ImageContentBlock(type="image", url=block["url"])
|
|
78
90
|
if block.get("mime_type"):
|
|
79
|
-
|
|
91
|
+
v1_image_url["mime_type"] = block["mime_type"]
|
|
80
92
|
|
|
93
|
+
v1_image_url["extras"] = {}
|
|
81
94
|
for key, value in extras.items():
|
|
82
95
|
if value is not None:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
96
|
+
v1_image_url["extras"][key] = value
|
|
97
|
+
if v1_image_url["extras"] == {}:
|
|
98
|
+
del v1_image_url["extras"]
|
|
99
|
+
|
|
100
|
+
return v1_image_url
|
|
86
101
|
if source_type == "base64":
|
|
87
|
-
|
|
102
|
+
# image-base64
|
|
103
|
+
known_keys = {"mime_type", "type", "source_type", "data"}
|
|
88
104
|
extras = _extract_v0_extras(block, known_keys)
|
|
89
105
|
if "id" in block:
|
|
90
106
|
return types.create_image_block(
|
|
@@ -94,71 +110,192 @@ def _convert_legacy_v0_content_block_to_v1(
|
|
|
94
110
|
**extras,
|
|
95
111
|
)
|
|
96
112
|
|
|
97
|
-
|
|
113
|
+
v1_image_base64 = types.ImageContentBlock(
|
|
114
|
+
type="image", base64=block["data"]
|
|
115
|
+
)
|
|
98
116
|
if block.get("mime_type"):
|
|
99
|
-
|
|
117
|
+
v1_image_base64["mime_type"] = block["mime_type"]
|
|
100
118
|
|
|
119
|
+
v1_image_base64["extras"] = {}
|
|
101
120
|
for key, value in extras.items():
|
|
102
121
|
if value is not None:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
v1_image_base64["extras"][key] = value
|
|
123
|
+
if v1_image_base64["extras"] == {}:
|
|
124
|
+
del v1_image_base64["extras"]
|
|
125
|
+
|
|
126
|
+
return v1_image_base64
|
|
106
127
|
if source_type == "id":
|
|
128
|
+
# image-id
|
|
107
129
|
known_keys = {"type", "source_type", "id"}
|
|
108
130
|
extras = _extract_v0_extras(block, known_keys)
|
|
109
131
|
# For id `source_type`, `id` is the file reference, not block ID
|
|
110
|
-
|
|
132
|
+
v1_image_id = types.ImageContentBlock(type="image", file_id=block["id"])
|
|
111
133
|
|
|
134
|
+
v1_image_id["extras"] = {}
|
|
112
135
|
for key, value in extras.items():
|
|
113
136
|
if value is not None:
|
|
114
|
-
|
|
115
|
-
|
|
137
|
+
v1_image_id["extras"][key] = value
|
|
138
|
+
if v1_image_id["extras"] == {}:
|
|
139
|
+
del v1_image_id["extras"]
|
|
116
140
|
|
|
117
|
-
return
|
|
141
|
+
return v1_image_id
|
|
118
142
|
elif block.get("type") == "audio":
|
|
119
143
|
source_type = block.get("source_type")
|
|
120
144
|
if source_type == "url":
|
|
121
|
-
|
|
145
|
+
# audio-url
|
|
146
|
+
known_keys = {"mime_type", "type", "source_type", "url"}
|
|
122
147
|
extras = _extract_v0_extras(block, known_keys)
|
|
123
|
-
|
|
124
|
-
|
|
148
|
+
if "id" in block:
|
|
149
|
+
return types.create_audio_block(
|
|
150
|
+
url=block["url"],
|
|
151
|
+
mime_type=block.get("mime_type"),
|
|
152
|
+
id=block["id"],
|
|
153
|
+
**extras,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Don't construct with an ID if not present in original block
|
|
157
|
+
v1_audio_url: types.AudioContentBlock = types.AudioContentBlock(
|
|
158
|
+
type="audio", url=block["url"]
|
|
125
159
|
)
|
|
160
|
+
if block.get("mime_type"):
|
|
161
|
+
v1_audio_url["mime_type"] = block["mime_type"]
|
|
162
|
+
|
|
163
|
+
v1_audio_url["extras"] = {}
|
|
164
|
+
for key, value in extras.items():
|
|
165
|
+
if value is not None:
|
|
166
|
+
v1_audio_url["extras"][key] = value
|
|
167
|
+
if v1_audio_url["extras"] == {}:
|
|
168
|
+
del v1_audio_url["extras"]
|
|
169
|
+
|
|
170
|
+
return v1_audio_url
|
|
126
171
|
if source_type == "base64":
|
|
127
|
-
|
|
172
|
+
# audio-base64
|
|
173
|
+
known_keys = {"mime_type", "type", "source_type", "data"}
|
|
128
174
|
extras = _extract_v0_extras(block, known_keys)
|
|
129
|
-
|
|
130
|
-
|
|
175
|
+
if "id" in block:
|
|
176
|
+
return types.create_audio_block(
|
|
177
|
+
base64=block["data"],
|
|
178
|
+
mime_type=block.get("mime_type"),
|
|
179
|
+
id=block["id"],
|
|
180
|
+
**extras,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
v1_audio_base64: types.AudioContentBlock = types.AudioContentBlock(
|
|
184
|
+
type="audio", base64=block["data"]
|
|
131
185
|
)
|
|
186
|
+
if block.get("mime_type"):
|
|
187
|
+
v1_audio_base64["mime_type"] = block["mime_type"]
|
|
188
|
+
|
|
189
|
+
v1_audio_base64["extras"] = {}
|
|
190
|
+
for key, value in extras.items():
|
|
191
|
+
if value is not None:
|
|
192
|
+
v1_audio_base64["extras"][key] = value
|
|
193
|
+
if v1_audio_base64["extras"] == {}:
|
|
194
|
+
del v1_audio_base64["extras"]
|
|
195
|
+
|
|
196
|
+
return v1_audio_base64
|
|
132
197
|
if source_type == "id":
|
|
198
|
+
# audio-id
|
|
133
199
|
known_keys = {"type", "source_type", "id"}
|
|
134
200
|
extras = _extract_v0_extras(block, known_keys)
|
|
135
|
-
|
|
201
|
+
v1_audio_id: types.AudioContentBlock = types.AudioContentBlock(
|
|
202
|
+
type="audio", file_id=block["id"]
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
v1_audio_id["extras"] = {}
|
|
206
|
+
for key, value in extras.items():
|
|
207
|
+
if value is not None:
|
|
208
|
+
v1_audio_id["extras"][key] = value
|
|
209
|
+
if v1_audio_id["extras"] == {}:
|
|
210
|
+
del v1_audio_id["extras"]
|
|
211
|
+
|
|
212
|
+
return v1_audio_id
|
|
136
213
|
elif block.get("type") == "file":
|
|
137
214
|
source_type = block.get("source_type")
|
|
138
215
|
if source_type == "url":
|
|
139
|
-
|
|
216
|
+
# file-url
|
|
217
|
+
known_keys = {"mime_type", "type", "source_type", "url"}
|
|
140
218
|
extras = _extract_v0_extras(block, known_keys)
|
|
141
|
-
|
|
142
|
-
|
|
219
|
+
if "id" in block:
|
|
220
|
+
return types.create_file_block(
|
|
221
|
+
url=block["url"],
|
|
222
|
+
mime_type=block.get("mime_type"),
|
|
223
|
+
id=block["id"],
|
|
224
|
+
**extras,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
v1_file_url: types.FileContentBlock = types.FileContentBlock(
|
|
228
|
+
type="file", url=block["url"]
|
|
143
229
|
)
|
|
230
|
+
if block.get("mime_type"):
|
|
231
|
+
v1_file_url["mime_type"] = block["mime_type"]
|
|
232
|
+
|
|
233
|
+
v1_file_url["extras"] = {}
|
|
234
|
+
for key, value in extras.items():
|
|
235
|
+
if value is not None:
|
|
236
|
+
v1_file_url["extras"][key] = value
|
|
237
|
+
if v1_file_url["extras"] == {}:
|
|
238
|
+
del v1_file_url["extras"]
|
|
239
|
+
|
|
240
|
+
return v1_file_url
|
|
144
241
|
if source_type == "base64":
|
|
145
|
-
|
|
242
|
+
# file-base64
|
|
243
|
+
known_keys = {"mime_type", "type", "source_type", "data"}
|
|
146
244
|
extras = _extract_v0_extras(block, known_keys)
|
|
147
|
-
|
|
148
|
-
|
|
245
|
+
if "id" in block:
|
|
246
|
+
return types.create_file_block(
|
|
247
|
+
base64=block["data"],
|
|
248
|
+
mime_type=block.get("mime_type"),
|
|
249
|
+
id=block["id"],
|
|
250
|
+
**extras,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
v1_file_base64: types.FileContentBlock = types.FileContentBlock(
|
|
254
|
+
type="file", base64=block["data"]
|
|
149
255
|
)
|
|
256
|
+
if block.get("mime_type"):
|
|
257
|
+
v1_file_base64["mime_type"] = block["mime_type"]
|
|
258
|
+
|
|
259
|
+
v1_file_base64["extras"] = {}
|
|
260
|
+
for key, value in extras.items():
|
|
261
|
+
if value is not None:
|
|
262
|
+
v1_file_base64["extras"][key] = value
|
|
263
|
+
if v1_file_base64["extras"] == {}:
|
|
264
|
+
del v1_file_base64["extras"]
|
|
265
|
+
|
|
266
|
+
return v1_file_base64
|
|
150
267
|
if source_type == "id":
|
|
268
|
+
# file-id
|
|
151
269
|
known_keys = {"type", "source_type", "id"}
|
|
152
270
|
extras = _extract_v0_extras(block, known_keys)
|
|
153
271
|
return types.create_file_block(file_id=block["id"], **extras)
|
|
154
272
|
if source_type == "text":
|
|
155
|
-
|
|
273
|
+
# file-text
|
|
274
|
+
known_keys = {"mime_type", "type", "source_type", "url"}
|
|
156
275
|
extras = _extract_v0_extras(block, known_keys)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
276
|
+
if "id" in block:
|
|
277
|
+
return types.create_plaintext_block(
|
|
278
|
+
# In v0, URL points to the text file content
|
|
279
|
+
# TODO: attribute this claim
|
|
280
|
+
text=block["url"],
|
|
281
|
+
id=block["id"],
|
|
282
|
+
**extras,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
v1_file_text: types.PlainTextContentBlock = types.PlainTextContentBlock(
|
|
286
|
+
type="text-plain", text=block["url"], mime_type="text/plain"
|
|
161
287
|
)
|
|
288
|
+
if block.get("mime_type"):
|
|
289
|
+
v1_file_text["mime_type"] = block["mime_type"]
|
|
290
|
+
|
|
291
|
+
v1_file_text["extras"] = {}
|
|
292
|
+
for key, value in extras.items():
|
|
293
|
+
if value is not None:
|
|
294
|
+
v1_file_text["extras"][key] = value
|
|
295
|
+
if v1_file_text["extras"] == {}:
|
|
296
|
+
del v1_file_text["extras"]
|
|
297
|
+
|
|
298
|
+
return v1_file_text
|
|
162
299
|
|
|
163
300
|
# If we can't convert, return the block unchanged
|
|
164
301
|
return block
|
|
@@ -151,15 +151,19 @@ def _convert_to_v1_from_chat_completions(
|
|
|
151
151
|
|
|
152
152
|
|
|
153
153
|
def _convert_to_v1_from_chat_completions_input(
|
|
154
|
-
|
|
154
|
+
content: list[types.ContentBlock],
|
|
155
155
|
) -> list[types.ContentBlock]:
|
|
156
156
|
"""Convert OpenAI Chat Completions format blocks to v1 format.
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
During the `.content_blocks` parsing process, we wrap blocks not recognized as a v1
|
|
159
|
+
block as a ``'non_standard'`` block with the original block stored in the ``value``
|
|
160
|
+
field. This function attempts to unpack those blocks and convert any blocks that
|
|
161
|
+
might be OpenAI format to v1 ContentBlocks.
|
|
162
|
+
|
|
163
|
+
If conversion fails, the block is left as a ``'non_standard'`` block.
|
|
160
164
|
|
|
161
165
|
Args:
|
|
162
|
-
|
|
166
|
+
content: List of content blocks to process.
|
|
163
167
|
|
|
164
168
|
Returns:
|
|
165
169
|
Updated list with OpenAI blocks converted to v1 format.
|
|
@@ -171,7 +175,7 @@ def _convert_to_v1_from_chat_completions_input(
|
|
|
171
175
|
cast("dict[str, Any]", block)
|
|
172
176
|
if block.get("type") != "non_standard"
|
|
173
177
|
else block["value"] # type: ignore[typeddict-item] # this is only non-standard blocks
|
|
174
|
-
for block in
|
|
178
|
+
for block in content
|
|
175
179
|
]
|
|
176
180
|
for block in unpacked_blocks:
|
|
177
181
|
if block.get("type") in {
|
|
@@ -713,21 +717,27 @@ def _convert_to_v1_from_responses(message: AIMessage) -> list[types.ContentBlock
|
|
|
713
717
|
yield tool_call_block
|
|
714
718
|
|
|
715
719
|
elif block_type == "web_search_call":
|
|
716
|
-
web_search_call = {
|
|
720
|
+
web_search_call = {
|
|
721
|
+
"type": "server_tool_call",
|
|
722
|
+
"name": "web_search",
|
|
723
|
+
"args": {},
|
|
724
|
+
"id": block["id"],
|
|
725
|
+
}
|
|
717
726
|
if "index" in block:
|
|
718
727
|
web_search_call["index"] = f"lc_wsc_{block['index']}"
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
728
|
+
|
|
729
|
+
sources: Optional[dict[str, Any]] = None
|
|
730
|
+
if "action" in block and isinstance(block["action"], dict):
|
|
731
|
+
if "sources" in block["action"]:
|
|
732
|
+
sources = block["action"]["sources"]
|
|
733
|
+
web_search_call["args"] = {
|
|
734
|
+
k: v for k, v in block["action"].items() if k != "sources"
|
|
735
|
+
}
|
|
726
736
|
for key in block:
|
|
727
|
-
if key not in ("type", "id", "index"):
|
|
737
|
+
if key not in ("type", "id", "action", "status", "index"):
|
|
728
738
|
web_search_call[key] = block[key]
|
|
729
739
|
|
|
730
|
-
yield cast("types.
|
|
740
|
+
yield cast("types.ServerToolCall", web_search_call)
|
|
731
741
|
|
|
732
742
|
# If .content already has web_search_result, don't add
|
|
733
743
|
if not any(
|
|
@@ -736,21 +746,88 @@ def _convert_to_v1_from_responses(message: AIMessage) -> list[types.ContentBlock
|
|
|
736
746
|
and other_block.get("id") == block["id"]
|
|
737
747
|
for other_block in message.content
|
|
738
748
|
):
|
|
739
|
-
web_search_result = {
|
|
749
|
+
web_search_result = {
|
|
750
|
+
"type": "server_tool_result",
|
|
751
|
+
"tool_call_id": block["id"],
|
|
752
|
+
}
|
|
753
|
+
if sources:
|
|
754
|
+
web_search_result["output"] = {"sources": sources}
|
|
755
|
+
|
|
756
|
+
status = block.get("status")
|
|
757
|
+
if status == "failed":
|
|
758
|
+
web_search_result["status"] = "error"
|
|
759
|
+
elif status == "completed":
|
|
760
|
+
web_search_result["status"] = "success"
|
|
761
|
+
elif status:
|
|
762
|
+
web_search_result["extras"] = {"status": status}
|
|
763
|
+
else:
|
|
764
|
+
pass
|
|
740
765
|
if "index" in block and isinstance(block["index"], int):
|
|
741
766
|
web_search_result["index"] = f"lc_wsr_{block['index'] + 1}"
|
|
742
|
-
yield cast("types.
|
|
767
|
+
yield cast("types.ServerToolResult", web_search_result)
|
|
768
|
+
|
|
769
|
+
elif block_type == "file_search_call":
|
|
770
|
+
file_search_call = {
|
|
771
|
+
"type": "server_tool_call",
|
|
772
|
+
"name": "file_search",
|
|
773
|
+
"id": block["id"],
|
|
774
|
+
"args": {"queries": block.get("queries", [])},
|
|
775
|
+
}
|
|
776
|
+
if "index" in block:
|
|
777
|
+
file_search_call["index"] = f"lc_fsc_{block['index']}"
|
|
778
|
+
|
|
779
|
+
for key in block:
|
|
780
|
+
if key not in (
|
|
781
|
+
"type",
|
|
782
|
+
"id",
|
|
783
|
+
"queries",
|
|
784
|
+
"results",
|
|
785
|
+
"status",
|
|
786
|
+
"index",
|
|
787
|
+
):
|
|
788
|
+
file_search_call[key] = block[key]
|
|
789
|
+
|
|
790
|
+
yield cast("types.ServerToolCall", file_search_call)
|
|
791
|
+
|
|
792
|
+
file_search_result = {
|
|
793
|
+
"type": "server_tool_result",
|
|
794
|
+
"tool_call_id": block["id"],
|
|
795
|
+
}
|
|
796
|
+
if file_search_output := block.get("results"):
|
|
797
|
+
file_search_result["output"] = file_search_output
|
|
798
|
+
|
|
799
|
+
status = block.get("status")
|
|
800
|
+
if status == "failed":
|
|
801
|
+
file_search_result["status"] = "error"
|
|
802
|
+
elif status == "completed":
|
|
803
|
+
file_search_result["status"] = "success"
|
|
804
|
+
elif status:
|
|
805
|
+
file_search_result["extras"] = {"status": status}
|
|
806
|
+
else:
|
|
807
|
+
pass
|
|
808
|
+
if "index" in block and isinstance(block["index"], int):
|
|
809
|
+
file_search_result["index"] = f"lc_fsr_{block['index'] + 1}"
|
|
810
|
+
yield cast("types.ServerToolResult", file_search_result)
|
|
743
811
|
|
|
744
812
|
elif block_type == "code_interpreter_call":
|
|
745
813
|
code_interpreter_call = {
|
|
746
|
-
"type": "
|
|
814
|
+
"type": "server_tool_call",
|
|
815
|
+
"name": "code_interpreter",
|
|
747
816
|
"id": block["id"],
|
|
748
817
|
}
|
|
749
818
|
if "code" in block:
|
|
750
|
-
code_interpreter_call["
|
|
819
|
+
code_interpreter_call["args"] = {"code": block["code"]}
|
|
751
820
|
if "index" in block:
|
|
752
821
|
code_interpreter_call["index"] = f"lc_cic_{block['index']}"
|
|
753
|
-
known_fields = {
|
|
822
|
+
known_fields = {
|
|
823
|
+
"type",
|
|
824
|
+
"id",
|
|
825
|
+
"outputs",
|
|
826
|
+
"status",
|
|
827
|
+
"code",
|
|
828
|
+
"extras",
|
|
829
|
+
"index",
|
|
830
|
+
}
|
|
754
831
|
for key in block:
|
|
755
832
|
if key not in known_fields:
|
|
756
833
|
if "extras" not in code_interpreter_call:
|
|
@@ -758,33 +835,138 @@ def _convert_to_v1_from_responses(message: AIMessage) -> list[types.ContentBlock
|
|
|
758
835
|
code_interpreter_call["extras"][key] = block[key]
|
|
759
836
|
|
|
760
837
|
code_interpreter_result = {
|
|
761
|
-
"type": "
|
|
762
|
-
"
|
|
838
|
+
"type": "server_tool_result",
|
|
839
|
+
"tool_call_id": block["id"],
|
|
763
840
|
}
|
|
764
841
|
if "outputs" in block:
|
|
765
|
-
code_interpreter_result["
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
"type": "code_interpreter_output",
|
|
777
|
-
"stdout": output.get("logs", ""),
|
|
778
|
-
}
|
|
779
|
-
)
|
|
780
|
-
|
|
781
|
-
if "status" in block:
|
|
782
|
-
code_interpreter_result["status"] = block["status"]
|
|
842
|
+
code_interpreter_result["output"] = block["outputs"]
|
|
843
|
+
|
|
844
|
+
status = block.get("status")
|
|
845
|
+
if status == "failed":
|
|
846
|
+
code_interpreter_result["status"] = "error"
|
|
847
|
+
elif status == "completed":
|
|
848
|
+
code_interpreter_result["status"] = "success"
|
|
849
|
+
elif status:
|
|
850
|
+
code_interpreter_result["extras"] = {"status": status}
|
|
851
|
+
else:
|
|
852
|
+
pass
|
|
783
853
|
if "index" in block and isinstance(block["index"], int):
|
|
784
854
|
code_interpreter_result["index"] = f"lc_cir_{block['index'] + 1}"
|
|
785
855
|
|
|
786
|
-
yield cast("types.
|
|
787
|
-
yield cast("types.
|
|
856
|
+
yield cast("types.ServerToolCall", code_interpreter_call)
|
|
857
|
+
yield cast("types.ServerToolResult", code_interpreter_result)
|
|
858
|
+
|
|
859
|
+
elif block_type == "mcp_call":
|
|
860
|
+
mcp_call = {
|
|
861
|
+
"type": "server_tool_call",
|
|
862
|
+
"name": "remote_mcp",
|
|
863
|
+
"id": block["id"],
|
|
864
|
+
}
|
|
865
|
+
if (arguments := block.get("arguments")) and isinstance(arguments, str):
|
|
866
|
+
try:
|
|
867
|
+
mcp_call["args"] = json.loads(block["arguments"])
|
|
868
|
+
except json.JSONDecodeError:
|
|
869
|
+
mcp_call["extras"] = {"arguments": arguments}
|
|
870
|
+
if "name" in block:
|
|
871
|
+
if "extras" not in mcp_call:
|
|
872
|
+
mcp_call["extras"] = {}
|
|
873
|
+
mcp_call["extras"]["tool_name"] = block["name"]
|
|
874
|
+
if "server_label" in block:
|
|
875
|
+
if "extras" not in mcp_call:
|
|
876
|
+
mcp_call["extras"] = {}
|
|
877
|
+
mcp_call["extras"]["server_label"] = block["server_label"]
|
|
878
|
+
if "index" in block:
|
|
879
|
+
mcp_call["index"] = f"lc_mcp_{block['index']}"
|
|
880
|
+
known_fields = {
|
|
881
|
+
"type",
|
|
882
|
+
"id",
|
|
883
|
+
"arguments",
|
|
884
|
+
"name",
|
|
885
|
+
"server_label",
|
|
886
|
+
"output",
|
|
887
|
+
"error",
|
|
888
|
+
"extras",
|
|
889
|
+
"index",
|
|
890
|
+
}
|
|
891
|
+
for key in block:
|
|
892
|
+
if key not in known_fields:
|
|
893
|
+
if "extras" not in mcp_call:
|
|
894
|
+
mcp_call["extras"] = {}
|
|
895
|
+
mcp_call["extras"][key] = block[key]
|
|
896
|
+
|
|
897
|
+
yield cast("types.ServerToolCall", mcp_call)
|
|
898
|
+
|
|
899
|
+
mcp_result = {
|
|
900
|
+
"type": "server_tool_result",
|
|
901
|
+
"tool_call_id": block["id"],
|
|
902
|
+
}
|
|
903
|
+
if mcp_output := block.get("output"):
|
|
904
|
+
mcp_result["output"] = mcp_output
|
|
905
|
+
|
|
906
|
+
error = block.get("error")
|
|
907
|
+
if error:
|
|
908
|
+
if "extras" not in mcp_result:
|
|
909
|
+
mcp_result["extras"] = {}
|
|
910
|
+
mcp_result["extras"]["error"] = error
|
|
911
|
+
mcp_result["status"] = "error"
|
|
912
|
+
else:
|
|
913
|
+
mcp_result["status"] = "success"
|
|
914
|
+
|
|
915
|
+
if "index" in block and isinstance(block["index"], int):
|
|
916
|
+
mcp_result["index"] = f"lc_mcpr_{block['index'] + 1}"
|
|
917
|
+
yield cast("types.ServerToolResult", mcp_result)
|
|
918
|
+
|
|
919
|
+
elif block_type == "mcp_list_tools":
|
|
920
|
+
mcp_list_tools_call = {
|
|
921
|
+
"type": "server_tool_call",
|
|
922
|
+
"name": "mcp_list_tools",
|
|
923
|
+
"args": {},
|
|
924
|
+
"id": block["id"],
|
|
925
|
+
}
|
|
926
|
+
if "server_label" in block:
|
|
927
|
+
mcp_list_tools_call["extras"] = {}
|
|
928
|
+
mcp_list_tools_call["extras"]["server_label"] = block[
|
|
929
|
+
"server_label"
|
|
930
|
+
]
|
|
931
|
+
if "index" in block:
|
|
932
|
+
mcp_list_tools_call["index"] = f"lc_mlt_{block['index']}"
|
|
933
|
+
known_fields = {
|
|
934
|
+
"type",
|
|
935
|
+
"id",
|
|
936
|
+
"name",
|
|
937
|
+
"server_label",
|
|
938
|
+
"tools",
|
|
939
|
+
"error",
|
|
940
|
+
"extras",
|
|
941
|
+
"index",
|
|
942
|
+
}
|
|
943
|
+
for key in block:
|
|
944
|
+
if key not in known_fields:
|
|
945
|
+
if "extras" not in mcp_list_tools_call:
|
|
946
|
+
mcp_list_tools_call["extras"] = {}
|
|
947
|
+
mcp_list_tools_call["extras"][key] = block[key]
|
|
948
|
+
|
|
949
|
+
yield cast("types.ServerToolCall", mcp_list_tools_call)
|
|
950
|
+
|
|
951
|
+
mcp_list_tools_result = {
|
|
952
|
+
"type": "server_tool_result",
|
|
953
|
+
"tool_call_id": block["id"],
|
|
954
|
+
}
|
|
955
|
+
if mcp_output := block.get("tools"):
|
|
956
|
+
mcp_list_tools_result["output"] = mcp_output
|
|
957
|
+
|
|
958
|
+
error = block.get("error")
|
|
959
|
+
if error:
|
|
960
|
+
if "extras" not in mcp_list_tools_result:
|
|
961
|
+
mcp_list_tools_result["extras"] = {}
|
|
962
|
+
mcp_list_tools_result["extras"]["error"] = error
|
|
963
|
+
mcp_list_tools_result["status"] = "error"
|
|
964
|
+
else:
|
|
965
|
+
mcp_list_tools_result["status"] = "success"
|
|
966
|
+
|
|
967
|
+
if "index" in block and isinstance(block["index"], int):
|
|
968
|
+
mcp_list_tools_result["index"] = f"lc_mltr_{block['index'] + 1}"
|
|
969
|
+
yield cast("types.ServerToolResult", mcp_list_tools_result)
|
|
788
970
|
|
|
789
971
|
elif block_type in types.KNOWN_BLOCK_TYPES:
|
|
790
972
|
yield cast("types.ContentBlock", block)
|
langchain_core/messages/chat.py
CHANGED
|
@@ -30,7 +30,10 @@ class ChatMessageChunk(ChatMessage, BaseMessageChunk):
|
|
|
30
30
|
# non-chunk variant.
|
|
31
31
|
type: Literal["ChatMessageChunk"] = "ChatMessageChunk" # type: ignore[assignment]
|
|
32
32
|
"""The type of the message (used during serialization).
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
Defaults to ``'ChatMessageChunk'``.
|
|
35
|
+
|
|
36
|
+
"""
|
|
34
37
|
|
|
35
38
|
@override
|
|
36
39
|
def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
|