langchain-core 0.4.0.dev0__py3-none-any.whl → 1.0.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of langchain-core might be problematic. Click here for more details.

Files changed (74) hide show
  1. langchain_core/_api/beta_decorator.py +2 -2
  2. langchain_core/_api/deprecation.py +1 -1
  3. langchain_core/beta/runnables/context.py +1 -1
  4. langchain_core/callbacks/base.py +14 -23
  5. langchain_core/callbacks/file.py +13 -2
  6. langchain_core/callbacks/manager.py +74 -157
  7. langchain_core/callbacks/streaming_stdout.py +3 -4
  8. langchain_core/callbacks/usage.py +2 -12
  9. langchain_core/chat_history.py +6 -6
  10. langchain_core/documents/base.py +1 -1
  11. langchain_core/documents/compressor.py +9 -6
  12. langchain_core/indexing/base.py +2 -2
  13. langchain_core/language_models/_utils.py +230 -101
  14. langchain_core/language_models/base.py +35 -23
  15. langchain_core/language_models/chat_models.py +245 -53
  16. langchain_core/language_models/fake_chat_models.py +28 -81
  17. langchain_core/load/dump.py +3 -4
  18. langchain_core/messages/__init__.py +38 -22
  19. langchain_core/messages/ai.py +188 -30
  20. langchain_core/messages/base.py +164 -25
  21. langchain_core/messages/block_translators/__init__.py +89 -0
  22. langchain_core/messages/block_translators/anthropic.py +451 -0
  23. langchain_core/messages/block_translators/bedrock.py +45 -0
  24. langchain_core/messages/block_translators/bedrock_converse.py +47 -0
  25. langchain_core/messages/block_translators/google_genai.py +45 -0
  26. langchain_core/messages/block_translators/google_vertexai.py +47 -0
  27. langchain_core/messages/block_translators/groq.py +45 -0
  28. langchain_core/messages/block_translators/langchain_v0.py +297 -0
  29. langchain_core/messages/block_translators/ollama.py +45 -0
  30. langchain_core/messages/block_translators/openai.py +586 -0
  31. langchain_core/messages/{content_blocks.py → content.py} +346 -213
  32. langchain_core/messages/human.py +29 -9
  33. langchain_core/messages/system.py +29 -9
  34. langchain_core/messages/tool.py +94 -13
  35. langchain_core/messages/utils.py +32 -234
  36. langchain_core/output_parsers/base.py +14 -50
  37. langchain_core/output_parsers/json.py +2 -5
  38. langchain_core/output_parsers/list.py +2 -7
  39. langchain_core/output_parsers/openai_functions.py +5 -28
  40. langchain_core/output_parsers/openai_tools.py +49 -90
  41. langchain_core/output_parsers/pydantic.py +2 -3
  42. langchain_core/output_parsers/transform.py +12 -53
  43. langchain_core/output_parsers/xml.py +9 -17
  44. langchain_core/prompt_values.py +8 -112
  45. langchain_core/prompts/chat.py +1 -3
  46. langchain_core/runnables/base.py +500 -451
  47. langchain_core/runnables/branch.py +1 -1
  48. langchain_core/runnables/fallbacks.py +4 -4
  49. langchain_core/runnables/history.py +1 -1
  50. langchain_core/runnables/passthrough.py +3 -3
  51. langchain_core/runnables/retry.py +1 -1
  52. langchain_core/runnables/router.py +1 -1
  53. langchain_core/structured_query.py +3 -7
  54. langchain_core/tools/base.py +14 -41
  55. langchain_core/tools/convert.py +2 -22
  56. langchain_core/tools/retriever.py +1 -8
  57. langchain_core/tools/structured.py +2 -10
  58. langchain_core/tracers/_streaming.py +6 -7
  59. langchain_core/tracers/base.py +7 -14
  60. langchain_core/tracers/core.py +4 -27
  61. langchain_core/tracers/event_stream.py +4 -15
  62. langchain_core/tracers/langchain.py +3 -14
  63. langchain_core/tracers/log_stream.py +2 -3
  64. langchain_core/utils/_merge.py +45 -7
  65. langchain_core/utils/function_calling.py +22 -9
  66. langchain_core/utils/utils.py +29 -0
  67. langchain_core/version.py +1 -1
  68. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/METADATA +7 -9
  69. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/RECORD +71 -64
  70. langchain_core/v1/__init__.py +0 -1
  71. langchain_core/v1/chat_models.py +0 -1047
  72. langchain_core/v1/messages.py +0 -755
  73. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/WHEEL +0 -0
  74. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,297 @@
