langchain-core 1.0.0a1__py3-none-any.whl → 1.0.0a2__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.
@@ -214,6 +214,8 @@ def _normalize_messages(
214
214
  """
215
215
  from langchain_core.messages.block_translators.langchain_v0 import (
216
216
  _convert_legacy_v0_content_block_to_v1,
217
+ )
218
+ from langchain_core.messages.block_translators.openai import (
217
219
  _convert_openai_format_to_data_block,
218
220
  )
219
221
 
@@ -44,11 +44,13 @@ from langchain_core.messages import (
44
44
  BaseMessage,
45
45
  HumanMessage,
46
46
  convert_to_messages,
47
- convert_to_openai_data_block,
48
- convert_to_openai_image_block,
49
47
  is_data_content_block,
50
48
  message_chunk_to_message,
51
49
  )
50
+ from langchain_core.messages.block_translators.openai import (
51
+ convert_to_openai_data_block,
52
+ convert_to_openai_image_block,
53
+ )
52
54
  from langchain_core.outputs import (
53
55
  ChatGeneration,
54
56
  ChatGenerationChunk,
@@ -32,6 +32,10 @@ if TYPE_CHECKING:
32
32
  message_to_dict,
33
33
  messages_to_dict,
34
34
  )
35
+ from langchain_core.messages.block_translators.openai import (
36
+ convert_to_openai_data_block,
37
+ convert_to_openai_image_block,
38
+ )
35
39
  from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
36
40
  from langchain_core.messages.content import (
37
41
  Annotation,
@@ -52,13 +56,7 @@ if TYPE_CHECKING:
52
56
  VideoContentBlock,
53
57
  WebSearchCall,
54
58
  WebSearchResult,
55
- convert_to_openai_data_block,
56
- convert_to_openai_image_block,
57
59
  is_data_content_block,
58
- is_reasoning_block,
59
- is_text_block,
60
- is_tool_call_block,
61
- is_tool_call_chunk,
62
60
  )
63
61
  from langchain_core.messages.function import FunctionMessage, FunctionMessageChunk
64
62
  from langchain_core.messages.human import HumanMessage, HumanMessageChunk
@@ -135,10 +133,6 @@ __all__ = (
135
133
  "filter_messages",
136
134
  "get_buffer_string",
137
135
  "is_data_content_block",
138
- "is_reasoning_block",
139
- "is_text_block",
140
- "is_tool_call_block",
141
- "is_tool_call_chunk",
142
136
  "merge_content",
143
137
  "merge_message_runs",
144
138
  "message_chunk_to_message",
@@ -192,16 +186,12 @@ _dynamic_imports = {
192
186
  "MessageLikeRepresentation": "utils",
193
187
  "_message_from_dict": "utils",
194
188
  "convert_to_messages": "utils",
195
- "convert_to_openai_data_block": "content",
196
- "convert_to_openai_image_block": "content",
189
+ "convert_to_openai_data_block": "block_translators.openai",
190
+ "convert_to_openai_image_block": "block_translators.openai",
197
191
  "convert_to_openai_messages": "utils",
198
192
  "filter_messages": "utils",
199
193
  "get_buffer_string": "utils",
200
194
  "is_data_content_block": "content",
201
- "is_reasoning_block": "content",
202
- "is_text_block": "content",
203
- "is_tool_call_block": "content",
204
- "is_tool_call_chunk": "content",
205
195
  "merge_message_runs": "utils",
206
196
  "message_chunk_to_message": "utils",
207
197
  "messages_from_dict": "utils",
@@ -2,7 +2,6 @@
2
2
 
3
3
  from typing import Any, Union, cast
4
4
 
5
- from langchain_core.language_models._utils import _parse_data_uri
6
5
  from langchain_core.messages import content as types
7
6
 
8
7
 
@@ -11,14 +10,15 @@ def _convert_v0_multimodal_input_to_v1(
11
10
  ) -> list[types.ContentBlock]:
12
11
  """Convert v0 multimodal blocks to v1 format.
13
12
 
14
- Processes non_standard blocks that might be v0 format and converts them
15
- to proper v1 ContentBlocks.
13
+ Processes ``'non_standard'`` blocks that might be v0 format and converts them
14
+ to proper v1 ``ContentBlock``.
16
15
 
17
16
  Args:
18
17
  blocks: List of content blocks to process.
19
18
 
20
19
  Returns:
21
20
  Updated list with v0 blocks converted to v1 format.
21
+
22
22
  """
23
23
  converted_blocks = []
24
24
  unpacked_blocks: list[dict[str, Any]] = [
@@ -162,136 +162,3 @@ def _convert_legacy_v0_content_block_to_v1(
162
162
 
163
163
  # If we can't convert, return the block unchanged
164
164
  return block
165
-
166
-
167
- def _convert_openai_format_to_data_block(
168
- block: dict,
169
- ) -> Union[types.ContentBlock, dict[Any, Any]]:
170
- """Convert OpenAI image/audio/file content block to respective v1 multimodal block.
171
-
172
- We expect that the incoming block is verified to be in OpenAI Chat Completions
173
- format.
174
-
175
- If parsing fails, passes block through unchanged.
176
-
177
- Mappings (Chat Completions to LangChain v1):
178
- - Image -> `ImageContentBlock`
179
- - Audio -> `AudioContentBlock`
180
- - File -> `FileContentBlock`
181
-
182
- """
183
-
184
- # Extract extra keys to put them in `extras`
185
- def _extract_extras(block_dict: dict, known_keys: set[str]) -> dict[str, Any]:
186
- """Extract unknown keys from block to preserve as extras."""
187
- return {k: v for k, v in block_dict.items() if k not in known_keys}
188
-
189
- # base64-style image block
190
- if (block["type"] == "image_url") and (
191
- parsed := _parse_data_uri(block["image_url"]["url"])
192
- ):
193
- known_keys = {"type", "image_url"}
194
- extras = _extract_extras(block, known_keys)
195
-
196
- # Also extract extras from nested image_url dict
197
- image_url_known_keys = {"url"}
198
- image_url_extras = _extract_extras(block["image_url"], image_url_known_keys)
199
-
200
- # Merge extras
201
- all_extras = {**extras}
202
- for key, value in image_url_extras.items():
203
- if key == "detail": # Don't rename
204
- all_extras["detail"] = value
205
- else:
206
- all_extras[f"image_url_{key}"] = value
207
-
208
- return types.create_image_block(
209
- # Even though this is labeled as `url`, it can be base64-encoded
210
- base64=parsed["data"],
211
- mime_type=parsed["mime_type"],
212
- **all_extras,
213
- )
214
-
215
- # url-style image block
216
- if (block["type"] == "image_url") and isinstance(
217
- block["image_url"].get("url"), str
218
- ):
219
- known_keys = {"type", "image_url"}
220
- extras = _extract_extras(block, known_keys)
221
-
222
- image_url_known_keys = {"url"}
223
- image_url_extras = _extract_extras(block["image_url"], image_url_known_keys)
224
-
225
- all_extras = {**extras}
226
- for key, value in image_url_extras.items():
227
- if key == "detail": # Don't rename
228
- all_extras["detail"] = value
229
- else:
230
- all_extras[f"image_url_{key}"] = value
231
-
232
- return types.create_image_block(
233
- url=block["image_url"]["url"],
234
- **all_extras,
235
- )
236
-
237
- # base64-style audio block
238
- # audio is only represented via raw data, no url or ID option
239
- if block["type"] == "input_audio":
240
- known_keys = {"type", "input_audio"}
241
- extras = _extract_extras(block, known_keys)
242
-
243
- # Also extract extras from nested audio dict
244
- audio_known_keys = {"data", "format"}
245
- audio_extras = _extract_extras(block["input_audio"], audio_known_keys)
246
-
247
- all_extras = {**extras}
248
- for key, value in audio_extras.items():
249
- all_extras[f"audio_{key}"] = value
250
-
251
- return types.create_audio_block(
252
- base64=block["input_audio"]["data"],
253
- mime_type=f"audio/{block['input_audio']['format']}",
254
- **all_extras,
255
- )
256
-
257
- # id-style file block
258
- if block.get("type") == "file" and "file_id" in block.get("file", {}):
259
- known_keys = {"type", "file"}
260
- extras = _extract_extras(block, known_keys)
261
-
262
- file_known_keys = {"file_id"}
263
- file_extras = _extract_extras(block["file"], file_known_keys)
264
-
265
- all_extras = {**extras}
266
- for key, value in file_extras.items():
267
- all_extras[f"file_{key}"] = value
268
-
269
- return types.create_file_block(
270
- file_id=block["file"]["file_id"],
271
- **all_extras,
272
- )
273
-
274
- # base64-style file block
275
- if (block["type"] == "file") and (
276
- parsed := _parse_data_uri(block["file"]["file_data"])
277
- ):
278
- known_keys = {"type", "file"}
279
- extras = _extract_extras(block, known_keys)
280
-
281
- file_known_keys = {"file_data", "filename"}
282
- file_extras = _extract_extras(block["file"], file_known_keys)
283
-
284
- all_extras = {**extras}
285
- for key, value in file_extras.items():
286
- all_extras[f"file_{key}"] = value
287
-
288
- filename = block["file"].get("filename")
289
- return types.create_file_block(
290
- base64=parsed["data"],
291
- mime_type="application/pdf",
292
- filename=filename,
293
- **all_extras,
294
- )
295
-
296
- # Escape hatch
297
- return block
@@ -3,21 +3,100 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
+ import warnings
6
7
  from collections.abc import Iterable
7
8
  from typing import TYPE_CHECKING, Any, Optional, Union, cast
8
9
 
9
10
  from langchain_core.language_models._utils import (
10
11
  _is_openai_data_block,
12
+ _parse_data_uri,
11
13
  )
12
14
  from langchain_core.messages import content as types
13
- from langchain_core.messages.block_translators.langchain_v0 import (
14
- _convert_openai_format_to_data_block,
15
- )
16
15
 
17
16
  if TYPE_CHECKING:
18
17
  from langchain_core.messages import AIMessage, AIMessageChunk
19
18
 
20
19
 
20
+ def convert_to_openai_image_block(block: dict[str, Any]) -> dict:
21
+ """Convert ``ImageContentBlock`` to format expected by OpenAI Chat Completions."""
22
+ if "url" in block:
23
+ return {
24
+ "type": "image_url",
25
+ "image_url": {
26
+ "url": block["url"],
27
+ },
28
+ }
29
+ if "base64" in block or block.get("source_type") == "base64":
30
+ if "mime_type" not in block:
31
+ error_message = "mime_type key is required for base64 data."
32
+ raise ValueError(error_message)
33
+ mime_type = block["mime_type"]
34
+ base64_data = block["data"] if "data" in block else block["base64"]
35
+ return {
36
+ "type": "image_url",
37
+ "image_url": {
38
+ "url": f"data:{mime_type};base64,{base64_data}",
39
+ },
40
+ }
41
+ error_message = "Unsupported source type. Only 'url' and 'base64' are supported."
42
+ raise ValueError(error_message)
43
+
44
+
45
+ def convert_to_openai_data_block(block: dict) -> dict:
46
+ """Format standard data content block to format expected by OpenAI."""
47
+ if block["type"] == "image":
48
+ formatted_block = convert_to_openai_image_block(block)
49
+
50
+ elif block["type"] == "file":
51
+ if "base64" in block or block.get("source_type") == "base64":
52
+ # Handle v0 format: {"source_type": "base64", "data": "...", ...}
53
+ # Handle v1 format: {"base64": "...", ...}
54
+ base64_data = block["data"] if "source_type" in block else block["base64"]
55
+ file = {"file_data": f"data:{block['mime_type']};base64,{base64_data}"}
56
+ if filename := block.get("filename"):
57
+ file["filename"] = filename
58
+ elif (extras := block.get("extras")) and ("filename" in extras):
59
+ file["filename"] = extras["filename"]
60
+ elif (extras := block.get("metadata")) and ("filename" in extras):
61
+ # Backward compat
62
+ file["filename"] = extras["filename"]
63
+ else:
64
+ warnings.warn(
65
+ "OpenAI may require a filename for file inputs. Specify a filename "
66
+ "in the content block: {'type': 'file', 'mime_type': "
67
+ "'application/pdf', 'base64': '...', 'filename': 'my-pdf'}",
68
+ stacklevel=1,
69
+ )
70
+ formatted_block = {"type": "file", "file": file}
71
+ elif "file_id" in block or block.get("source_type") == "id":
72
+ # Handle v0 format: {"source_type": "id", "id": "...", ...}
73
+ # Handle v1 format: {"file_id": "...", ...}
74
+ file_id = block["id"] if "source_type" in block else block["file_id"]
75
+ formatted_block = {"type": "file", "file": {"file_id": file_id}}
76
+ else:
77
+ error_msg = "Keys base64 or file_id required for file blocks."
78
+ raise ValueError(error_msg)
79
+
80
+ elif block["type"] == "audio":
81
+ if "base64" in block or block.get("source_type") == "base64":
82
+ # Handle v0 format: {"source_type": "base64", "data": "...", ...}
83
+ # Handle v1 format: {"base64": "...", ...}
84
+ base64_data = block["data"] if "source_type" in block else block["base64"]
85
+ audio_format = block["mime_type"].split("/")[-1]
86
+ formatted_block = {
87
+ "type": "input_audio",
88
+ "input_audio": {"data": base64_data, "format": audio_format},
89
+ }
90
+ else:
91
+ error_msg = "Key base64 is required for audio blocks."
92
+ raise ValueError(error_msg)
93
+ else:
94
+ error_msg = f"Block of type {block['type']} is not supported."
95
+ raise ValueError(error_msg)
96
+
97
+ return formatted_block
98
+
99
+
21
100
  # v1 / Chat Completions
22
101
  def _convert_to_v1_from_chat_completions(
23
102
  message: AIMessage,
@@ -288,6 +367,139 @@ def _convert_from_v03_ai_message(message: AIMessage) -> AIMessage:
288
367
  )
289
368
 
290
369
 
370
+ def _convert_openai_format_to_data_block(
371
+ block: dict,
372
+ ) -> Union[types.ContentBlock, dict[Any, Any]]:
373
+ """Convert OpenAI image/audio/file content block to respective v1 multimodal block.
374
+
375
+ We expect that the incoming block is verified to be in OpenAI Chat Completions
376
+ format.
377
+
378
+ If parsing fails, passes block through unchanged.
379
+
380
+ Mappings (Chat Completions to LangChain v1):
381
+ - Image -> `ImageContentBlock`
382
+ - Audio -> `AudioContentBlock`
383
+ - File -> `FileContentBlock`
384
+
385
+ """
386
+
387
+ # Extract extra keys to put them in `extras`
388
+ def _extract_extras(block_dict: dict, known_keys: set[str]) -> dict[str, Any]:
389
+ """Extract unknown keys from block to preserve as extras."""
390
+ return {k: v for k, v in block_dict.items() if k not in known_keys}
391
+
392
+ # base64-style image block
393
+ if (block["type"] == "image_url") and (
394
+ parsed := _parse_data_uri(block["image_url"]["url"])
395
+ ):
396
+ known_keys = {"type", "image_url"}
397
+ extras = _extract_extras(block, known_keys)
398
+
399
+ # Also extract extras from nested image_url dict
400
+ image_url_known_keys = {"url"}
401
+ image_url_extras = _extract_extras(block["image_url"], image_url_known_keys)
402
+
403
+ # Merge extras
404
+ all_extras = {**extras}
405
+ for key, value in image_url_extras.items():
406
+ if key == "detail": # Don't rename
407
+ all_extras["detail"] = value
408
+ else:
409
+ all_extras[f"image_url_{key}"] = value
410
+
411
+ return types.create_image_block(
412
+ # Even though this is labeled as `url`, it can be base64-encoded
413
+ base64=parsed["data"],
414
+ mime_type=parsed["mime_type"],
415
+ **all_extras,
416
+ )
417
+
418
+ # url-style image block
419
+ if (block["type"] == "image_url") and isinstance(
420
+ block["image_url"].get("url"), str
421
+ ):
422
+ known_keys = {"type", "image_url"}
423
+ extras = _extract_extras(block, known_keys)
424
+
425
+ image_url_known_keys = {"url"}
426
+ image_url_extras = _extract_extras(block["image_url"], image_url_known_keys)
427
+
428
+ all_extras = {**extras}
429
+ for key, value in image_url_extras.items():
430
+ if key == "detail": # Don't rename
431
+ all_extras["detail"] = value
432
+ else:
433
+ all_extras[f"image_url_{key}"] = value
434
+
435
+ return types.create_image_block(
436
+ url=block["image_url"]["url"],
437
+ **all_extras,
438
+ )
439
+
440
+ # base64-style audio block
441
+ # audio is only represented via raw data, no url or ID option
442
+ if block["type"] == "input_audio":
443
+ known_keys = {"type", "input_audio"}
444
+ extras = _extract_extras(block, known_keys)
445
+
446
+ # Also extract extras from nested audio dict
447
+ audio_known_keys = {"data", "format"}
448
+ audio_extras = _extract_extras(block["input_audio"], audio_known_keys)
449
+
450
+ all_extras = {**extras}
451
+ for key, value in audio_extras.items():
452
+ all_extras[f"audio_{key}"] = value
453
+
454
+ return types.create_audio_block(
455
+ base64=block["input_audio"]["data"],
456
+ mime_type=f"audio/{block['input_audio']['format']}",
457
+ **all_extras,
458
+ )
459
+
460
+ # id-style file block
461
+ if block.get("type") == "file" and "file_id" in block.get("file", {}):
462
+ known_keys = {"type", "file"}
463
+ extras = _extract_extras(block, known_keys)
464
+
465
+ file_known_keys = {"file_id"}
466
+ file_extras = _extract_extras(block["file"], file_known_keys)
467
+
468
+ all_extras = {**extras}
469
+ for key, value in file_extras.items():
470
+ all_extras[f"file_{key}"] = value
471
+
472
+ return types.create_file_block(
473
+ file_id=block["file"]["file_id"],
474
+ **all_extras,
475
+ )
476
+
477
+ # base64-style file block
478
+ if (block["type"] == "file") and (
479
+ parsed := _parse_data_uri(block["file"]["file_data"])
480
+ ):
481
+ known_keys = {"type", "file"}
482
+ extras = _extract_extras(block, known_keys)
483
+
484
+ file_known_keys = {"file_data", "filename"}
485
+ file_extras = _extract_extras(block["file"], file_known_keys)
486
+
487
+ all_extras = {**extras}
488
+ for key, value in file_extras.items():
489
+ all_extras[f"file_{key}"] = value
490
+
491
+ filename = block["file"].get("filename")
492
+ return types.create_file_block(
493
+ base64=parsed["data"],
494
+ mime_type="application/pdf",
495
+ filename=filename,
496
+ **all_extras,
497
+ )
498
+
499
+ # Escape hatch
500
+ return block
501
+
502
+
291
503
  # v1 / Responses
292
504
  def _convert_annotation_to_v1(annotation: dict[str, Any]) -> types.Annotation:
293
505
  annotation_type = annotation.get("type")
@@ -83,7 +83,7 @@ The module defines several types of content blocks, including:
83
83
 
84
84
  - ``TextContentBlock``: Standard text output.
85
85
  - ``Citation``: For annotations that link text output to a source document.
86
- - ``ToolCallContentBlock``: For function calling.
86
+ - ``ToolCall``: For function calling.
87
87
  - ``ReasoningContentBlock``: To capture a model's thought process.
88
88
  - Multimodal data:
89
89
  - ``ImageContentBlock``
@@ -129,10 +129,9 @@ Factory functions offer benefits such as:
129
129
 
130
130
  """
131
131
 
132
- import warnings
133
132
  from typing import Any, Literal, Optional, Union, get_args, get_type_hints
134
133
 
135
- from typing_extensions import NotRequired, TypedDict, TypeGuard
134
+ from typing_extensions import NotRequired, TypedDict
136
135
 
137
136
  from langchain_core.utils.utils import ensure_id
138
137
 
@@ -834,7 +833,7 @@ class NonStandardContentBlock(TypedDict):
834
833
  The purpose of this block should be to simply hold a provider-specific payload.
835
834
  If a provider's non-standard output includes reasoning and tool calls, it should be
836
835
  the adapter's job to parse that payload and emit the corresponding standard
837
- ``ReasoningContentBlock`` and ``ToolCallContentBlocks``.
836
+ ``ReasoningContentBlock`` and ``ToolCalls``.
838
837
 
839
838
  Has no ``extras`` field, as provider-specific data should be included in the
840
839
  ``value`` field.
@@ -966,113 +965,6 @@ def is_data_content_block(block: dict) -> bool:
966
965
  return False
967
966
 
968
967
 
969
- def is_tool_call_block(block: ContentBlock) -> TypeGuard[ToolCall]:
970
- """Type guard to check if a content block is a ``ToolCall``."""
971
- return block.get("type") == "tool_call"
972
-
973
-
974
- def is_tool_call_chunk(block: ContentBlock) -> TypeGuard[ToolCallChunk]:
975
- """Type guard to check if a content block is a ``ToolCallChunk``."""
976
- return block.get("type") == "tool_call_chunk"
977
-
978
-
979
- def is_text_block(block: ContentBlock) -> TypeGuard[TextContentBlock]:
980
- """Type guard to check if a content block is a ``TextContentBlock``."""
981
- return block.get("type") == "text"
982
-
983
-
984
- def is_reasoning_block(block: ContentBlock) -> TypeGuard[ReasoningContentBlock]:
985
- """Type guard to check if a content block is a ``ReasoningContentBlock``."""
986
- return block.get("type") == "reasoning"
987
-
988
-
989
- def is_invalid_tool_call_block(
990
- block: ContentBlock,
991
- ) -> TypeGuard[InvalidToolCall]:
992
- """Type guard to check if a content block is an ``InvalidToolCall``."""
993
- return block.get("type") == "invalid_tool_call"
994
-
995
-
996
- def convert_to_openai_image_block(block: dict[str, Any]) -> dict:
997
- """Convert ``ImageContentBlock`` to format expected by OpenAI Chat Completions."""
998
- if "url" in block:
999
- return {
1000
- "type": "image_url",
1001
- "image_url": {
1002
- "url": block["url"],
1003
- },
1004
- }
1005
- if "base64" in block or block.get("source_type") == "base64":
1006
- if "mime_type" not in block:
1007
- error_message = "mime_type key is required for base64 data."
1008
- raise ValueError(error_message)
1009
- mime_type = block["mime_type"]
1010
- base64_data = block["data"] if "data" in block else block["base64"]
1011
- return {
1012
- "type": "image_url",
1013
- "image_url": {
1014
- "url": f"data:{mime_type};base64,{base64_data}",
1015
- },
1016
- }
1017
- error_message = "Unsupported source type. Only 'url' and 'base64' are supported."
1018
- raise ValueError(error_message)
1019
-
1020
-
1021
- def convert_to_openai_data_block(block: dict) -> dict:
1022
- """Format standard data content block to format expected by OpenAI."""
1023
- if block["type"] == "image":
1024
- formatted_block = convert_to_openai_image_block(block)
1025
-
1026
- elif block["type"] == "file":
1027
- if "base64" in block or block.get("source_type") == "base64":
1028
- # Handle v0 format: {"source_type": "base64", "data": "...", ...}
1029
- # Handle v1 format: {"base64": "...", ...}
1030
- base64_data = block["data"] if "source_type" in block else block["base64"]
1031
- file = {"file_data": f"data:{block['mime_type']};base64,{base64_data}"}
1032
- if filename := block.get("filename"):
1033
- file["filename"] = filename
1034
- elif (extras := block.get("extras")) and ("filename" in extras):
1035
- file["filename"] = extras["filename"]
1036
- elif (extras := block.get("metadata")) and ("filename" in extras):
1037
- # Backward compat
1038
- file["filename"] = extras["filename"]
1039
- else:
1040
- warnings.warn(
1041
- "OpenAI may require a filename for file inputs. Specify a filename "
1042
- "in the content block: {'type': 'file', 'mime_type': "
1043
- "'application/pdf', 'base64': '...', 'filename': 'my-pdf'}",
1044
- stacklevel=1,
1045
- )
1046
- formatted_block = {"type": "file", "file": file}
1047
- elif "file_id" in block or block.get("source_type") == "id":
1048
- # Handle v0 format: {"source_type": "id", "id": "...", ...}
1049
- # Handle v1 format: {"file_id": "...", ...}
1050
- file_id = block["id"] if "source_type" in block else block["file_id"]
1051
- formatted_block = {"type": "file", "file": {"file_id": file_id}}
1052
- else:
1053
- error_msg = "Keys base64 or file_id required for file blocks."
1054
- raise ValueError(error_msg)
1055
-
1056
- elif block["type"] == "audio":
1057
- if "base64" in block or block.get("source_type") == "base64":
1058
- # Handle v0 format: {"source_type": "base64", "data": "...", ...}
1059
- # Handle v1 format: {"base64": "...", ...}
1060
- base64_data = block["data"] if "source_type" in block else block["base64"]
1061
- audio_format = block["mime_type"].split("/")[-1]
1062
- formatted_block = {
1063
- "type": "input_audio",
1064
- "input_audio": {"data": base64_data, "format": audio_format},
1065
- }
1066
- else:
1067
- error_msg = "Key base64 is required for audio blocks."
1068
- raise ValueError(error_msg)
1069
- else:
1070
- error_msg = f"Block of type {block['type']} is not supported."
1071
- raise ValueError(error_msg)
1072
-
1073
- return formatted_block
1074
-
1075
-
1076
968
  def create_text_block(
1077
969
  text: str,
1078
970
  *,
@@ -33,9 +33,11 @@ from pydantic import Discriminator, Field, Tag
33
33
  from langchain_core.exceptions import ErrorCode, create_message
34
34
  from langchain_core.messages.ai import AIMessage, AIMessageChunk
35
35
  from langchain_core.messages.base import BaseMessage, BaseMessageChunk
36
+ from langchain_core.messages.block_translators.openai import (
37
+ convert_to_openai_data_block,
38
+ )
36
39
  from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
37
40
  from langchain_core.messages.content import (
38
- convert_to_openai_data_block,
39
41
  is_data_content_block,
40
42
  )
41
43
  from langchain_core.messages.function import FunctionMessage, FunctionMessageChunk
langchain_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """langchain-core version information and utilities."""
2
2
 
3
- VERSION = "1.0.0a1"
3
+ VERSION = "1.0.0a2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langchain-core
3
- Version: 1.0.0a1
3
+ Version: 1.0.0a2
4
4
  Summary: Building applications with LLMs through composability
5
5
  License: MIT
6
6
  Project-URL: Source Code, https://github.com/langchain-ai/langchain/tree/master/libs/core
@@ -1,6 +1,6 @@
1
- langchain_core-1.0.0a1.dist-info/METADATA,sha256=SQGbriHdvIcGvwox-0Y4trQZu8CouYfDZw8QlxCGFgg,5716
2
- langchain_core-1.0.0a1.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
- langchain_core-1.0.0a1.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1
+ langchain_core-1.0.0a2.dist-info/METADATA,sha256=kN6BukdKVgkYPdefzaRcg-LGqdUjxyfzI1JfwEldMPU,5716
2
+ langchain_core-1.0.0a2.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
+ langchain_core-1.0.0a2.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
4
  langchain_core/__init__.py,sha256=TgvhxbrjCRVJwr2HddiyHvtH8W94K-uLM6-6ifNIBXo,713
5
5
  langchain_core/_api/__init__.py,sha256=WDOMw4faVuscjDCL5ttnRQNienJP_M9vGMmJUXS6L5w,1976
6
6
  langchain_core/_api/beta_decorator.py,sha256=uN-N3vGj7-56mNbXw-eh7I-Cvgrt4V4YOoz-7jLQl1Y,9908
@@ -46,9 +46,9 @@ langchain_core/indexing/api.py,sha256=z620e6bVNUKH_2bbVGrSqQiMVfVAJQl_iyQCSBiMmY
46
46
  langchain_core/indexing/base.py,sha256=NGqIzktMBlzqK_AN3xF42DC9tP6uM0EJzBr-rJnHDu8,23298
47
47
  langchain_core/indexing/in_memory.py,sha256=-qyKjAWJFWxtH_MbUu3JJct0x3R_pbHyHuxA4Cra1nA,2709
48
48
  langchain_core/language_models/__init__.py,sha256=j6OXr7CriShFr7BYfCWZ2kOTEZpzvlE7dNDTab75prg,3778
49
- langchain_core/language_models/_utils.py,sha256=SNhec_vW0RLE5SXHvF4npT8vGL-2GPDY8EPqcAVvY_c,10111
49
+ langchain_core/language_models/_utils.py,sha256=3-hXk8zeiERVLZ1B-5nzOxet1npu625W_EUn2kmiSVI,10184
50
50
  langchain_core/language_models/base.py,sha256=dwn3uiZsvI0zUqALQ0OmpIUFHe3YmOhRI2pEc9qQxmY,14486
51
- langchain_core/language_models/chat_models.py,sha256=FRYgoRdzZsh0V2iwhO-p4hhCaNlwLRBL-vk-rziymXs,77482
51
+ langchain_core/language_models/chat_models.py,sha256=na_GWv-tyy4EBhObVvvdA7Sg3BBe9AWNZ3WvZAg88RY,77547
52
52
  langchain_core/language_models/fake.py,sha256=h9LhVTkmYLXkJ1_VvsKhqYVpkQsM7eAr9geXF_IVkPs,3772
53
53
  langchain_core/language_models/fake_chat_models.py,sha256=rojzv3arvsT0x2RLVJfSpaZ_zQLo18EM5MUnmwktpN4,13690
54
54
  langchain_core/language_models/llms.py,sha256=jzhJ1v4tGuuuD9lFZEYfesoE3WhtRNIWI6XKgQjPooc,56803
@@ -58,7 +58,7 @@ langchain_core/load/load.py,sha256=8Jq62M9QYcW78Iv3J_EKQG6OIAsbthudMM60gqyUjFg,9
58
58
  langchain_core/load/mapping.py,sha256=nnFXiTdQkfdv41_wP38aWGtpp9svxW6fwVyC3LmRkok,29633
59
59
  langchain_core/load/serializable.py,sha256=JIM8GTYYLXBTrRn9zal1tMJOP4z5vs-Hi-aAov6JYtY,11684
60
60
  langchain_core/memory.py,sha256=V-ARgyy5_xgvoBfp4WArMrk6NdbBjbqXdGDIbSTI_tA,3631
61
- langchain_core/messages/__init__.py,sha256=8HJOtGK8pSpql4w-q1xA2a0Fc7KYTkMZMiARuojoVaE,6290
61
+ langchain_core/messages/__init__.py,sha256=wTOjbQMPUBTC0WX5tQpuW-pHIktPVojIlJY2AbNtgsA,6048
62
62
  langchain_core/messages/ai.py,sha256=2V3yqVK5dkeg_roprSWuK2ZWgzlnPVvRV0F98cpTP3Y,24579
63
63
  langchain_core/messages/base.py,sha256=TWMnefCv8loiv5LnIBDSw6k_YHfO2uVCI6UWFg8Hpas,14591
64
64
  langchain_core/messages/block_translators/__init__.py,sha256=Mo_pXM7a2DCKNvPgp1CStt4EOGHaiNMFNaKFIn01fcA,3102
@@ -68,17 +68,17 @@ langchain_core/messages/block_translators/bedrock_converse.py,sha256=k13H9VvFPNq
68
68
  langchain_core/messages/block_translators/google_genai.py,sha256=qsE1KH3byEBftE6BLcjZBta-WuSFVM8oFGC7k99A72Q,1537
69
69
  langchain_core/messages/block_translators/google_vertexai.py,sha256=FXiJT2yIB64WZbxkho9k-qLPbcsDK74vwdFZgKik6QM,1594
70
70
  langchain_core/messages/block_translators/groq.py,sha256=6r6j9Hy3HS2hOEAZ1V-_hYHGKNPk6KmRrCmbiTTDgrY,1465
71
- langchain_core/messages/block_translators/langchain_v0.py,sha256=RvUfo27YkkqEMdj8ueWpg2XEj2gWnsx1yOD6aKh5qPg,11236
71
+ langchain_core/messages/block_translators/langchain_v0.py,sha256=NQjsIeP5lrCqKMK-NcAsI66dvVwIvjUegbtkzv9cj3Y,6708
72
72
  langchain_core/messages/block_translators/ollama.py,sha256=BuPm47bluNhKseHozaiBNi9He8I4V7Fn038luhBttcg,1483
73
- langchain_core/messages/block_translators/openai.py,sha256=9Bvb_APKTPo9dfKd4DbT7ra9M5tqQIQYSE94zQOcM58,22431
73
+ langchain_core/messages/block_translators/openai.py,sha256=1s8T1WbPEkjVEBLU19Qw_oB5hEOh1hRwZpUZ9B2rRVQ,30505
74
74
  langchain_core/messages/chat.py,sha256=Vgk3y03F9NP-wKkXAjBDLOtrH43NpEMN2xaWRp6qhRA,2260
75
- langchain_core/messages/content.py,sha256=1PVxEsdfzpBxFdhz8p-E2zvOkcfBMUDrfrImq4SZmm0,47747
75
+ langchain_core/messages/content.py,sha256=Lfx1O9tdy2zNcFSJM4GVjZpJhpFeQbILKIfyHm70Frw,43022
76
76
  langchain_core/messages/function.py,sha256=QO2WgKmJ5nm7QL-xXG11Fmz3qFkHm1lL0k41WjDeEZE,2157
77
77
  langchain_core/messages/human.py,sha256=VB8sw1DaNmzrc77T9NYd1QWQYCman6GFOfrlmVBWZMU,2582
78
78
  langchain_core/messages/modifier.py,sha256=ch0RedUM_uA7wOEJHk8mkoJSNR0Rli_32BmOfdbS1dU,894
79
79
  langchain_core/messages/system.py,sha256=FE2XZ7oHWqqIxOjEOMGEkMO97PqLXwVLa-jL5mvriGE,2388
80
80
  langchain_core/messages/tool.py,sha256=5uZsdZg-FbYc5U3v71TaxtrDQf6sT895LHNXZh-CRJ8,12406
81
- langchain_core/messages/utils.py,sha256=ZLG9I0fJbIA3HjtGk05j-ahLJpBPF8Hqhev2pFpVAb8,67673
81
+ langchain_core/messages/utils.py,sha256=GWI-E9YHNMjLXFfNP_WKacQxU5MozmkCKgfUps2edP4,67738
82
82
  langchain_core/output_parsers/__init__.py,sha256=R8L0GwY-vD9qvqze3EVELXF6i45IYUJ_FbSfno_IREg,2873
83
83
  langchain_core/output_parsers/base.py,sha256=RD0BgBBeNKDUTrEGxnLmA1DuCJowcEAfTB70Y8yqVoc,11168
84
84
  langchain_core/output_parsers/format_instructions.py,sha256=8oUbeysnVGvXWyNd5gqXlEL850D31gMTy74GflsuvRU,553
@@ -180,5 +180,5 @@ langchain_core/vectorstores/__init__.py,sha256=5P0eoeoH5LHab64JjmEeWa6SxX4eMy-et
180
180
  langchain_core/vectorstores/base.py,sha256=4AR5L6RWuAPEo9DQj9pOIN7UR3Ln45s02pU_Oe8sYsI,42026
181
181
  langchain_core/vectorstores/in_memory.py,sha256=lxe2bR-wFtvNN2Ii7EGOh3ON3MwqNRP996eUEek55fA,18076
182
182
  langchain_core/vectorstores/utils.py,sha256=DZUUR1xDybHDhmZJsd1V2OEPsYiFVc2nhtD4w8hw9ns,4934
183
- langchain_core/version.py,sha256=HfC-RSEPgK8xUsSnhsgkQrAYtIg4me_JR67qwEwqo-0,77
184
- langchain_core-1.0.0a1.dist-info/RECORD,,
183
+ langchain_core/version.py,sha256=_B2u_U6ZgDX4uw2fUg69i5bBqHaW-FZKLszrXH4LLHQ,77
184
+ langchain_core-1.0.0a2.dist-info/RECORD,,