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

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

Potentially problematic release.


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

Files changed (172) hide show
  1. langchain_core/__init__.py +1 -1
  2. langchain_core/_api/__init__.py +3 -4
  3. langchain_core/_api/beta_decorator.py +45 -70
  4. langchain_core/_api/deprecation.py +80 -80
  5. langchain_core/_api/path.py +22 -8
  6. langchain_core/_import_utils.py +10 -4
  7. langchain_core/agents.py +25 -21
  8. langchain_core/caches.py +53 -63
  9. langchain_core/callbacks/__init__.py +1 -8
  10. langchain_core/callbacks/base.py +341 -348
  11. langchain_core/callbacks/file.py +55 -44
  12. langchain_core/callbacks/manager.py +546 -683
  13. langchain_core/callbacks/stdout.py +29 -30
  14. langchain_core/callbacks/streaming_stdout.py +35 -36
  15. langchain_core/callbacks/usage.py +65 -70
  16. langchain_core/chat_history.py +48 -55
  17. langchain_core/document_loaders/base.py +46 -21
  18. langchain_core/document_loaders/langsmith.py +39 -36
  19. langchain_core/documents/__init__.py +0 -1
  20. langchain_core/documents/base.py +96 -74
  21. langchain_core/documents/compressor.py +12 -9
  22. langchain_core/documents/transformers.py +29 -28
  23. langchain_core/embeddings/fake.py +56 -57
  24. langchain_core/env.py +2 -3
  25. langchain_core/example_selectors/base.py +12 -0
  26. langchain_core/example_selectors/length_based.py +1 -1
  27. langchain_core/example_selectors/semantic_similarity.py +21 -25
  28. langchain_core/exceptions.py +15 -9
  29. langchain_core/globals.py +4 -163
  30. langchain_core/indexing/api.py +132 -125
  31. langchain_core/indexing/base.py +64 -67
  32. langchain_core/indexing/in_memory.py +26 -6
  33. langchain_core/language_models/__init__.py +15 -27
  34. langchain_core/language_models/_utils.py +267 -117
  35. langchain_core/language_models/base.py +92 -177
  36. langchain_core/language_models/chat_models.py +547 -407
  37. langchain_core/language_models/fake.py +11 -11
  38. langchain_core/language_models/fake_chat_models.py +72 -118
  39. langchain_core/language_models/llms.py +168 -242
  40. langchain_core/load/dump.py +8 -11
  41. langchain_core/load/load.py +32 -28
  42. langchain_core/load/mapping.py +2 -4
  43. langchain_core/load/serializable.py +50 -56
  44. langchain_core/messages/__init__.py +36 -51
  45. langchain_core/messages/ai.py +377 -150
  46. langchain_core/messages/base.py +239 -47
  47. langchain_core/messages/block_translators/__init__.py +111 -0
  48. langchain_core/messages/block_translators/anthropic.py +470 -0
  49. langchain_core/messages/block_translators/bedrock.py +94 -0
  50. langchain_core/messages/block_translators/bedrock_converse.py +297 -0
  51. langchain_core/messages/block_translators/google_genai.py +530 -0
  52. langchain_core/messages/block_translators/google_vertexai.py +21 -0
  53. langchain_core/messages/block_translators/groq.py +143 -0
  54. langchain_core/messages/block_translators/langchain_v0.py +301 -0
  55. langchain_core/messages/block_translators/openai.py +1010 -0
  56. langchain_core/messages/chat.py +2 -3
  57. langchain_core/messages/content.py +1423 -0
  58. langchain_core/messages/function.py +7 -7
  59. langchain_core/messages/human.py +44 -38
  60. langchain_core/messages/modifier.py +3 -2
  61. langchain_core/messages/system.py +40 -27
  62. langchain_core/messages/tool.py +160 -58
  63. langchain_core/messages/utils.py +527 -638
  64. langchain_core/output_parsers/__init__.py +1 -14
  65. langchain_core/output_parsers/base.py +68 -104
  66. langchain_core/output_parsers/json.py +13 -17
  67. langchain_core/output_parsers/list.py +11 -33
  68. langchain_core/output_parsers/openai_functions.py +56 -74
  69. langchain_core/output_parsers/openai_tools.py +68 -109
  70. langchain_core/output_parsers/pydantic.py +15 -13
  71. langchain_core/output_parsers/string.py +6 -2
  72. langchain_core/output_parsers/transform.py +17 -60
  73. langchain_core/output_parsers/xml.py +34 -44
  74. langchain_core/outputs/__init__.py +1 -1
  75. langchain_core/outputs/chat_generation.py +26 -11
  76. langchain_core/outputs/chat_result.py +1 -3
  77. langchain_core/outputs/generation.py +17 -6
  78. langchain_core/outputs/llm_result.py +15 -8
  79. langchain_core/prompt_values.py +29 -123
  80. langchain_core/prompts/__init__.py +3 -27
  81. langchain_core/prompts/base.py +48 -63
  82. langchain_core/prompts/chat.py +259 -288
  83. langchain_core/prompts/dict.py +19 -11
  84. langchain_core/prompts/few_shot.py +84 -90
  85. langchain_core/prompts/few_shot_with_templates.py +14 -12
  86. langchain_core/prompts/image.py +19 -14
  87. langchain_core/prompts/loading.py +6 -8
  88. langchain_core/prompts/message.py +7 -8
  89. langchain_core/prompts/prompt.py +42 -43
  90. langchain_core/prompts/string.py +37 -16
  91. langchain_core/prompts/structured.py +43 -46
  92. langchain_core/rate_limiters.py +51 -60
  93. langchain_core/retrievers.py +52 -192
  94. langchain_core/runnables/base.py +1727 -1683
  95. langchain_core/runnables/branch.py +52 -73
  96. langchain_core/runnables/config.py +89 -103
  97. langchain_core/runnables/configurable.py +128 -130
  98. langchain_core/runnables/fallbacks.py +93 -82
  99. langchain_core/runnables/graph.py +127 -127
  100. langchain_core/runnables/graph_ascii.py +63 -41
  101. langchain_core/runnables/graph_mermaid.py +87 -70
  102. langchain_core/runnables/graph_png.py +31 -36
  103. langchain_core/runnables/history.py +145 -161
  104. langchain_core/runnables/passthrough.py +141 -144
  105. langchain_core/runnables/retry.py +84 -68
  106. langchain_core/runnables/router.py +33 -37
  107. langchain_core/runnables/schema.py +79 -72
  108. langchain_core/runnables/utils.py +95 -139
  109. langchain_core/stores.py +85 -131
  110. langchain_core/structured_query.py +11 -15
  111. langchain_core/sys_info.py +31 -32
  112. langchain_core/tools/__init__.py +1 -14
  113. langchain_core/tools/base.py +221 -247
  114. langchain_core/tools/convert.py +144 -161
  115. langchain_core/tools/render.py +10 -10
  116. langchain_core/tools/retriever.py +12 -19
  117. langchain_core/tools/simple.py +52 -29
  118. langchain_core/tools/structured.py +56 -60
  119. langchain_core/tracers/__init__.py +1 -9
  120. langchain_core/tracers/_streaming.py +6 -7
  121. langchain_core/tracers/base.py +103 -112
  122. langchain_core/tracers/context.py +29 -48
  123. langchain_core/tracers/core.py +142 -105
  124. langchain_core/tracers/evaluation.py +30 -34
  125. langchain_core/tracers/event_stream.py +162 -117
  126. langchain_core/tracers/langchain.py +34 -36
  127. langchain_core/tracers/log_stream.py +87 -49
  128. langchain_core/tracers/memory_stream.py +3 -3
  129. langchain_core/tracers/root_listeners.py +18 -34
  130. langchain_core/tracers/run_collector.py +8 -20
  131. langchain_core/tracers/schemas.py +0 -125
  132. langchain_core/tracers/stdout.py +3 -3
  133. langchain_core/utils/__init__.py +1 -4
  134. langchain_core/utils/_merge.py +47 -9
  135. langchain_core/utils/aiter.py +70 -66
  136. langchain_core/utils/env.py +12 -9
  137. langchain_core/utils/function_calling.py +139 -206
  138. langchain_core/utils/html.py +7 -8
  139. langchain_core/utils/input.py +6 -6
  140. langchain_core/utils/interactive_env.py +6 -2
  141. langchain_core/utils/iter.py +48 -45
  142. langchain_core/utils/json.py +14 -4
  143. langchain_core/utils/json_schema.py +159 -43
  144. langchain_core/utils/mustache.py +32 -25
  145. langchain_core/utils/pydantic.py +67 -40
  146. langchain_core/utils/strings.py +5 -5
  147. langchain_core/utils/usage.py +1 -1
  148. langchain_core/utils/utils.py +104 -62
  149. langchain_core/vectorstores/base.py +131 -179
  150. langchain_core/vectorstores/in_memory.py +113 -182
  151. langchain_core/vectorstores/utils.py +23 -17
  152. langchain_core/version.py +1 -1
  153. langchain_core-1.0.0.dist-info/METADATA +68 -0
  154. langchain_core-1.0.0.dist-info/RECORD +172 -0
  155. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0.dist-info}/WHEEL +1 -1
  156. langchain_core/beta/__init__.py +0 -1
  157. langchain_core/beta/runnables/__init__.py +0 -1
  158. langchain_core/beta/runnables/context.py +0 -448
  159. langchain_core/memory.py +0 -116
  160. langchain_core/messages/content_blocks.py +0 -1435
  161. langchain_core/prompts/pipeline.py +0 -133
  162. langchain_core/pydantic_v1/__init__.py +0 -30
  163. langchain_core/pydantic_v1/dataclasses.py +0 -23
  164. langchain_core/pydantic_v1/main.py +0 -23
  165. langchain_core/tracers/langchain_v1.py +0 -23
  166. langchain_core/utils/loading.py +0 -31
  167. langchain_core/v1/__init__.py +0 -1
  168. langchain_core/v1/chat_models.py +0 -1047
  169. langchain_core/v1/messages.py +0 -755
  170. langchain_core-0.4.0.dev0.dist-info/METADATA +0 -108
  171. langchain_core-0.4.0.dev0.dist-info/RECORD +0 -177
  172. langchain_core-0.4.0.dev0.dist-info/entry_points.txt +0 -4