1
+ """Derivations of standard content blocks from LangChain v0 multimodal content."""
2
+
3
+ from typing import Any, Union, cast
4
+
5
+ from langchain_core.language_models._utils import _parse_data_uri
6
+ from langchain_core.messages import content as types
7
+
8
+
9
+ def _convert_v0_multimodal_input_to_v1(
10
+ blocks: list[types.ContentBlock],
11
+ ) -> list[types.ContentBlock]:
12
+ """Convert v0 multimodal blocks to v1 format.
13
+
14
+ Processes non_standard blocks that might be v0 format and converts them
15
+ to proper v1 ContentBlocks.
16
+
17
+ Args:
18
+ blocks: List of content blocks to process.
19
+
20
+ Returns:
21
+ Updated list with v0 blocks converted to v1 format.
22
+ """
23
+ converted_blocks = []
24
+ unpacked_blocks: list[dict[str, Any]] = [
25
+ cast("dict[str, Any]", block)
26
+ if block.get("type") != "non_standard"
27
+ else block["value"] # type: ignore[typeddict-item] # this is only non-standard blocks
28
+ for block in blocks
29
+ ]
30
+ for block in unpacked_blocks:
31
+ if block.get("type") in {"image", "audio", "file"} and "source_type" in block:
32
+ converted_block = _convert_legacy_v0_content_block_to_v1(block)
33
+ converted_blocks.append(cast("types.ContentBlock", converted_block))
34
+ elif block.get("type") in types.KNOWN_BLOCK_TYPES:
35
+ converted_blocks.append(cast("types.ContentBlock", block))
36
+ else:
37
+ converted_blocks.append({"type": "non_standard", "value": block})
38
+
39
+ return converted_blocks
40
+
41
+
42
+ def _convert_legacy_v0_content_block_to_v1(
43
+ block: dict,
44
+ ) -> Union[types.ContentBlock, dict]:
45
+ """Convert a LangChain v0 content block to v1 format.
46
+
47
+ Preserves unknown keys as extras to avoid data loss.
48
+
49
+ Returns the original block unchanged if it's not in v0 format.
50
+
51
+ """
52
+
53
+ 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."""
55
+ return {k: v for k, v in block_dict.items() if k not in known_keys}
56
+
57
+ # Check if this is actually a v0 format block
58
+ block_type = block.get("type")
59
+ if block_type not in {"image", "audio", "file"} or "source_type" not in block:
60
+ # Not a v0 format block, return unchanged
61
+ return block
62
+
63
+ if block.get("type") == "image":
64
+ source_type = block.get("source_type")
65
+ if source_type == "url":
66
+ known_keys = {"type", "source_type", "url", "mime_type"}
67
+ extras = _extract_v0_extras(block, known_keys)
68
+ if "id" in block:
69
+ return types.create_image_block(
70
+ url=block["url"],
71
+ mime_type=block.get("mime_type"),
72
+ id=block["id"],
73
+ **extras,
74
+ )
75
+
76
+ # Don't construct with an ID if not present in original block
77
+ v1_block = types.ImageContentBlock(type="image", url=block["url"])
78
+ if block.get("mime_type"):
79
+ v1_block["mime_type"] = block["mime_type"]
80
+
81
+ for key, value in extras.items():
82
+ if value is not None:
83
+ v1_block["extras"] = {}
84
+ v1_block["extras"][key] = value
85
+ return v1_block
86
+ if source_type == "base64":
87
+ known_keys = {"type", "source_type", "data", "mime_type"}
88
+ extras = _extract_v0_extras(block, known_keys)
89
+ if "id" in block:
90
+ return types.create_image_block(
91
+ base64=block["data"],
92
+ mime_type=block.get("mime_type"),
93
+ id=block["id"],
94
+ **extras,
95
+ )
96
+
97
+ v1_block = types.ImageContentBlock(type="image", base64=block["data"])
98
+ if block.get("mime_type"):
99
+ v1_block["mime_type"] = block["mime_type"]
100
+
101
+ for key, value in extras.items():
102
+ if value is not None:
103
+ v1_block["extras"] = {}
104
+ v1_block["extras"][key] = value
105
+ return v1_block
106
+ if source_type == "id":
107
+ known_keys = {"type", "source_type", "id"}
108
+ extras = _extract_v0_extras(block, known_keys)
109
+ # For id `source_type`, `id` is the file reference, not block ID
110
+ v1_block = types.ImageContentBlock(type="image", file_id=block["id"])
111
+
112
+ for key, value in extras.items():
113
+ if value is not None:
114
+ v1_block["extras"] = {}
115
+ v1_block["extras"][key] = value
116
+
117
+ return v1_block
118
+ elif block.get("type") == "audio":
119
+ source_type = block.get("source_type")
120
+ if source_type == "url":
121
+ known_keys = {"type", "source_type", "url", "mime_type"}
122
+ extras = _extract_v0_extras(block, known_keys)
123
+ return types.create_audio_block(
124
+ url=block["url"], mime_type=block.get("mime_type"), **extras
125
+ )
126
+ if source_type == "base64":
127
+ known_keys = {"type", "source_type", "data", "mime_type"}
128
+ extras = _extract_v0_extras(block, known_keys)
129
+ return types.create_audio_block(
130
+ base64=block["data"], mime_type=block.get("mime_type"), **extras
131
+ )
132
+ if source_type == "id":
133
+ known_keys = {"type", "source_type", "id"}
134
+ extras = _extract_v0_extras(block, known_keys)
135
+ return types.create_audio_block(file_id=block["id"], **extras)
136
+ elif block.get("type") == "file":
137
+ source_type = block.get("source_type")
138
+ if source_type == "url":
139
+ known_keys = {"type", "source_type", "url", "mime_type"}
140
+ extras = _extract_v0_extras(block, known_keys)
141
+ return types.create_file_block(
142
+ url=block["url"], mime_type=block.get("mime_type"), **extras
143
+ )
144
+ if source_type == "base64":
145
+ known_keys = {"type", "source_type", "data", "mime_type"}
146
+ extras = _extract_v0_extras(block, known_keys)
147
+ return types.create_file_block(
148
+ base64=block["data"], mime_type=block.get("mime_type"), **extras
149
+ )
150
+ if source_type == "id":
151
+ known_keys = {"type", "source_type", "id"}
152
+ extras = _extract_v0_extras(block, known_keys)
153
+ return types.create_file_block(file_id=block["id"], **extras)
154
+ if source_type == "text":
155
+ known_keys = {"type", "source_type", "url", "mime_type"}
156
+ extras = _extract_v0_extras(block, known_keys)
157
+ return types.create_plaintext_block(
158
+ # In v0, URL points to the text file content
159
+ text=block["url"],
160
+ **extras,
161
+ )
162
+
163
+ # If we can't convert, return the block unchanged
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
@@ -0,0 +1,45 @@
1
+ """Derivations of standard content blocks from Ollama content."""
2
+
3
+ import warnings
4
+
5
+ from langchain_core.messages import AIMessage, AIMessageChunk
6
+ from langchain_core.messages import content as types
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 Ollama content."""
13
+ global WARNED # noqa: PLW0603
14
+ if not WARNED:
15
+ warning_message = (
16
+ "Content block standardization is not yet fully supported for Ollama."
17
+ )
18
+ warnings.warn(warning_message, stacklevel=2)
19
+ WARNED = True
20
+ raise NotImplementedError
21
+
22
+
23
+ def translate_content_chunk(message: AIMessageChunk) -> list[types.ContentBlock]: # noqa: ARG001
24
+ """Derive standard content blocks from a message chunk with Ollama content."""
25
+ global WARNED # noqa: PLW0603
26
+ if not WARNED:
27
+ warning_message = (
28
+ "Content block standardization is not yet fully supported for Ollama."
29
+ )
30
+ warnings.warn(warning_message, stacklevel=2)
31
+ WARNED = True
32
+ raise NotImplementedError
33
+
34
+
35
+ def _register_ollama_translator() -> None:
36
+ """Register the Ollama translator with the central registry.
37
+
38
+ Run automatically when the module is imported.
39
+ """
40
+ from langchain_core.messages.block_translators import register_translator
41
+
42
+ register_translator("ollama", translate_content, translate_content_chunk)
43
+
44
+
45
+ _register_ollama_translator()