langchain-core 0.3.79__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.
- langchain_core/__init__.py +1 -1
- langchain_core/_api/__init__.py +3 -4
- langchain_core/_api/beta_decorator.py +23 -26
- langchain_core/_api/deprecation.py +52 -65
- langchain_core/_api/path.py +3 -6
- langchain_core/_import_utils.py +3 -4
- langchain_core/agents.py +19 -19
- langchain_core/caches.py +53 -63
- langchain_core/callbacks/__init__.py +1 -8
- langchain_core/callbacks/base.py +323 -334
- langchain_core/callbacks/file.py +44 -44
- langchain_core/callbacks/manager.py +441 -507
- langchain_core/callbacks/stdout.py +29 -30
- langchain_core/callbacks/streaming_stdout.py +32 -32
- langchain_core/callbacks/usage.py +60 -57
- langchain_core/chat_history.py +48 -63
- langchain_core/document_loaders/base.py +23 -23
- langchain_core/document_loaders/langsmith.py +37 -37
- langchain_core/documents/__init__.py +0 -1
- langchain_core/documents/base.py +62 -65
- langchain_core/documents/compressor.py +4 -4
- langchain_core/documents/transformers.py +28 -29
- langchain_core/embeddings/fake.py +50 -54
- langchain_core/example_selectors/length_based.py +1 -1
- langchain_core/example_selectors/semantic_similarity.py +21 -25
- langchain_core/exceptions.py +10 -11
- langchain_core/globals.py +3 -151
- langchain_core/indexing/api.py +61 -66
- langchain_core/indexing/base.py +58 -58
- langchain_core/indexing/in_memory.py +3 -3
- langchain_core/language_models/__init__.py +14 -27
- langchain_core/language_models/_utils.py +270 -84
- langchain_core/language_models/base.py +55 -162
- langchain_core/language_models/chat_models.py +442 -402
- langchain_core/language_models/fake.py +11 -11
- langchain_core/language_models/fake_chat_models.py +61 -39
- langchain_core/language_models/llms.py +123 -231
- langchain_core/load/dump.py +4 -5
- langchain_core/load/load.py +18 -28
- langchain_core/load/mapping.py +2 -4
- langchain_core/load/serializable.py +39 -40
- langchain_core/messages/__init__.py +61 -22
- langchain_core/messages/ai.py +368 -163
- langchain_core/messages/base.py +214 -43
- langchain_core/messages/block_translators/__init__.py +111 -0
- langchain_core/messages/block_translators/anthropic.py +470 -0
- langchain_core/messages/block_translators/bedrock.py +94 -0
- langchain_core/messages/block_translators/bedrock_converse.py +297 -0
- langchain_core/messages/block_translators/google_genai.py +530 -0
- langchain_core/messages/block_translators/google_vertexai.py +21 -0
- langchain_core/messages/block_translators/groq.py +143 -0
- langchain_core/messages/block_translators/langchain_v0.py +301 -0
- langchain_core/messages/block_translators/openai.py +1010 -0
- langchain_core/messages/chat.py +2 -6
- langchain_core/messages/content.py +1423 -0
- langchain_core/messages/function.py +6 -10
- langchain_core/messages/human.py +41 -38
- langchain_core/messages/modifier.py +2 -2
- langchain_core/messages/system.py +38 -28
- langchain_core/messages/tool.py +96 -103
- langchain_core/messages/utils.py +478 -504
- langchain_core/output_parsers/__init__.py +1 -14
- langchain_core/output_parsers/base.py +58 -61
- langchain_core/output_parsers/json.py +7 -8
- langchain_core/output_parsers/list.py +5 -7
- langchain_core/output_parsers/openai_functions.py +49 -47
- langchain_core/output_parsers/openai_tools.py +14 -19
- langchain_core/output_parsers/pydantic.py +12 -13
- langchain_core/output_parsers/string.py +2 -2
- langchain_core/output_parsers/transform.py +15 -17
- langchain_core/output_parsers/xml.py +8 -10
- langchain_core/outputs/__init__.py +1 -1
- langchain_core/outputs/chat_generation.py +18 -18
- langchain_core/outputs/chat_result.py +1 -3
- langchain_core/outputs/generation.py +8 -8
- langchain_core/outputs/llm_result.py +10 -10
- langchain_core/prompt_values.py +12 -12
- langchain_core/prompts/__init__.py +3 -27
- langchain_core/prompts/base.py +45 -55
- langchain_core/prompts/chat.py +254 -313
- langchain_core/prompts/dict.py +5 -5
- langchain_core/prompts/few_shot.py +81 -88
- langchain_core/prompts/few_shot_with_templates.py +11 -13
- langchain_core/prompts/image.py +12 -14
- langchain_core/prompts/loading.py +6 -8
- langchain_core/prompts/message.py +3 -3
- langchain_core/prompts/prompt.py +24 -39
- langchain_core/prompts/string.py +4 -4
- langchain_core/prompts/structured.py +42 -50
- langchain_core/rate_limiters.py +51 -60
- langchain_core/retrievers.py +49 -190
- langchain_core/runnables/base.py +1484 -1709
- langchain_core/runnables/branch.py +45 -61
- langchain_core/runnables/config.py +80 -88
- langchain_core/runnables/configurable.py +117 -134
- langchain_core/runnables/fallbacks.py +83 -79
- langchain_core/runnables/graph.py +85 -95
- langchain_core/runnables/graph_ascii.py +27 -28
- langchain_core/runnables/graph_mermaid.py +38 -50
- langchain_core/runnables/graph_png.py +15 -16
- langchain_core/runnables/history.py +135 -148
- langchain_core/runnables/passthrough.py +124 -150
- langchain_core/runnables/retry.py +46 -51
- langchain_core/runnables/router.py +25 -30
- langchain_core/runnables/schema.py +79 -74
- langchain_core/runnables/utils.py +62 -68
- langchain_core/stores.py +81 -115
- langchain_core/structured_query.py +8 -8
- langchain_core/sys_info.py +27 -29
- langchain_core/tools/__init__.py +1 -14
- langchain_core/tools/base.py +179 -187
- langchain_core/tools/convert.py +131 -139
- langchain_core/tools/render.py +10 -10
- langchain_core/tools/retriever.py +11 -11
- langchain_core/tools/simple.py +19 -24
- langchain_core/tools/structured.py +30 -39
- langchain_core/tracers/__init__.py +1 -9
- langchain_core/tracers/base.py +97 -99
- langchain_core/tracers/context.py +29 -52
- langchain_core/tracers/core.py +50 -60
- langchain_core/tracers/evaluation.py +11 -11
- langchain_core/tracers/event_stream.py +115 -70
- langchain_core/tracers/langchain.py +21 -21
- langchain_core/tracers/log_stream.py +43 -43
- langchain_core/tracers/memory_stream.py +3 -3
- langchain_core/tracers/root_listeners.py +16 -16
- langchain_core/tracers/run_collector.py +2 -4
- langchain_core/tracers/schemas.py +0 -129
- langchain_core/tracers/stdout.py +3 -3
- langchain_core/utils/__init__.py +1 -4
- langchain_core/utils/_merge.py +46 -8
- langchain_core/utils/aiter.py +57 -61
- langchain_core/utils/env.py +9 -9
- langchain_core/utils/function_calling.py +89 -191
- langchain_core/utils/html.py +7 -8
- langchain_core/utils/input.py +6 -6
- langchain_core/utils/interactive_env.py +1 -1
- langchain_core/utils/iter.py +37 -42
- langchain_core/utils/json.py +4 -3
- langchain_core/utils/json_schema.py +8 -8
- langchain_core/utils/mustache.py +9 -11
- langchain_core/utils/pydantic.py +33 -35
- langchain_core/utils/strings.py +5 -5
- langchain_core/utils/usage.py +1 -1
- langchain_core/utils/utils.py +80 -54
- langchain_core/vectorstores/base.py +129 -164
- langchain_core/vectorstores/in_memory.py +99 -174
- langchain_core/vectorstores/utils.py +5 -5
- langchain_core/version.py +1 -1
- {langchain_core-0.3.79.dist-info → langchain_core-1.0.0.dist-info}/METADATA +28 -27
- langchain_core-1.0.0.dist-info/RECORD +172 -0
- {langchain_core-0.3.79.dist-info → langchain_core-1.0.0.dist-info}/WHEEL +1 -1
- langchain_core/beta/__init__.py +0 -1
- langchain_core/beta/runnables/__init__.py +0 -1
- langchain_core/beta/runnables/context.py +0 -447
- langchain_core/memory.py +0 -120
- langchain_core/messages/content_blocks.py +0 -176
- langchain_core/prompts/pipeline.py +0 -138
- langchain_core/pydantic_v1/__init__.py +0 -30
- langchain_core/pydantic_v1/dataclasses.py +0 -23
- langchain_core/pydantic_v1/main.py +0 -23
- langchain_core/tracers/langchain_v1.py +0 -31
- langchain_core/utils/loading.py +0 -35
- langchain_core-0.3.79.dist-info/RECORD +0 -174
- langchain_core-0.3.79.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
|