@@ -0,0 +1,143 @@
1
+ """Derivations of standard content blocks from Groq content."""
2
+
3
+ import json
4
+ import re
5
+ from typing import Any
6
+
7
+ from langchain_core.messages import AIMessage, AIMessageChunk
8
+ from langchain_core.messages import content as types
9
+ from langchain_core.messages.base import _extract_reasoning_from_additional_kwargs
10
+
11
+
12
+ def _populate_extras(
13
+ standard_block: types.ContentBlock, block: dict[str, Any], known_fields: set[str]
14
+ ) -> types.ContentBlock:
15
+ """Mutate a block, populating extras."""
16
+ if standard_block.get("type") == "non_standard":
17
+ return standard_block
18
+
19
+ for key, value in block.items():
20
+ if key not in known_fields:
21
+ if "extras" not in standard_block:
22
+ # Below type-ignores are because mypy thinks a non-standard block can
23
+ # get here, although we exclude them above.
24
+ standard_block["extras"] = {} # type: ignore[typeddict-unknown-key]
25
+ standard_block["extras"][key] = value # type: ignore[typeddict-item]
26
+
27
+ return standard_block
28
+
29
+
30
+ def _parse_code_json(s: str) -> dict:
31
+ """Extract Python code from Groq built-in tool content.
32
+
33
+ Extracts the value of the 'code' field from a string of the form:
34
+ {"code": some_arbitrary_text_with_unescaped_quotes}
35
+
36
+ As Groq may not escape quotes in the executed tools, e.g.:
37
+ ```
38
+ '{"code": "import math; print("The square root of 101 is: "); print(math.sqrt(101))"}'
39
+ ```
40
+ """ # noqa: E501
41
+ m = re.fullmatch(r'\s*\{\s*"code"\s*:\s*"(.*)"\s*\}\s*', s, flags=re.DOTALL)
42
+ if not m:
43
+ msg = (
44
+ "Could not extract Python code from Groq tool arguments. "
45
+ "Expected a JSON object with a 'code' field."
46
+ )
47
+ raise ValueError(msg)
48
+ return {"code": m.group(1)}
49
+
50
+
51
+ def _convert_to_v1_from_groq(message: AIMessage) -> list[types.ContentBlock]:
52
+ """Convert groq message content to v1 format."""
53
+ content_blocks: list[types.ContentBlock] = []
54
+
55
+ if reasoning_block := _extract_reasoning_from_additional_kwargs(message):
56
+ content_blocks.append(reasoning_block)
57
+
58
+ if executed_tools := message.additional_kwargs.get("executed_tools"):
59
+ for idx, executed_tool in enumerate(executed_tools):
60
+ args: dict[str, Any] | None = None
61
+ if arguments := executed_tool.get("arguments"):
62
+ try:
63
+ args = json.loads(arguments)
64
+ except json.JSONDecodeError:
65
+ if executed_tool.get("type") == "python":
66
+ try:
67
+ args = _parse_code_json(arguments)
68
+ except ValueError:
69
+ continue
70
+ elif (
71
+ executed_tool.get("type") == "function"
72
+ and executed_tool.get("name") == "python"
73
+ ):
74
+ # GPT-OSS
75
+ args = {"code": arguments}
76
+ else:
77
+ continue
78
+ if isinstance(args, dict):
79
+ name = ""
80
+ if executed_tool.get("type") == "search":
81
+ name = "web_search"
82
+ elif executed_tool.get("type") == "python" or (
83
+ executed_tool.get("type") == "function"
84
+ and executed_tool.get("name") == "python"
85
+ ):
86
+ name = "code_interpreter"
87
+ server_tool_call: types.ServerToolCall = {
88
+ "type": "server_tool_call",
89
+ "name": name,
90
+ "id": str(idx),
91
+ "args": args,
92
+ }
93
+ content_blocks.append(server_tool_call)
94
+ if tool_output := executed_tool.get("output"):
95
+ tool_result: types.ServerToolResult = {
96
+ "type": "server_tool_result",
97
+ "tool_call_id": str(idx),
98
+ "output": tool_output,
99
+ "status": "success",
100
+ }
101
+ known_fields = {"type", "arguments", "index", "output"}
102
+ _populate_extras(tool_result, executed_tool, known_fields)
103
+ content_blocks.append(tool_result)
104
+
105
+ if isinstance(message.content, str) and message.content:
106
+ content_blocks.append({"type": "text", "text": message.content})
107
+
108
+ for tool_call in message.tool_calls:
109
+ content_blocks.append( # noqa: PERF401
110
+ {
111
+ "type": "tool_call",
112
+ "name": tool_call["name"],
113
+ "args": tool_call["args"],
114
+ "id": tool_call.get("id"),
115
+ }
116
+ )
117
+
118
+ return content_blocks
119
+
120
+
121
+ def translate_content(message: AIMessage) -> list[types.ContentBlock]:
122
+ """Derive standard content blocks from a message with groq content."""
123
+ return _convert_to_v1_from_groq(message)
124
+
125
+
126
+ def translate_content_chunk(message: AIMessageChunk) -> list[types.ContentBlock]:
127
+ """Derive standard content blocks from a message chunk with groq content."""
128
+ return _convert_to_v1_from_groq(message)
129
+
130
+
131
+ def _register_groq_translator() -> None:
132
+ """Register the groq translator with the central registry.
133
+
134
+ Run automatically when the module is imported.
135
+ """
136
+ from langchain_core.messages.block_translators import ( # noqa: PLC0415
137
+ register_translator,
138
+ )
139
+
140
+ register_translator("groq", translate_content, translate_content_chunk)
141
+
142
+
143
+ _register_groq_translator()
@@ -0,0 +1,301 @@
1
+ """Derivations of standard content blocks from LangChain v0 multimodal content."""
2
+
3
+ from typing import Any, cast
4
+
5
+ from langchain_core.messages import content as types
6
+
7
+
8
+ def _convert_v0_multimodal_input_to_v1(
9
+ content: list[types.ContentBlock],
10
+ ) -> list[types.ContentBlock]:
11
+ """Convert v0 multimodal blocks to v1 format.
12
+
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.
19
+
20
+ Args:
21
+ content: List of content blocks to process.
22
+
23
+ Returns:
24
+ v1 content blocks.
25
+ """
26
+ converted_blocks = []
27
+ unpacked_blocks: list[dict[str, Any]] = [
28
+ cast("dict[str, Any]", block)
29
+ if block.get("type") != "non_standard"
30
+ else block["value"] # type: ignore[typeddict-item] # this is only non-standard blocks
31
+ for block in content
32
+ ]
33
+ for block in unpacked_blocks:
34
+ if block.get("type") in {"image", "audio", "file"} and "source_type" in block:
35
+ converted_block = _convert_legacy_v0_content_block_to_v1(block)
36
+ converted_blocks.append(cast("types.ContentBlock", converted_block))
37
+ elif block.get("type") in types.KNOWN_BLOCK_TYPES:
38
+ # Guard in case this function is used outside of the .content_blocks flow
39
+ converted_blocks.append(cast("types.ContentBlock", block))
40
+ else:
41
+ converted_blocks.append({"type": "non_standard", "value": block})
42
+
43
+ return converted_blocks
44
+
45
+
46
+ def _convert_legacy_v0_content_block_to_v1(
47
+ block: dict,
48
+ ) -> types.ContentBlock | dict:
49
+ """Convert a LangChain v0 content block to v1 format.
50
+
51
+ Preserves unknown keys as extras to avoid data loss.
52
+
53
+ Returns the original block unchanged if it's not in v0 format.
54
+ """
55
+
56
+ def _extract_v0_extras(block_dict: dict, known_keys: set[str]) -> dict[str, Any]:
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
+ """
66
+ return {k: v for k, v in block_dict.items() if k not in known_keys}
67
+
68
+ # Check if this is actually a v0 format block
69
+ block_type = block.get("type")
70
+ if block_type not in {"image", "audio", "file"} or "source_type" not in block:
71
+ # Not a v0 format block, return unchanged
72
+ return block
73
+
74
+ if block.get("type") == "image":
75
+ source_type = block.get("source_type")
76
+ if source_type == "url":
77
+ # image-url
78
+ known_keys = {"mime_type", "type", "source_type", "url"}
79
+ extras = _extract_v0_extras(block, known_keys)
80
+ if "id" in block:
81
+ return types.create_image_block(
82
+ url=block["url"],
83
+ mime_type=block.get("mime_type"),
84
+ id=block["id"],
85
+ **extras,
86
+ )
87
+
88
+ # Don't construct with an ID if not present in original block
89
+ v1_image_url = types.ImageContentBlock(type="image", url=block["url"])
90
+ if block.get("mime_type"):
91
+ v1_image_url["mime_type"] = block["mime_type"]
92
+
93
+ v1_image_url["extras"] = {}
94
+ for key, value in extras.items():
95
+ if value is not None:
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
101
+ if source_type == "base64":
102
+ # image-base64
103
+ known_keys = {"mime_type", "type", "source_type", "data"}
104
+ extras = _extract_v0_extras(block, known_keys)
105
+ if "id" in block:
106
+ return types.create_image_block(
107
+ base64=block["data"],
108
+ mime_type=block.get("mime_type"),
109
+ id=block["id"],
110
+ **extras,
111
+ )
112
+
113
+ v1_image_base64 = types.ImageContentBlock(
114
+ type="image", base64=block["data"]
115
+ )
116
+ if block.get("mime_type"):
117
+ v1_image_base64["mime_type"] = block["mime_type"]
118
+
119
+ v1_image_base64["extras"] = {}
120
+ for key, value in extras.items():
121
+ if value is not None:
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
127
+ if source_type == "id":
128
+ # image-id
129
+ known_keys = {"type", "source_type", "id"}
130
+ extras = _extract_v0_extras(block, known_keys)
131
+ # For id `source_type`, `id` is the file reference, not block ID
132
+ v1_image_id = types.ImageContentBlock(type="image", file_id=block["id"])
133
+
134
+ v1_image_id["extras"] = {}
135
+ for key, value in extras.items():
136
+ if value is not None:
137
+ v1_image_id["extras"][key] = value
138
+ if v1_image_id["extras"] == {}:
139
+ del v1_image_id["extras"]
140
+
141
+ return v1_image_id
142
+ elif block.get("type") == "audio":
143
+ source_type = block.get("source_type")
144
+ if source_type == "url":
145
+ # audio-url
146
+ known_keys = {"mime_type", "type", "source_type", "url"}
147
+ extras = _extract_v0_extras(block, known_keys)
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"]
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
171
+ if source_type == "base64":
172
+ # audio-base64
173
+ known_keys = {"mime_type", "type", "source_type", "data"}
174
+ extras = _extract_v0_extras(block, known_keys)
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"]
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
197
+ if source_type == "id":
198
+ # audio-id
199
+ known_keys = {"type", "source_type", "id"}
200
+ extras = _extract_v0_extras(block, known_keys)
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
213
+ elif block.get("type") == "file":
214
+ source_type = block.get("source_type")
215
+ if source_type == "url":
216
+ # file-url
217
+ known_keys = {"mime_type", "type", "source_type", "url"}
218
+ extras = _extract_v0_extras(block, known_keys)
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"]
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
241
+ if source_type == "base64":
242
+ # file-base64
243
+ known_keys = {"mime_type", "type", "source_type", "data"}
244
+ extras = _extract_v0_extras(block, known_keys)
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"]
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
267
+ if source_type == "id":
268
+ # file-id
269
+ known_keys = {"type", "source_type", "id"}
270
+ extras = _extract_v0_extras(block, known_keys)
271
+ return types.create_file_block(file_id=block["id"], **extras)
272
+ if source_type == "text":
273
+ # file-text
274
+ known_keys = {"mime_type", "type", "source_type", "url"}
275
+ extras = _extract_v0_extras(block, known_keys)
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"
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
299
+
300
+ # If we can't convert, return the block unchanged
301
+ return block