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,183 @@
|
|
|
1
|
+
"""Model profile types and utilities."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import get_type_hints
|
|
6
|
+
|
|
7
|
+
from pydantic import ConfigDict
|
|
8
|
+
from typing_extensions import TypedDict
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ModelProfile(TypedDict, total=False):
|
|
14
|
+
"""Description of a chat model's capabilities, exposed via `model.profile`.
|
|
15
|
+
|
|
16
|
+
See the
|
|
17
|
+
[model profiles guide](https://docs.langchain.com/oss/python/langchain/models#model-profiles)
|
|
18
|
+
for concepts and usage. Data is sourced from
|
|
19
|
+
[models.dev](https://github.com/sst/models.dev), augmented with additional
|
|
20
|
+
fields, and generated by the
|
|
21
|
+
[`trustcoco-model-profiles`](https://github.com/langchain-ai/langchain/tree/master/libs/model-profiles)
|
|
22
|
+
package (via its `trustcoco-profiles` CLI).
|
|
23
|
+
|
|
24
|
+
!!! warning "Beta feature"
|
|
25
|
+
|
|
26
|
+
Fields and format are subject to change. This is a `total=False`
|
|
27
|
+
`TypedDict`, so any field may be absent — guard accesses with `.get()`.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
__pydantic_config__ = ConfigDict(extra="allow") # type: ignore[misc]
|
|
31
|
+
|
|
32
|
+
# --- Model metadata ---
|
|
33
|
+
|
|
34
|
+
name: str
|
|
35
|
+
"""Human-readable model name (e.g., `'GPT-5'`)."""
|
|
36
|
+
|
|
37
|
+
status: str
|
|
38
|
+
"""Model lifecycle status (e.g., `'active'`, `'deprecated'`)."""
|
|
39
|
+
|
|
40
|
+
release_date: str
|
|
41
|
+
"""Model release date (ISO 8601 format, e.g., `'2025-06-01'`)."""
|
|
42
|
+
|
|
43
|
+
last_updated: str
|
|
44
|
+
"""Date the model was last updated (ISO 8601 format)."""
|
|
45
|
+
|
|
46
|
+
open_weights: bool
|
|
47
|
+
"""Whether the model weights are openly available."""
|
|
48
|
+
|
|
49
|
+
# --- Input constraints ---
|
|
50
|
+
|
|
51
|
+
max_input_tokens: int
|
|
52
|
+
"""Maximum context window (tokens)."""
|
|
53
|
+
|
|
54
|
+
text_inputs: bool
|
|
55
|
+
"""Whether text inputs are supported."""
|
|
56
|
+
|
|
57
|
+
image_inputs: bool
|
|
58
|
+
"""Whether image inputs are supported."""
|
|
59
|
+
# TODO: add more detail about formats?
|
|
60
|
+
|
|
61
|
+
image_url_inputs: bool
|
|
62
|
+
"""Whether [image URL inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
|
|
63
|
+
are supported."""
|
|
64
|
+
|
|
65
|
+
pdf_inputs: bool
|
|
66
|
+
"""Whether [PDF inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
|
|
67
|
+
are supported."""
|
|
68
|
+
# TODO: add more detail about formats? e.g. bytes or base64
|
|
69
|
+
|
|
70
|
+
audio_inputs: bool
|
|
71
|
+
"""Whether [audio inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
|
|
72
|
+
are supported."""
|
|
73
|
+
# TODO: add more detail about formats? e.g. bytes or base64
|
|
74
|
+
|
|
75
|
+
video_inputs: bool
|
|
76
|
+
"""Whether [video inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
|
|
77
|
+
are supported."""
|
|
78
|
+
# TODO: add more detail about formats? e.g. bytes or base64
|
|
79
|
+
|
|
80
|
+
image_tool_message: bool
|
|
81
|
+
"""Whether images can be included in `ToolMessage` content."""
|
|
82
|
+
|
|
83
|
+
pdf_tool_message: bool
|
|
84
|
+
"""Whether PDFs can be included in `ToolMessage` content."""
|
|
85
|
+
|
|
86
|
+
# --- Output constraints ---
|
|
87
|
+
|
|
88
|
+
max_output_tokens: int
|
|
89
|
+
"""Maximum output tokens."""
|
|
90
|
+
|
|
91
|
+
reasoning_output: bool
|
|
92
|
+
"""Whether the model supports [reasoning / chain-of-thought](https://docs.langchain.com/oss/python/langchain/models#reasoning)."""
|
|
93
|
+
|
|
94
|
+
reasoning_effort_levels: list[str]
|
|
95
|
+
"""Supported reasoning-effort levels (e.g. `['low', 'medium', 'high']`).
|
|
96
|
+
|
|
97
|
+
Absent or empty if the model does not support a configurable reasoning
|
|
98
|
+
effort. Only meaningful when `reasoning_output` is `True`.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
reasoning_effort_default: str
|
|
102
|
+
"""The provider's documented default reasoning-effort level, if known.
|
|
103
|
+
|
|
104
|
+
Absent when no default is documented. Only meaningful when
|
|
105
|
+
`reasoning_effort_levels` is non-empty; not necessarily a member of
|
|
106
|
+
`reasoning_effort_levels` itself (a model may default to unconfigurable
|
|
107
|
+
behavior distinct from any explicit level).
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
text_outputs: bool
|
|
111
|
+
"""Whether text outputs are supported."""
|
|
112
|
+
|
|
113
|
+
image_outputs: bool
|
|
114
|
+
"""Whether [image outputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
|
|
115
|
+
are supported."""
|
|
116
|
+
|
|
117
|
+
audio_outputs: bool
|
|
118
|
+
"""Whether [audio outputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
|
|
119
|
+
are supported."""
|
|
120
|
+
|
|
121
|
+
video_outputs: bool
|
|
122
|
+
"""Whether [video outputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
|
|
123
|
+
are supported."""
|
|
124
|
+
|
|
125
|
+
# --- Tool calling ---
|
|
126
|
+
tool_calling: bool
|
|
127
|
+
"""Whether the model supports [tool calling](https://docs.langchain.com/oss/python/langchain/models#tool-calling)."""
|
|
128
|
+
|
|
129
|
+
tool_choice: bool
|
|
130
|
+
"""Whether the model supports [tool choice](https://docs.langchain.com/oss/python/langchain/models#forcing-tool-calls)."""
|
|
131
|
+
|
|
132
|
+
tool_call_streaming: bool
|
|
133
|
+
"""Whether the model returns properly structured `tool_call_chunks` when streaming.
|
|
134
|
+
|
|
135
|
+
Only meaningful when `tool_calling` is `True`.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
# --- Structured output ---
|
|
139
|
+
structured_output: bool
|
|
140
|
+
"""Whether the model supports native [structured output](https://docs.langchain.com/oss/python/langchain/models#structured-outputs)."""
|
|
141
|
+
|
|
142
|
+
# --- Other capabilities ---
|
|
143
|
+
|
|
144
|
+
attachment: bool
|
|
145
|
+
"""Whether the model supports file attachments."""
|
|
146
|
+
|
|
147
|
+
temperature: bool
|
|
148
|
+
"""Whether the model supports a temperature parameter."""
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
ModelProfileRegistry = dict[str, ModelProfile]
|
|
152
|
+
"""Registry mapping model identifiers or names to their ModelProfile."""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _warn_unknown_profile_keys(profile: ModelProfile) -> None:
|
|
156
|
+
"""Warn if `profile` contains keys not declared on `ModelProfile`.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
profile: The model profile dict to check for undeclared keys.
|
|
160
|
+
"""
|
|
161
|
+
if not isinstance(profile, dict):
|
|
162
|
+
return # type: ignore[unreachable]
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
declared = frozenset(get_type_hints(ModelProfile).keys())
|
|
166
|
+
except (TypeError, NameError):
|
|
167
|
+
# get_type_hints raises NameError on unresolvable forward refs and
|
|
168
|
+
# TypeError when annotations evaluate to non-type objects.
|
|
169
|
+
logger.debug(
|
|
170
|
+
"Could not resolve type hints for ModelProfile; "
|
|
171
|
+
"skipping unknown-key check.",
|
|
172
|
+
exc_info=True,
|
|
173
|
+
)
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
extra = sorted(set(profile) - declared)
|
|
177
|
+
if extra:
|
|
178
|
+
warnings.warn(
|
|
179
|
+
f"Unrecognized keys in model profile: {extra}. "
|
|
180
|
+
f"This may indicate a version mismatch between trustcoco-core "
|
|
181
|
+
f"and your provider package. Consider upgrading trustcoco-core.",
|
|
182
|
+
stacklevel=2,
|
|
183
|
+
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""**Load** module helps with serialization and deserialization."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from coco_core._import_utils import import_attr
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from coco_core.load.dump import dumpd, dumps
|
|
9
|
+
from coco_core.load.load import InitValidator, loads
|
|
10
|
+
from coco_core.load.serializable import Serializable
|
|
11
|
+
|
|
12
|
+
# Unfortunately, we have to eagerly import load from coco_core/load/load.py
|
|
13
|
+
# eagerly to avoid a namespace conflict. We want users to still be able to use
|
|
14
|
+
# `from coco_core.load import load` to get the load function, but
|
|
15
|
+
# the `from coco_core.load.load import load` absolute import should also work.
|
|
16
|
+
from coco_core.load.load import load
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
|
+
"InitValidator",
|
|
20
|
+
"Serializable",
|
|
21
|
+
"dumpd",
|
|
22
|
+
"dumps",
|
|
23
|
+
"load",
|
|
24
|
+
"loads",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
_dynamic_imports = {
|
|
28
|
+
"dumpd": "dump",
|
|
29
|
+
"dumps": "dump",
|
|
30
|
+
"InitValidator": "load",
|
|
31
|
+
"loads": "load",
|
|
32
|
+
"Serializable": "serializable",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def __getattr__(attr_name: str) -> object:
|
|
37
|
+
module_name = _dynamic_imports.get(attr_name)
|
|
38
|
+
result = import_attr(attr_name, module_name, __spec__.parent)
|
|
39
|
+
globals()[attr_name] = result
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def __dir__() -> list[str]:
|
|
44
|
+
return list(__all__)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Validation utilities for Coco serialization.
|
|
2
|
+
|
|
3
|
+
Provides escape-based protection against injection attacks in serialized objects. The
|
|
4
|
+
approach uses an allowlist design: only dicts explicitly produced by
|
|
5
|
+
`Serializable.to_json()` are treated as LC objects during deserialization.
|
|
6
|
+
|
|
7
|
+
## How escaping works
|
|
8
|
+
|
|
9
|
+
During serialization, plain dicts (user data) that contain an `'lc'` key are wrapped:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
{"lc": 1, ...} # user data that looks like LC object
|
|
13
|
+
# becomes:
|
|
14
|
+
{"__lc_escaped__": {"lc": 1, ...}}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
During deserialization, escaped dicts are unwrapped and returned as plain dicts,
|
|
18
|
+
NOT instantiated as LC objects.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Any, cast
|
|
22
|
+
|
|
23
|
+
from coco_core.load.serializable import (
|
|
24
|
+
Serializable,
|
|
25
|
+
to_json_not_implemented,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
_LC_ESCAPED_KEY = "__lc_escaped__"
|
|
29
|
+
"""Sentinel key used to mark escaped user dicts during serialization.
|
|
30
|
+
|
|
31
|
+
When a plain dict contains 'lc' key (which could be confused with LC objects),
|
|
32
|
+
we wrap it as {"__lc_escaped__": {...original...}}.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _needs_escaping(obj: dict[str, Any]) -> bool:
|
|
37
|
+
"""Check if a dict needs escaping to prevent confusion with LC objects.
|
|
38
|
+
|
|
39
|
+
A dict needs escaping if:
|
|
40
|
+
|
|
41
|
+
1. It has an `'lc'` key (could be confused with LC serialization format)
|
|
42
|
+
2. It has only the escape key (would be mistaken for an escaped dict)
|
|
43
|
+
"""
|
|
44
|
+
return "lc" in obj or (len(obj) == 1 and _LC_ESCAPED_KEY in obj)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _escape_dict(obj: dict[str, Any]) -> dict[str, Any]:
|
|
48
|
+
"""Wrap a dict in the escape marker.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
```python
|
|
52
|
+
{"key": "value"} # becomes {"__lc_escaped__": {"key": "value"}}
|
|
53
|
+
```
|
|
54
|
+
"""
|
|
55
|
+
return {_LC_ESCAPED_KEY: obj}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _is_escaped_dict(obj: dict[str, Any]) -> bool:
|
|
59
|
+
"""Check if a dict is an escaped user dict.
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
```python
|
|
63
|
+
{"__lc_escaped__": {...}} # is an escaped dict
|
|
64
|
+
```
|
|
65
|
+
"""
|
|
66
|
+
return len(obj) == 1 and _LC_ESCAPED_KEY in obj
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _serialize_value(obj: Any) -> Any:
|
|
70
|
+
"""Serialize a value with escaping of user dicts.
|
|
71
|
+
|
|
72
|
+
Called recursively on kwarg values to escape any plain dicts that could be confused
|
|
73
|
+
with LC objects.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
obj: The value to serialize.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
The serialized value with user dicts escaped as needed.
|
|
80
|
+
"""
|
|
81
|
+
if isinstance(obj, Serializable):
|
|
82
|
+
# This is an LC object - serialize it properly (not escaped)
|
|
83
|
+
return _serialize_lc_object(obj)
|
|
84
|
+
if isinstance(obj, dict):
|
|
85
|
+
if not all(isinstance(k, (str, int, float, bool, type(None))) for k in obj):
|
|
86
|
+
# if keys are not json serializable
|
|
87
|
+
return to_json_not_implemented(obj)
|
|
88
|
+
# Check if dict needs escaping BEFORE recursing into values.
|
|
89
|
+
# If it needs escaping, wrap it as-is - the contents are user data that
|
|
90
|
+
# will be returned as-is during deserialization (no instantiation).
|
|
91
|
+
# This prevents re-escaping of already-escaped nested content.
|
|
92
|
+
if _needs_escaping(obj):
|
|
93
|
+
return _escape_dict(obj)
|
|
94
|
+
# Safe dict (no 'lc' key) - recurse into values
|
|
95
|
+
return {k: _serialize_value(v) for k, v in obj.items()}
|
|
96
|
+
if isinstance(obj, (list, tuple)):
|
|
97
|
+
return [_serialize_value(item) for item in obj]
|
|
98
|
+
if isinstance(obj, (str, int, float, bool, type(None))):
|
|
99
|
+
return obj
|
|
100
|
+
|
|
101
|
+
# Non-JSON-serializable object (datetime, custom objects, etc.)
|
|
102
|
+
return to_json_not_implemented(obj)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _get_secret_keys(obj: Serializable) -> set[str]:
|
|
106
|
+
"""Return the merged set of constructor kwarg names declared as secrets.
|
|
107
|
+
|
|
108
|
+
Mirrors the MRO walk in `Serializable.to_json` so the keys returned here
|
|
109
|
+
match the keys whose values `_replace_secrets` rewrites into secret
|
|
110
|
+
markers. Used by `_serialize_lc_object` to decide which kwargs to skip
|
|
111
|
+
when escaping user data.
|
|
112
|
+
"""
|
|
113
|
+
secrets: dict[str, str] = {}
|
|
114
|
+
model_fields = type(obj).model_fields
|
|
115
|
+
for cls in [None, *obj.__class__.mro()]:
|
|
116
|
+
if cls is Serializable:
|
|
117
|
+
break
|
|
118
|
+
this = cast("Serializable", obj if cls is None else super(cls, obj))
|
|
119
|
+
secrets.update(this.lc_secrets)
|
|
120
|
+
for key in list(secrets):
|
|
121
|
+
if (key in model_fields) and (alias := model_fields[key].alias) is not None:
|
|
122
|
+
secrets[alias] = secrets[key]
|
|
123
|
+
return set(secrets)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _serialize_lc_object(obj: Any) -> dict[str, Any]:
|
|
127
|
+
"""Serialize a `Serializable` object with escaping of user data in kwargs.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
obj: The `Serializable` object to serialize.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
The serialized dict with user data in kwargs escaped as needed.
|
|
134
|
+
|
|
135
|
+
Note:
|
|
136
|
+
Kwargs values are processed with `_serialize_value` to escape user data
|
|
137
|
+
(like metadata) that contains `'lc'` keys. Secret fields are identified
|
|
138
|
+
by the class's declared `lc_secrets` and skipped because `to_json()`
|
|
139
|
+
already converted their values to secret markers.
|
|
140
|
+
|
|
141
|
+
The check is key-based rather than shape-based. A shape-based check
|
|
142
|
+
("this dict looks like a secret marker") can be forged by user data,
|
|
143
|
+
letting attacker-controlled free-form dicts bypass escaping and reach
|
|
144
|
+
the Reviver.
|
|
145
|
+
"""
|
|
146
|
+
if not isinstance(obj, Serializable):
|
|
147
|
+
msg = f"Expected Serializable, got {type(obj)}"
|
|
148
|
+
raise TypeError(msg)
|
|
149
|
+
|
|
150
|
+
serialized: dict[str, Any] = dict(obj.to_json())
|
|
151
|
+
|
|
152
|
+
# Process kwargs to escape user data that could be confused with LC objects.
|
|
153
|
+
# Skip kwargs declared as secrets - `to_json()` already replaced their
|
|
154
|
+
# values with secret markers via `_replace_secrets`.
|
|
155
|
+
if serialized.get("type") == "constructor" and "kwargs" in serialized:
|
|
156
|
+
secret_keys = _get_secret_keys(obj)
|
|
157
|
+
serialized["kwargs"] = {
|
|
158
|
+
k: v if k in secret_keys else _serialize_value(v)
|
|
159
|
+
for k, v in serialized["kwargs"].items()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return serialized
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _unescape_value(obj: Any) -> Any:
|
|
166
|
+
"""Unescape a value, processing escape markers in dict values and lists.
|
|
167
|
+
|
|
168
|
+
When an escaped dict is encountered (`{"__lc_escaped__": ...}`), it's
|
|
169
|
+
unwrapped and the contents are returned AS-IS (no further processing).
|
|
170
|
+
The contents represent user data that should not be modified.
|
|
171
|
+
|
|
172
|
+
For regular dicts and lists, we recurse to find any nested escape markers.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
obj: The value to unescape.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
The unescaped value.
|
|
179
|
+
"""
|
|
180
|
+
if isinstance(obj, dict):
|
|
181
|
+
if _is_escaped_dict(obj):
|
|
182
|
+
# Unwrap and return the user data as-is (no further unescaping).
|
|
183
|
+
# The contents are user data that may contain more escape keys,
|
|
184
|
+
# but those are part of the user's actual data.
|
|
185
|
+
return obj[_LC_ESCAPED_KEY]
|
|
186
|
+
|
|
187
|
+
# Regular dict - recurse into values to find nested escape markers
|
|
188
|
+
return {k: _unescape_value(v) for k, v in obj.items()}
|
|
189
|
+
if isinstance(obj, list):
|
|
190
|
+
return [_unescape_value(item) for item in obj]
|
|
191
|
+
return obj
|
coco_core/load/dump.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Serialize Coco objects to JSON.
|
|
2
|
+
|
|
3
|
+
Provides `dumps` (to JSON string) and `dumpd` (to dict) for serializing
|
|
4
|
+
`Serializable` objects.
|
|
5
|
+
|
|
6
|
+
## Escaping
|
|
7
|
+
|
|
8
|
+
During serialization, plain dicts (user data) that contain an `'lc'` key are escaped
|
|
9
|
+
by wrapping them: `{"__lc_escaped__": {...original...}}`. This prevents injection
|
|
10
|
+
attacks where malicious data could trick the deserializer into instantiating
|
|
11
|
+
arbitrary classes. The escape marker is removed during deserialization.
|
|
12
|
+
|
|
13
|
+
This is an allowlist approach: only dicts explicitly produced by
|
|
14
|
+
`Serializable.to_json()` are treated as LC objects; everything else is escaped if it
|
|
15
|
+
could be confused with the LC format.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
|
|
23
|
+
from coco_core.load._validation import _serialize_value
|
|
24
|
+
from coco_core.load.serializable import Serializable, to_json_not_implemented
|
|
25
|
+
from coco_core.messages import AIMessage
|
|
26
|
+
from coco_core.outputs import ChatGeneration
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def default(obj: Any) -> Any:
|
|
30
|
+
"""Return a default value for an object.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
obj: The object to serialize to json if it is a Serializable object.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
A JSON serializable object or a SerializedNotImplemented object.
|
|
37
|
+
"""
|
|
38
|
+
if isinstance(obj, Serializable):
|
|
39
|
+
return obj.to_json()
|
|
40
|
+
return to_json_not_implemented(obj)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _dump_pydantic_models(obj: Any) -> Any:
|
|
44
|
+
"""Convert nested Pydantic models to dicts for JSON serialization.
|
|
45
|
+
|
|
46
|
+
Handles the special case where a `ChatGeneration` contains an `AIMessage`
|
|
47
|
+
with a parsed Pydantic model in `additional_kwargs["parsed"]`. Since
|
|
48
|
+
Pydantic models aren't directly JSON serializable, this converts them to
|
|
49
|
+
dicts.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
obj: The object to process.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
A copy of the object with nested Pydantic models converted to dicts, or
|
|
56
|
+
the original object unchanged if no conversion was needed.
|
|
57
|
+
"""
|
|
58
|
+
if (
|
|
59
|
+
isinstance(obj, ChatGeneration)
|
|
60
|
+
and isinstance(obj.message, AIMessage)
|
|
61
|
+
and (parsed := obj.message.additional_kwargs.get("parsed"))
|
|
62
|
+
and isinstance(parsed, BaseModel)
|
|
63
|
+
):
|
|
64
|
+
obj_copy = obj.model_copy(deep=True)
|
|
65
|
+
obj_copy.message.additional_kwargs["parsed"] = parsed.model_dump()
|
|
66
|
+
return obj_copy
|
|
67
|
+
return obj
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def dumps(obj: Any, *, pretty: bool = False, **kwargs: Any) -> str:
|
|
71
|
+
"""Return a JSON string representation of an object.
|
|
72
|
+
|
|
73
|
+
Note:
|
|
74
|
+
Plain dicts containing an `'lc'` key are automatically escaped to prevent
|
|
75
|
+
confusion with LC serialization format. The escape marker is removed during
|
|
76
|
+
deserialization.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
obj: The object to dump.
|
|
80
|
+
pretty: Whether to pretty print the json.
|
|
81
|
+
|
|
82
|
+
If `True`, the json will be indented by either 2 spaces or the amount
|
|
83
|
+
provided in the `indent` kwarg.
|
|
84
|
+
**kwargs: Additional arguments to pass to `json.dumps`
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
A JSON string representation of the object.
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
ValueError: If `default` is passed as a kwarg.
|
|
91
|
+
"""
|
|
92
|
+
if "default" in kwargs:
|
|
93
|
+
msg = "`default` should not be passed to dumps"
|
|
94
|
+
raise ValueError(msg)
|
|
95
|
+
|
|
96
|
+
obj = _dump_pydantic_models(obj)
|
|
97
|
+
serialized = _serialize_value(obj)
|
|
98
|
+
|
|
99
|
+
if pretty:
|
|
100
|
+
indent = kwargs.pop("indent", 2)
|
|
101
|
+
return json.dumps(serialized, indent=indent, **kwargs)
|
|
102
|
+
return json.dumps(serialized, **kwargs)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def dumpd(obj: Any) -> Any:
|
|
106
|
+
"""Return a dict representation of an object.
|
|
107
|
+
|
|
108
|
+
Note:
|
|
109
|
+
Plain dicts containing an `'lc'` key are automatically escaped to prevent
|
|
110
|
+
confusion with LC serialization format. The escape marker is removed during
|
|
111
|
+
deserialization.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
obj: The object to dump.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Dictionary that can be serialized to json using `json.dumps`.
|
|
118
|
+
"""
|
|
119
|
+
obj = _dump_pydantic_models(obj)
|
|
120
|
+
return _serialize_value(obj)
|