coco-framework 0.1.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.
- coco/__init__.py +3 -0
- coco/agents/__init__.py +9 -0
- coco/agents/_subagent_transformer.py +307 -0
- coco/agents/factory.py +2164 -0
- coco/agents/middleware/__init__.py +100 -0
- coco/agents/middleware/_execution.py +385 -0
- coco/agents/middleware/_redaction.py +454 -0
- coco/agents/middleware/_retry.py +134 -0
- coco/agents/middleware/_trace_policy.py +61 -0
- coco/agents/middleware/context_editing.py +306 -0
- coco/agents/middleware/file_search.py +433 -0
- coco/agents/middleware/human_in_the_loop.py +680 -0
- coco/agents/middleware/internal_call_transformer.py +108 -0
- coco/agents/middleware/model_call_limit.py +267 -0
- coco/agents/middleware/model_fallback.py +417 -0
- coco/agents/middleware/model_retry.py +327 -0
- coco/agents/middleware/pii.py +878 -0
- coco/agents/middleware/provider_tool_search.py +307 -0
- coco/agents/middleware/shell_tool.py +949 -0
- coco/agents/middleware/summarization.py +910 -0
- coco/agents/middleware/todo.py +357 -0
- coco/agents/middleware/tool_call_limit.py +495 -0
- coco/agents/middleware/tool_emulator.py +251 -0
- coco/agents/middleware/tool_error.py +209 -0
- coco/agents/middleware/tool_retry.py +421 -0
- coco/agents/middleware/tool_selection.py +515 -0
- coco/agents/middleware/types.py +2201 -0
- coco/agents/structured_output.py +472 -0
- coco/chat_models/__init__.py +7 -0
- coco/chat_models/base.py +1082 -0
- coco/embeddings/__init__.py +18 -0
- coco/embeddings/base.py +279 -0
- coco/mcp/__init__.py +36 -0
- coco/mcp/adapter.py +245 -0
- coco/mcp/elicitation.py +394 -0
- coco/mcp/tools.py +307 -0
- coco/messages/__init__.py +73 -0
- coco/py.typed +0 -0
- coco/rate_limiters/__init__.py +13 -0
- coco/tools/__init__.py +22 -0
- coco/tools/tool_node.py +20 -0
- coco_anthropic/__init__.py +15 -0
- coco_anthropic/_client_utils.py +81 -0
- coco_anthropic/_compat.py +228 -0
- coco_anthropic/_sdk_compat.py +124 -0
- coco_anthropic/_version.py +3 -0
- coco_anthropic/chat_models.py +3349 -0
- coco_anthropic/data/__init__.py +1 -0
- coco_anthropic/data/_profiles.py +491 -0
- coco_anthropic/data/profile_augmentations.toml +69 -0
- coco_anthropic/experimental.py +140 -0
- coco_anthropic/llms.py +428 -0
- coco_anthropic/middleware/__init__.py +25 -0
- coco_anthropic/middleware/anthropic_tools.py +1197 -0
- coco_anthropic/middleware/bash.py +84 -0
- coco_anthropic/middleware/file_search.py +360 -0
- coco_anthropic/middleware/prompt_caching.py +276 -0
- coco_anthropic/output_parsers.py +93 -0
- coco_anthropic/py.typed +0 -0
- coco_core/__init__.py +20 -0
- coco_core/_api/__init__.py +87 -0
- coco_core/_api/beta_decorator.py +266 -0
- coco_core/_api/deprecation.py +637 -0
- coco_core/_api/internal.py +23 -0
- coco_core/_api/path.py +50 -0
- coco_core/_import_utils.py +41 -0
- coco_core/_protocol.py +625 -0
- coco_core/_security/__init__.py +36 -0
- coco_core/_security/_exceptions.py +9 -0
- coco_core/_security/_policy.py +313 -0
- coco_core/_security/_ssrf_protection.py +155 -0
- coco_core/_security/_transport.py +254 -0
- coco_core/agents.py +258 -0
- coco_core/caches.py +272 -0
- coco_core/callbacks/__init__.py +132 -0
- coco_core/callbacks/base.py +1229 -0
- coco_core/callbacks/file.py +266 -0
- coco_core/callbacks/manager.py +2826 -0
- coco_core/callbacks/stdout.py +123 -0
- coco_core/callbacks/streaming_stdout.py +154 -0
- coco_core/callbacks/usage.py +124 -0
- coco_core/chat_history.py +267 -0
- coco_core/chat_loaders.py +26 -0
- coco_core/chat_sessions.py +19 -0
- coco_core/cross_encoders.py +18 -0
- coco_core/document_loaders/__init__.py +36 -0
- coco_core/document_loaders/base.py +155 -0
- coco_core/document_loaders/blob_loaders.py +38 -0
- coco_core/documents/__init__.py +55 -0
- coco_core/documents/base.py +347 -0
- coco_core/documents/compressor.py +74 -0
- coco_core/documents/transformers.py +79 -0
- coco_core/embeddings/__init__.py +31 -0
- coco_core/embeddings/embeddings.py +78 -0
- coco_core/embeddings/fake.py +129 -0
- coco_core/env.py +22 -0
- coco_core/example_selectors/__init__.py +47 -0
- coco_core/example_selectors/base.py +60 -0
- coco_core/example_selectors/length_based.py +131 -0
- coco_core/example_selectors/semantic_similarity.py +366 -0
- coco_core/exceptions.py +166 -0
- coco_core/globals.py +72 -0
- coco_core/indexing/__init__.py +53 -0
- coco_core/indexing/api.py +954 -0
- coco_core/indexing/base.py +661 -0
- coco_core/indexing/in_memory.py +104 -0
- coco_core/language_models/__init__.py +116 -0
- coco_core/language_models/_compat_bridge.py +844 -0
- coco_core/language_models/_utils.py +343 -0
- coco_core/language_models/base.py +500 -0
- coco_core/language_models/chat_model_stream.py +1525 -0
- coco_core/language_models/chat_models.py +2748 -0
- coco_core/language_models/fake.py +137 -0
- coco_core/language_models/fake_chat_models.py +396 -0
- coco_core/language_models/llms.py +1569 -0
- coco_core/language_models/model_profile.py +183 -0
- coco_core/load/__init__.py +44 -0
- coco_core/load/_validation.py +191 -0
- coco_core/load/dump.py +120 -0
- coco_core/load/load.py +828 -0
- coco_core/load/mapping.py +1097 -0
- coco_core/load/serializable.py +406 -0
- coco_core/messages/__init__.py +198 -0
- coco_core/messages/ai.py +854 -0
- coco_core/messages/base.py +518 -0
- coco_core/messages/block_translators/__init__.py +112 -0
- coco_core/messages/block_translators/anthropic.py +534 -0
- coco_core/messages/block_translators/bedrock.py +108 -0
- coco_core/messages/block_translators/bedrock_converse.py +329 -0
- coco_core/messages/block_translators/coco_v0.py +303 -0
- coco_core/messages/block_translators/google_genai.py +610 -0
- coco_core/messages/block_translators/google_vertexai.py +21 -0
- coco_core/messages/block_translators/groq.py +157 -0
- coco_core/messages/block_translators/openai.py +1089 -0
- coco_core/messages/chat.py +64 -0
- coco_core/messages/content.py +1488 -0
- coco_core/messages/function.py +62 -0
- coco_core/messages/human.py +70 -0
- coco_core/messages/modifier.py +33 -0
- coco_core/messages/system.py +70 -0
- coco_core/messages/tool.py +418 -0
- coco_core/messages/utils.py +2442 -0
- coco_core/output_parsers/__init__.py +101 -0
- coco_core/output_parsers/base.py +361 -0
- coco_core/output_parsers/format_instructions.py +16 -0
- coco_core/output_parsers/json.py +139 -0
- coco_core/output_parsers/list.py +249 -0
- coco_core/output_parsers/openai_functions.py +324 -0
- coco_core/output_parsers/openai_tools.py +383 -0
- coco_core/output_parsers/pydantic.py +141 -0
- coco_core/output_parsers/string.py +63 -0
- coco_core/output_parsers/transform.py +175 -0
- coco_core/output_parsers/xml.py +301 -0
- coco_core/outputs/__init__.py +66 -0
- coco_core/outputs/chat_generation.py +147 -0
- coco_core/outputs/chat_result.py +39 -0
- coco_core/outputs/generation.py +80 -0
- coco_core/outputs/llm_result.py +112 -0
- coco_core/outputs/run_info.py +22 -0
- coco_core/prompt_values.py +161 -0
- coco_core/prompts/__init__.py +101 -0
- coco_core/prompts/base.py +508 -0
- coco_core/prompts/chat.py +1495 -0
- coco_core/prompts/dict.py +177 -0
- coco_core/prompts/few_shot.py +483 -0
- coco_core/prompts/few_shot_with_templates.py +239 -0
- coco_core/prompts/image.py +177 -0
- coco_core/prompts/loading.py +296 -0
- coco_core/prompts/message.py +97 -0
- coco_core/prompts/prompt.py +312 -0
- coco_core/prompts/string.py +399 -0
- coco_core/prompts/structured.py +214 -0
- coco_core/py.typed +0 -0
- coco_core/rate_limiters.py +254 -0
- coco_core/retrievers.py +328 -0
- coco_core/runnables/__init__.py +136 -0
- coco_core/runnables/base.py +6725 -0
- coco_core/runnables/branch.py +457 -0
- coco_core/runnables/config.py +712 -0
- coco_core/runnables/configurable.py +710 -0
- coco_core/runnables/fallbacks.py +663 -0
- coco_core/runnables/graph.py +741 -0
- coco_core/runnables/graph_ascii.py +366 -0
- coco_core/runnables/graph_mermaid.py +503 -0
- coco_core/runnables/graph_png.py +215 -0
- coco_core/runnables/history.py +631 -0
- coco_core/runnables/passthrough.py +849 -0
- coco_core/runnables/retry.py +379 -0
- coco_core/runnables/router.py +239 -0
- coco_core/runnables/schema.py +188 -0
- coco_core/runnables/utils.py +798 -0
- coco_core/stores.py +291 -0
- coco_core/structured_query.py +203 -0
- coco_core/sys_info.py +154 -0
- coco_core/tools/__init__.py +95 -0
- coco_core/tools/base.py +1948 -0
- coco_core/tools/convert.py +497 -0
- coco_core/tools/render.py +67 -0
- coco_core/tools/retriever.py +94 -0
- coco_core/tools/simple.py +208 -0
- coco_core/tools/structured.py +322 -0
- coco_core/tracers/__init__.py +47 -0
- coco_core/tracers/_compat.py +95 -0
- coco_core/tracers/_context.py +337 -0
- coco_core/tracers/_streaming.py +53 -0
- coco_core/tracers/base.py +955 -0
- coco_core/tracers/coco.py +279 -0
- coco_core/tracers/context.py +205 -0
- coco_core/tracers/core.py +766 -0
- coco_core/tracers/event_stream.py +1101 -0
- coco_core/tracers/log_stream.py +765 -0
- coco_core/tracers/memory_stream.py +200 -0
- coco_core/tracers/root_listeners.py +130 -0
- coco_core/tracers/run_collector.py +40 -0
- coco_core/tracers/schemas.py +14 -0
- coco_core/tracers/stdout.py +205 -0
- coco_core/utils/__init__.py +111 -0
- coco_core/utils/_gateway.py +274 -0
- coco_core/utils/_merge.py +218 -0
- coco_core/utils/aiter.py +357 -0
- coco_core/utils/env.py +86 -0
- coco_core/utils/formatting.py +80 -0
- coco_core/utils/function_calling.py +869 -0
- coco_core/utils/html.py +132 -0
- coco_core/utils/image.py +15 -0
- coco_core/utils/input.py +82 -0
- coco_core/utils/interactive_env.py +12 -0
- coco_core/utils/iter.py +229 -0
- coco_core/utils/json.py +230 -0
- coco_core/utils/json_schema.py +273 -0
- coco_core/utils/mustache.py +706 -0
- coco_core/utils/pydantic.py +631 -0
- coco_core/utils/strings.py +69 -0
- coco_core/utils/usage.py +61 -0
- coco_core/utils/utils.py +528 -0
- coco_core/utils/uuid.py +57 -0
- coco_core/vectorstores/__init__.py +53 -0
- coco_core/vectorstores/base.py +1111 -0
- coco_core/vectorstores/in_memory.py +546 -0
- coco_core/vectorstores/utils.py +163 -0
- coco_core/version.py +3 -0
- coco_framework-0.1.0.dist-info/METADATA +184 -0
- coco_framework-0.1.0.dist-info/RECORD +400 -0
- coco_framework-0.1.0.dist-info/WHEEL +4 -0
- coco_framework-0.1.0.dist-info/licenses/LICENSE +24 -0
- coco_framework-0.1.0.dist-info/licenses/NOTICE +15 -0
- coco_graph/_internal/__init__.py +4 -0
- coco_graph/_internal/_cache.py +31 -0
- coco_graph/_internal/_config.py +474 -0
- coco_graph/_internal/_constants.py +140 -0
- coco_graph/_internal/_fields.py +213 -0
- coco_graph/_internal/_future.py +220 -0
- coco_graph/_internal/_pydantic.py +275 -0
- coco_graph/_internal/_queue.py +124 -0
- coco_graph/_internal/_replay.py +90 -0
- coco_graph/_internal/_retry.py +29 -0
- coco_graph/_internal/_runnable.py +969 -0
- coco_graph/_internal/_scratchpad.py +19 -0
- coco_graph/_internal/_serde.py +253 -0
- coco_graph/_internal/_timeout.py +25 -0
- coco_graph/_internal/_typing.py +54 -0
- coco_graph/cache/base/__init__.py +48 -0
- coco_graph/cache/base/py.typed +0 -0
- coco_graph/cache/memory/__init__.py +73 -0
- coco_graph/cache/redis/__init__.py +144 -0
- coco_graph/cache/sqlite/__init__.py +120 -0
- coco_graph/callbacks.py +394 -0
- coco_graph/channels/__init__.py +29 -0
- coco_graph/channels/any_value.py +72 -0
- coco_graph/channels/base.py +121 -0
- coco_graph/channels/binop.py +155 -0
- coco_graph/channels/delta.py +202 -0
- coco_graph/channels/ephemeral_value.py +79 -0
- coco_graph/channels/last_value.py +151 -0
- coco_graph/channels/named_barrier_value.py +167 -0
- coco_graph/channels/topic.py +94 -0
- coco_graph/channels/untracked_value.py +73 -0
- coco_graph/checkpoint/base/__init__.py +861 -0
- coco_graph/checkpoint/base/id.py +109 -0
- coco_graph/checkpoint/base/py.typed +0 -0
- coco_graph/checkpoint/memory/__init__.py +698 -0
- coco_graph/checkpoint/memory/py.typed +0 -0
- coco_graph/checkpoint/postgres/__init__.py +602 -0
- coco_graph/checkpoint/postgres/_ainternal.py +23 -0
- coco_graph/checkpoint/postgres/_internal.py +21 -0
- coco_graph/checkpoint/postgres/aio.py +691 -0
- coco_graph/checkpoint/postgres/base.py +666 -0
- coco_graph/checkpoint/postgres/py.typed +0 -0
- coco_graph/checkpoint/postgres/shallow.py +967 -0
- coco_graph/checkpoint/serde/__init__.py +0 -0
- coco_graph/checkpoint/serde/_msgpack.py +97 -0
- coco_graph/checkpoint/serde/base.py +64 -0
- coco_graph/checkpoint/serde/encrypted.py +80 -0
- coco_graph/checkpoint/serde/event_hooks.py +52 -0
- coco_graph/checkpoint/serde/jsonplus.py +883 -0
- coco_graph/checkpoint/serde/py.typed +0 -0
- coco_graph/checkpoint/serde/types.py +68 -0
- coco_graph/checkpoint/sqlite/__init__.py +645 -0
- coco_graph/checkpoint/sqlite/_delta.py +172 -0
- coco_graph/checkpoint/sqlite/aio.py +738 -0
- coco_graph/checkpoint/sqlite/py.typed +0 -0
- coco_graph/checkpoint/sqlite/utils.py +116 -0
- coco_graph/config.py +196 -0
- coco_graph/constants.py +64 -0
- coco_graph/errors.py +241 -0
- coco_graph/func/__init__.py +620 -0
- coco_graph/graph/__init__.py +12 -0
- coco_graph/graph/_branch.py +225 -0
- coco_graph/graph/_node.py +103 -0
- coco_graph/graph/message.py +437 -0
- coco_graph/graph/state.py +1978 -0
- coco_graph/graph/ui.py +227 -0
- coco_graph/managed/__init__.py +3 -0
- coco_graph/managed/base.py +31 -0
- coco_graph/managed/is_last_step.py +24 -0
- coco_graph/prebuilt/__init__.py +23 -0
- coco_graph/prebuilt/_tool_call_stream.py +117 -0
- coco_graph/prebuilt/_tool_call_transformer.py +165 -0
- coco_graph/prebuilt/chat_agent_executor.py +1015 -0
- coco_graph/prebuilt/interrupt.py +105 -0
- coco_graph/prebuilt/py.typed +0 -0
- coco_graph/prebuilt/tool_node.py +2030 -0
- coco_graph/prebuilt/tool_validator.py +221 -0
- coco_graph/pregel/__init__.py +3 -0
- coco_graph/pregel/_algo.py +1460 -0
- coco_graph/pregel/_call.py +298 -0
- coco_graph/pregel/_checkpoint.py +331 -0
- coco_graph/pregel/_config.py +0 -0
- coco_graph/pregel/_draw.py +294 -0
- coco_graph/pregel/_executor.py +223 -0
- coco_graph/pregel/_io.py +174 -0
- coco_graph/pregel/_log.py +3 -0
- coco_graph/pregel/_loop.py +1988 -0
- coco_graph/pregel/_messages.py +461 -0
- coco_graph/pregel/_read.py +312 -0
- coco_graph/pregel/_retry.py +854 -0
- coco_graph/pregel/_runner.py +941 -0
- coco_graph/pregel/_tools.py +268 -0
- coco_graph/pregel/_utils.py +221 -0
- coco_graph/pregel/_validate.py +120 -0
- coco_graph/pregel/_write.py +192 -0
- coco_graph/pregel/debug.py +302 -0
- coco_graph/pregel/main.py +4364 -0
- coco_graph/pregel/protocol.py +288 -0
- coco_graph/pregel/types.py +38 -0
- coco_graph/py.typed +0 -0
- coco_graph/runtime.py +350 -0
- coco_graph/store/base/__init__.py +1322 -0
- coco_graph/store/base/batch.py +371 -0
- coco_graph/store/base/embed.py +436 -0
- coco_graph/store/base/py.typed +0 -0
- coco_graph/store/memory/__init__.py +592 -0
- coco_graph/store/memory/py.typed +0 -0
- coco_graph/store/postgres/__init__.py +4 -0
- coco_graph/store/postgres/aio.py +592 -0
- coco_graph/store/postgres/base.py +1480 -0
- coco_graph/store/postgres/py.typed +0 -0
- coco_graph/store/sqlite/__init__.py +4 -0
- coco_graph/store/sqlite/aio.py +632 -0
- coco_graph/store/sqlite/base.py +1514 -0
- coco_graph/stream/__init__.py +45 -0
- coco_graph/stream/_convert.py +32 -0
- coco_graph/stream/_mux.py +523 -0
- coco_graph/stream/_types.py +330 -0
- coco_graph/stream/run_stream.py +735 -0
- coco_graph/stream/stream_channel.py +341 -0
- coco_graph/stream/transformers.py +1039 -0
- coco_graph/types.py +1079 -0
- coco_graph/typing.py +48 -0
- coco_graph/utils/__init__.py +1 -0
- coco_graph/utils/config.py +4 -0
- coco_graph/utils/runnable.py +3 -0
- coco_graph/version.py +13 -0
- coco_graph/warnings.py +69 -0
- coco_openai/__init__.py +20 -0
- coco_openai/_compat.py +45 -0
- coco_openai/_version.py +3 -0
- coco_openai/chat_models/__init__.py +6 -0
- coco_openai/chat_models/_client_utils.py +684 -0
- coco_openai/chat_models/_compat.py +525 -0
- coco_openai/chat_models/azure.py +1227 -0
- coco_openai/chat_models/base.py +5654 -0
- coco_openai/chat_models/codex.py +557 -0
- coco_openai/chatgpt_oauth.py +1063 -0
- coco_openai/data/__init__.py +1 -0
- coco_openai/data/_profiles.py +1655 -0
- coco_openai/data/profile_augmentations.toml +127 -0
- coco_openai/embeddings/__init__.py +6 -0
- coco_openai/embeddings/azure.py +240 -0
- coco_openai/embeddings/base.py +820 -0
- coco_openai/llms/__init__.py +6 -0
- coco_openai/llms/azure.py +249 -0
- coco_openai/llms/base.py +919 -0
- coco_openai/middleware/__init__.py +11 -0
- coco_openai/middleware/openai_moderation.py +484 -0
- coco_openai/output_parsers/__init__.py +9 -0
- coco_openai/output_parsers/tools.py +9 -0
- coco_openai/py.typed +0 -0
- coco_openai/tools/__init__.py +5 -0
- coco_openai/tools/custom_tool.py +111 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Utilities for JSON Schema."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _retrieve_ref(path: str, schema: dict[str, Any]) -> list[Any] | dict[Any, Any]:
|
|
13
|
+
"""Retrieve a referenced object from a JSON schema using a path.
|
|
14
|
+
|
|
15
|
+
Resolves JSON schema references (e.g., `'#/definitions/MyType'`) by traversing the
|
|
16
|
+
schema structure.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
path: Reference path starting with `'#'` (e.g., `'#/definitions/MyType'`).
|
|
20
|
+
schema: The JSON schema dictionary to search in.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
A deep copy of the referenced object (dict or list).
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
ValueError: If the path does not start with `'#'`.
|
|
27
|
+
KeyError: If the reference path is not found in the schema.
|
|
28
|
+
"""
|
|
29
|
+
components = path.split("/")
|
|
30
|
+
if components[0] != "#":
|
|
31
|
+
msg = (
|
|
32
|
+
"ref paths are expected to be URI fragments, meaning they should start "
|
|
33
|
+
"with #."
|
|
34
|
+
)
|
|
35
|
+
raise ValueError(msg)
|
|
36
|
+
out: list[Any] | dict[Any, Any] = schema
|
|
37
|
+
for component in components[1:]:
|
|
38
|
+
if component in out:
|
|
39
|
+
if isinstance(out, list):
|
|
40
|
+
msg = f"Reference '{path}' not found."
|
|
41
|
+
raise KeyError(msg)
|
|
42
|
+
out = out[component]
|
|
43
|
+
elif component.isdigit():
|
|
44
|
+
index = int(component)
|
|
45
|
+
if (isinstance(out, list) and 0 <= index < len(out)) or (
|
|
46
|
+
isinstance(out, dict) and index in out
|
|
47
|
+
):
|
|
48
|
+
out = out[index]
|
|
49
|
+
else:
|
|
50
|
+
msg = f"Reference '{path}' not found."
|
|
51
|
+
raise KeyError(msg)
|
|
52
|
+
else:
|
|
53
|
+
msg = f"Reference '{path}' not found."
|
|
54
|
+
raise KeyError(msg)
|
|
55
|
+
return deepcopy(out)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _process_dict_properties(
|
|
59
|
+
properties: dict[str, Any],
|
|
60
|
+
full_schema: dict[str, Any],
|
|
61
|
+
processed_refs: set[str],
|
|
62
|
+
skip_keys: Sequence[str],
|
|
63
|
+
*,
|
|
64
|
+
shallow_refs: bool,
|
|
65
|
+
) -> dict[str, Any]:
|
|
66
|
+
"""Process dictionary properties, recursing into nested structures."""
|
|
67
|
+
result: dict[str, Any] = {}
|
|
68
|
+
for key, value in properties.items():
|
|
69
|
+
if key in skip_keys:
|
|
70
|
+
# Skip recursion for specified keys, just copy the value as-is
|
|
71
|
+
result[key] = deepcopy(value)
|
|
72
|
+
elif isinstance(value, (dict, list)):
|
|
73
|
+
# Recursively process nested objects and arrays
|
|
74
|
+
result[key] = _dereference_refs_helper(
|
|
75
|
+
value, full_schema, processed_refs, skip_keys, shallow_refs=shallow_refs
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
# Copy primitive values directly
|
|
79
|
+
result[key] = value
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _dereference_refs_helper(
|
|
84
|
+
obj: Any,
|
|
85
|
+
full_schema: dict[str, Any],
|
|
86
|
+
processed_refs: set[str] | None,
|
|
87
|
+
skip_keys: Sequence[str],
|
|
88
|
+
*,
|
|
89
|
+
shallow_refs: bool,
|
|
90
|
+
) -> Any:
|
|
91
|
+
"""Dereference JSON Schema $ref objects, handling both pure and mixed references.
|
|
92
|
+
|
|
93
|
+
This function processes JSON Schema objects containing $ref properties by resolving
|
|
94
|
+
the references and merging any additional properties. It handles:
|
|
95
|
+
|
|
96
|
+
- Pure `$ref` objects: `{"$ref": "#/path/to/definition"}`
|
|
97
|
+
- Mixed `$ref` objects: `{"$ref": "#/path", "title": "Custom Title", ...}`
|
|
98
|
+
- Circular references by breaking cycles and preserving non-ref properties
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
obj: The object to process (can be dict, list, or primitive)
|
|
102
|
+
full_schema: The complete schema containing all definitions
|
|
103
|
+
processed_refs: Set tracking currently processing refs (for cycle detection)
|
|
104
|
+
skip_keys: Keys under which to skip recursion
|
|
105
|
+
shallow_refs: If `True`, only break cycles; if `False`, deep-inline all refs
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The object with `$ref` properties resolved and merged with other properties.
|
|
109
|
+
"""
|
|
110
|
+
if processed_refs is None:
|
|
111
|
+
processed_refs = set()
|
|
112
|
+
|
|
113
|
+
# Case 1: Object contains a $ref property (pure or mixed with additional properties)
|
|
114
|
+
if isinstance(obj, dict) and "$ref" in obj:
|
|
115
|
+
ref_path = obj["$ref"]
|
|
116
|
+
additional_properties = {
|
|
117
|
+
key: value for key, value in obj.items() if key != "$ref"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Detect circular reference: if we're already processing this $ref,
|
|
121
|
+
# return only the additional properties to break the cycle
|
|
122
|
+
if ref_path in processed_refs:
|
|
123
|
+
return _process_dict_properties(
|
|
124
|
+
additional_properties,
|
|
125
|
+
full_schema,
|
|
126
|
+
processed_refs,
|
|
127
|
+
skip_keys,
|
|
128
|
+
shallow_refs=shallow_refs,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Mark this reference as being processed (for cycle detection)
|
|
132
|
+
processed_refs.add(ref_path)
|
|
133
|
+
|
|
134
|
+
# Fetch and recursively resolve the referenced object
|
|
135
|
+
referenced_object = deepcopy(_retrieve_ref(ref_path, full_schema))
|
|
136
|
+
resolved_reference = _dereference_refs_helper(
|
|
137
|
+
referenced_object,
|
|
138
|
+
full_schema,
|
|
139
|
+
processed_refs,
|
|
140
|
+
skip_keys,
|
|
141
|
+
shallow_refs=shallow_refs,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Clean up: remove from processing set before returning
|
|
145
|
+
processed_refs.remove(ref_path)
|
|
146
|
+
|
|
147
|
+
# Pure $ref case: no additional properties, return resolved reference directly
|
|
148
|
+
if not additional_properties:
|
|
149
|
+
return resolved_reference
|
|
150
|
+
|
|
151
|
+
# Mixed $ref case: merge resolved reference with additional properties
|
|
152
|
+
# Additional properties take precedence over resolved properties
|
|
153
|
+
merged_result = {}
|
|
154
|
+
if isinstance(resolved_reference, dict):
|
|
155
|
+
merged_result.update(resolved_reference)
|
|
156
|
+
|
|
157
|
+
# Process additional properties and merge them (they override resolved ones)
|
|
158
|
+
processed_additional = _process_dict_properties(
|
|
159
|
+
additional_properties,
|
|
160
|
+
full_schema,
|
|
161
|
+
processed_refs,
|
|
162
|
+
skip_keys,
|
|
163
|
+
shallow_refs=shallow_refs,
|
|
164
|
+
)
|
|
165
|
+
merged_result.update(processed_additional)
|
|
166
|
+
|
|
167
|
+
return merged_result
|
|
168
|
+
|
|
169
|
+
# Case 2: Regular dictionary without $ref - process all properties
|
|
170
|
+
if isinstance(obj, dict):
|
|
171
|
+
return _process_dict_properties(
|
|
172
|
+
obj, full_schema, processed_refs, skip_keys, shallow_refs=shallow_refs
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Case 3: List - recursively process each item
|
|
176
|
+
if isinstance(obj, list):
|
|
177
|
+
return [
|
|
178
|
+
_dereference_refs_helper(
|
|
179
|
+
item, full_schema, processed_refs, skip_keys, shallow_refs=shallow_refs
|
|
180
|
+
)
|
|
181
|
+
for item in obj
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
# Case 4: Primitive value (string, number, boolean, null) - return unchanged
|
|
185
|
+
return obj
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def dereference_refs(
|
|
189
|
+
schema_obj: dict[str, Any],
|
|
190
|
+
*,
|
|
191
|
+
full_schema: dict[str, Any] | None = None,
|
|
192
|
+
skip_keys: Sequence[str] | None = None,
|
|
193
|
+
) -> dict[str, Any]:
|
|
194
|
+
"""Resolve and inline JSON Schema `$ref` references in a schema object.
|
|
195
|
+
|
|
196
|
+
This function processes a JSON Schema and resolves all `$ref` references by
|
|
197
|
+
replacing them with the actual referenced content.
|
|
198
|
+
|
|
199
|
+
Handles both simple references and complex cases like circular references and mixed
|
|
200
|
+
`$ref` objects that contain additional properties alongside the `$ref`.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
schema_obj: The JSON Schema object or fragment to process.
|
|
204
|
+
|
|
205
|
+
This can be a complete schema or just a portion of one.
|
|
206
|
+
full_schema: The complete schema containing all definitions that `$refs` might
|
|
207
|
+
point to.
|
|
208
|
+
|
|
209
|
+
If not provided, defaults to `schema_obj` (useful when the schema is
|
|
210
|
+
self-contained).
|
|
211
|
+
skip_keys: Controls recursion behavior and reference resolution depth.
|
|
212
|
+
|
|
213
|
+
- If `None` (Default): Only recurse under `'$defs'` and use shallow
|
|
214
|
+
reference resolution (break cycles but don't deep-inline nested refs)
|
|
215
|
+
- If provided (even as `[]`): Recurse under all keys and use deep reference
|
|
216
|
+
resolution (fully inline all nested references)
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
A new dictionary with all $ref references resolved and inlined.
|
|
220
|
+
|
|
221
|
+
The original `schema_obj` is not modified.
|
|
222
|
+
|
|
223
|
+
Examples:
|
|
224
|
+
Basic reference resolution:
|
|
225
|
+
>>> schema = {
|
|
226
|
+
... "type": "object",
|
|
227
|
+
... "properties": {"name": {"$ref": "#/$defs/string_type"}},
|
|
228
|
+
... "$defs": {"string_type": {"type": "string"}},
|
|
229
|
+
... }
|
|
230
|
+
>>> result = dereference_refs(schema)
|
|
231
|
+
>>> result["properties"]["name"] # {"type": "string"}
|
|
232
|
+
|
|
233
|
+
Mixed `$ref` with additional properties:
|
|
234
|
+
|
|
235
|
+
>>> schema = {
|
|
236
|
+
... "properties": {
|
|
237
|
+
... "name": {"$ref": "#/$defs/base", "description": "User name"}
|
|
238
|
+
... },
|
|
239
|
+
... "$defs": {"base": {"type": "string", "minLength": 1}},
|
|
240
|
+
... }
|
|
241
|
+
>>> result = dereference_refs(schema)
|
|
242
|
+
>>> result["properties"]["name"]
|
|
243
|
+
# {"type": "string", "minLength": 1, "description": "User name"}
|
|
244
|
+
|
|
245
|
+
Handling circular references:
|
|
246
|
+
|
|
247
|
+
>>> schema = {
|
|
248
|
+
... "properties": {"user": {"$ref": "#/$defs/User"}},
|
|
249
|
+
... "$defs": {
|
|
250
|
+
... "User": {
|
|
251
|
+
... "type": "object",
|
|
252
|
+
... "properties": {"friend": {"$ref": "#/$defs/User"}},
|
|
253
|
+
... }
|
|
254
|
+
... },
|
|
255
|
+
... }
|
|
256
|
+
>>> result = dereference_refs(schema) # Won't cause infinite recursion
|
|
257
|
+
|
|
258
|
+
!!! note
|
|
259
|
+
|
|
260
|
+
- Circular references are handled gracefully by breaking cycles
|
|
261
|
+
- Mixed `$ref` objects (with both `$ref` and other properties) are supported
|
|
262
|
+
- Additional properties in mixed `$refs` override resolved properties
|
|
263
|
+
- The `$defs` section is preserved in the output by default
|
|
264
|
+
"""
|
|
265
|
+
full = full_schema or schema_obj
|
|
266
|
+
keys_to_skip = list(skip_keys) if skip_keys is not None else ["$defs"]
|
|
267
|
+
shallow = skip_keys is None
|
|
268
|
+
return cast(
|
|
269
|
+
"dict[str, Any]",
|
|
270
|
+
_dereference_refs_helper(
|
|
271
|
+
schema_obj, full, None, keys_to_skip, shallow_refs=shallow
|
|
272
|
+
),
|
|
273
|
+
)
|