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,208 @@
|
|
|
1
|
+
"""Tool that takes in function or coroutine directly."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from inspect import signature
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from typing_extensions import override
|
|
13
|
+
|
|
14
|
+
# Cannot move to TYPE_CHECKING as _run/_arun parameter annotations are needed at runtime
|
|
15
|
+
from coco_core.callbacks import (
|
|
16
|
+
AsyncCallbackManagerForToolRun, # noqa: TC001
|
|
17
|
+
CallbackManagerForToolRun, # noqa: TC001
|
|
18
|
+
)
|
|
19
|
+
from coco_core.runnables import RunnableConfig, run_in_executor
|
|
20
|
+
from coco_core.tools.base import (
|
|
21
|
+
ArgsSchema,
|
|
22
|
+
BaseTool,
|
|
23
|
+
ToolException,
|
|
24
|
+
_get_runnable_config_param,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from coco_core.messages import ToolCall
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Tool(BaseTool):
|
|
32
|
+
"""Tool that takes in function or coroutine directly."""
|
|
33
|
+
|
|
34
|
+
description: str = ""
|
|
35
|
+
|
|
36
|
+
func: Callable[..., str] | None
|
|
37
|
+
"""The function to run when the tool is called."""
|
|
38
|
+
|
|
39
|
+
coroutine: Callable[..., Awaitable[str]] | None = None
|
|
40
|
+
"""The asynchronous version of the function."""
|
|
41
|
+
|
|
42
|
+
# --- Runnable ---
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
async def ainvoke(
|
|
46
|
+
self,
|
|
47
|
+
input: str | dict[str, Any] | ToolCall,
|
|
48
|
+
config: RunnableConfig | None = None,
|
|
49
|
+
**kwargs: Any,
|
|
50
|
+
) -> Any:
|
|
51
|
+
if not self.coroutine:
|
|
52
|
+
# If the tool does not implement async, fall back to default implementation
|
|
53
|
+
return await run_in_executor(config, self.invoke, input, config, **kwargs)
|
|
54
|
+
|
|
55
|
+
return await super().ainvoke(input, config, **kwargs)
|
|
56
|
+
|
|
57
|
+
# --- Tool ---
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def args(self) -> dict[str, Any]:
|
|
61
|
+
"""The tool's input arguments.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The input arguments for the tool.
|
|
65
|
+
"""
|
|
66
|
+
if self.args_schema is not None:
|
|
67
|
+
return super().args
|
|
68
|
+
# For backwards compatibility, if the function signature is ambiguous,
|
|
69
|
+
# assume it takes a single string input.
|
|
70
|
+
return {"tool_input": {"type": "string"}}
|
|
71
|
+
|
|
72
|
+
def _to_args_and_kwargs(
|
|
73
|
+
self, tool_input: str | dict[str, Any], tool_call_id: str | None
|
|
74
|
+
) -> tuple[tuple[str, ...], dict[str, Any]]:
|
|
75
|
+
"""Convert tool input to Pydantic model.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
tool_input: The input to the tool.
|
|
79
|
+
tool_call_id: The ID of the tool call.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
ToolException: If the tool input is invalid.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
The Pydantic model args and kwargs.
|
|
86
|
+
"""
|
|
87
|
+
args, kwargs = super()._to_args_and_kwargs(tool_input, tool_call_id)
|
|
88
|
+
# For backwards compatibility. The tool must be run with a single input
|
|
89
|
+
all_args = list(args) + list(kwargs.values())
|
|
90
|
+
if len(all_args) != 1:
|
|
91
|
+
msg = (
|
|
92
|
+
f"""Too many arguments to single-input tool {self.name}.
|
|
93
|
+
Consider using StructuredTool instead."""
|
|
94
|
+
f" Args: {all_args}"
|
|
95
|
+
)
|
|
96
|
+
raise ToolException(msg)
|
|
97
|
+
return tuple(all_args), {}
|
|
98
|
+
|
|
99
|
+
def _run(
|
|
100
|
+
self,
|
|
101
|
+
*args: Any,
|
|
102
|
+
config: RunnableConfig,
|
|
103
|
+
run_manager: CallbackManagerForToolRun | None = None,
|
|
104
|
+
**kwargs: Any,
|
|
105
|
+
) -> Any:
|
|
106
|
+
"""Use the tool.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
*args: Positional arguments to pass to the tool
|
|
110
|
+
config: Configuration for the run
|
|
111
|
+
run_manager: Optional callback manager to use for the run
|
|
112
|
+
**kwargs: Keyword arguments to pass to the tool
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
The result of the tool execution
|
|
116
|
+
"""
|
|
117
|
+
if self.func:
|
|
118
|
+
if run_manager and signature(self.func).parameters.get("callbacks"):
|
|
119
|
+
kwargs["callbacks"] = run_manager.get_child()
|
|
120
|
+
if config_param := _get_runnable_config_param(self.func):
|
|
121
|
+
kwargs[config_param] = config
|
|
122
|
+
return self.func(*args, **kwargs)
|
|
123
|
+
msg = "Tool does not support sync invocation."
|
|
124
|
+
raise NotImplementedError(msg)
|
|
125
|
+
|
|
126
|
+
async def _arun(
|
|
127
|
+
self,
|
|
128
|
+
*args: Any,
|
|
129
|
+
config: RunnableConfig,
|
|
130
|
+
run_manager: AsyncCallbackManagerForToolRun | None = None,
|
|
131
|
+
**kwargs: Any,
|
|
132
|
+
) -> Any:
|
|
133
|
+
"""Use the tool asynchronously.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
*args: Positional arguments to pass to the tool
|
|
137
|
+
config: Configuration for the run
|
|
138
|
+
run_manager: Optional callback manager to use for the run
|
|
139
|
+
**kwargs: Keyword arguments to pass to the tool
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
The result of the tool execution
|
|
143
|
+
"""
|
|
144
|
+
if self.coroutine:
|
|
145
|
+
if run_manager and signature(self.coroutine).parameters.get("callbacks"):
|
|
146
|
+
kwargs["callbacks"] = run_manager.get_child()
|
|
147
|
+
if config_param := _get_runnable_config_param(self.coroutine):
|
|
148
|
+
kwargs[config_param] = config
|
|
149
|
+
return await self.coroutine(*args, **kwargs)
|
|
150
|
+
|
|
151
|
+
# NOTE: this code is unreachable since _arun is only called if coroutine is not
|
|
152
|
+
# None.
|
|
153
|
+
return await super()._arun(
|
|
154
|
+
*args, config=config, run_manager=run_manager, **kwargs
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# TODO: this is for backwards compatibility, remove in future
|
|
158
|
+
def __init__(
|
|
159
|
+
self,
|
|
160
|
+
name: str,
|
|
161
|
+
func: Callable[..., Any] | None,
|
|
162
|
+
description: str,
|
|
163
|
+
**kwargs: Any,
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Initialize tool."""
|
|
166
|
+
super().__init__(name=name, func=func, description=description, **kwargs)
|
|
167
|
+
|
|
168
|
+
@classmethod
|
|
169
|
+
def from_function(
|
|
170
|
+
cls,
|
|
171
|
+
func: Callable[..., Any] | None,
|
|
172
|
+
name: str, # We keep these required to support backwards compatibility
|
|
173
|
+
description: str,
|
|
174
|
+
return_direct: bool = False, # noqa: FBT001,FBT002
|
|
175
|
+
args_schema: ArgsSchema | None = None,
|
|
176
|
+
coroutine: Callable[..., Awaitable[Any]]
|
|
177
|
+
| None = None, # This is last for compatibility, but should be after func
|
|
178
|
+
**kwargs: Any,
|
|
179
|
+
) -> Tool:
|
|
180
|
+
"""Initialize tool from a function.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
func: The function to create the tool from.
|
|
184
|
+
name: The name of the tool.
|
|
185
|
+
description: The description of the tool.
|
|
186
|
+
return_direct: Whether to return the output directly.
|
|
187
|
+
args_schema: The schema of the tool's input arguments.
|
|
188
|
+
coroutine: The asynchronous version of the function.
|
|
189
|
+
**kwargs: Additional arguments to pass to the tool.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
The tool.
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
ValueError: If the function is not provided.
|
|
196
|
+
"""
|
|
197
|
+
if func is None and coroutine is None:
|
|
198
|
+
msg = "Function and/or coroutine must be provided"
|
|
199
|
+
raise ValueError(msg)
|
|
200
|
+
return cls(
|
|
201
|
+
name=name,
|
|
202
|
+
func=func,
|
|
203
|
+
coroutine=coroutine,
|
|
204
|
+
description=description,
|
|
205
|
+
return_direct=return_direct,
|
|
206
|
+
args_schema=args_schema,
|
|
207
|
+
**kwargs,
|
|
208
|
+
)
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"""Structured tool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import textwrap
|
|
7
|
+
from collections.abc import Awaitable, Callable
|
|
8
|
+
from inspect import signature
|
|
9
|
+
from typing import (
|
|
10
|
+
TYPE_CHECKING,
|
|
11
|
+
Annotated,
|
|
12
|
+
Any,
|
|
13
|
+
Literal,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from pydantic import Field, PlainSerializer, SkipValidation
|
|
17
|
+
from typing_extensions import override
|
|
18
|
+
|
|
19
|
+
# Cannot move to TYPE_CHECKING as _run/_arun parameter annotations are needed at runtime
|
|
20
|
+
from coco_core.callbacks import (
|
|
21
|
+
AsyncCallbackManagerForToolRun, # noqa: TC001
|
|
22
|
+
CallbackManagerForToolRun, # noqa: TC001
|
|
23
|
+
)
|
|
24
|
+
from coco_core.runnables import RunnableConfig, run_in_executor
|
|
25
|
+
from coco_core.tools.base import (
|
|
26
|
+
_EMPTY_SET,
|
|
27
|
+
FILTERED_ARGS,
|
|
28
|
+
ArgsSchema,
|
|
29
|
+
BaseTool,
|
|
30
|
+
_get_injected_args_keys_from_signature,
|
|
31
|
+
_get_runnable_config_param,
|
|
32
|
+
create_schema_from_function,
|
|
33
|
+
)
|
|
34
|
+
from coco_core.utils.pydantic import is_basemodel_subclass, model_json_schema
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from coco_core.messages import ToolCall
|
|
38
|
+
from coco_core.utils.pydantic import TypeBaseModel
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _serialize_as_str(value: Any) -> str:
|
|
42
|
+
"""Stringify a value that has no JSON form.
|
|
43
|
+
|
|
44
|
+
A plain function rather than the `str` builtin: Pydantic < 2.9 inspects the
|
|
45
|
+
serializer's signature, and `inspect.signature` raises for C builtins.
|
|
46
|
+
"""
|
|
47
|
+
return str(value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@functools.lru_cache(maxsize=256)
|
|
51
|
+
def _model_json_schema(args_schema: TypeBaseModel, _rebuild_token: object) -> Any:
|
|
52
|
+
"""Ask a v1 or v2 model class for its JSON schema, or fall back to its repr.
|
|
53
|
+
|
|
54
|
+
Cached because pydantic does not memoize this per class and it dominates the
|
|
55
|
+
dump -- 1.10 ms against 0.02 ms for an eight-tool payload -- which tracing
|
|
56
|
+
pays on every run.
|
|
57
|
+
|
|
58
|
+
`_rebuild_token` is the class's validator, which `model_rebuild()` replaces.
|
|
59
|
+
Keying on it retires the entry for a rebuilt schema, and for one whose
|
|
60
|
+
forward reference was still unresolved when it was first dumped. Mutating a
|
|
61
|
+
class in place without rebuilding it is not detected.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
return model_json_schema(args_schema)
|
|
65
|
+
except Exception: # a schema holding an arbitrary type has no JSON schema
|
|
66
|
+
return str(args_schema)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _serialize_args_schema(args_schema: ArgsSchema) -> Any:
|
|
70
|
+
"""Represent a schema class by its JSON schema when dumping to JSON.
|
|
71
|
+
|
|
72
|
+
A Pydantic model class has no JSON form, so leaving it to the default
|
|
73
|
+
serializer raises `PydanticSerializationError`. Its own JSON schema is the
|
|
74
|
+
shape a dict schema already has, and a dict is returned unchanged.
|
|
75
|
+
"""
|
|
76
|
+
if isinstance(args_schema, dict):
|
|
77
|
+
return args_schema
|
|
78
|
+
return _model_json_schema(
|
|
79
|
+
args_schema, getattr(args_schema, "__pydantic_validator__", None)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Attached to the fields via `Annotated` rather than declared with
|
|
84
|
+
# `@field_serializer`, which would take the field's one serializer slot and make
|
|
85
|
+
# any subclass that declares its own serializer for the same field fail at class
|
|
86
|
+
# creation with `PydanticUserError: Multiple field serializer functions ...`.
|
|
87
|
+
_JsonSchemaFallback = PlainSerializer(
|
|
88
|
+
_serialize_args_schema, when_used="json-unless-none"
|
|
89
|
+
)
|
|
90
|
+
_JsonCallableFallback = PlainSerializer(_serialize_as_str, when_used="json-unless-none")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class StructuredTool(BaseTool):
|
|
94
|
+
"""Tool that can operate on any number of inputs."""
|
|
95
|
+
|
|
96
|
+
description: str = ""
|
|
97
|
+
|
|
98
|
+
args_schema: Annotated[ArgsSchema, SkipValidation(), _JsonSchemaFallback] = Field(
|
|
99
|
+
..., description="The tool schema."
|
|
100
|
+
)
|
|
101
|
+
"""The input arguments' schema."""
|
|
102
|
+
|
|
103
|
+
func: Annotated[Callable[..., Any] | None, _JsonCallableFallback] = None
|
|
104
|
+
"""The function to run when the tool is called."""
|
|
105
|
+
|
|
106
|
+
coroutine: Annotated[
|
|
107
|
+
Callable[..., Awaitable[Any]] | None, _JsonCallableFallback
|
|
108
|
+
] = None
|
|
109
|
+
"""The asynchronous version of the function."""
|
|
110
|
+
|
|
111
|
+
# --- Runnable ---
|
|
112
|
+
|
|
113
|
+
# TODO: Is this needed?
|
|
114
|
+
@override
|
|
115
|
+
async def ainvoke(
|
|
116
|
+
self,
|
|
117
|
+
input: str | dict[str, Any] | ToolCall,
|
|
118
|
+
config: RunnableConfig | None = None,
|
|
119
|
+
**kwargs: Any,
|
|
120
|
+
) -> Any:
|
|
121
|
+
if not self.coroutine:
|
|
122
|
+
# If the tool does not implement async, fall back to default implementation
|
|
123
|
+
return await run_in_executor(config, self.invoke, input, config, **kwargs)
|
|
124
|
+
|
|
125
|
+
return await super().ainvoke(input, config, **kwargs)
|
|
126
|
+
|
|
127
|
+
# --- Tool ---
|
|
128
|
+
|
|
129
|
+
def _run(
|
|
130
|
+
self,
|
|
131
|
+
*args: Any,
|
|
132
|
+
config: RunnableConfig,
|
|
133
|
+
run_manager: CallbackManagerForToolRun | None = None,
|
|
134
|
+
**kwargs: Any,
|
|
135
|
+
) -> Any:
|
|
136
|
+
"""Use the tool.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
*args: Positional arguments to pass to the tool
|
|
140
|
+
config: Configuration for the run
|
|
141
|
+
run_manager: Optional callback manager to use for the run
|
|
142
|
+
**kwargs: Keyword arguments to pass to the tool
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
The result of the tool execution
|
|
146
|
+
"""
|
|
147
|
+
if self.func:
|
|
148
|
+
if run_manager and signature(self.func).parameters.get("callbacks"):
|
|
149
|
+
kwargs["callbacks"] = run_manager.get_child()
|
|
150
|
+
if config_param := _get_runnable_config_param(self.func):
|
|
151
|
+
kwargs[config_param] = config
|
|
152
|
+
return self.func(*args, **kwargs)
|
|
153
|
+
msg = "StructuredTool does not support sync invocation."
|
|
154
|
+
raise NotImplementedError(msg)
|
|
155
|
+
|
|
156
|
+
async def _arun(
|
|
157
|
+
self,
|
|
158
|
+
*args: Any,
|
|
159
|
+
config: RunnableConfig,
|
|
160
|
+
run_manager: AsyncCallbackManagerForToolRun | None = None,
|
|
161
|
+
**kwargs: Any,
|
|
162
|
+
) -> Any:
|
|
163
|
+
"""Use the tool asynchronously.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
*args: Positional arguments to pass to the tool
|
|
167
|
+
config: Configuration for the run
|
|
168
|
+
run_manager: Optional callback manager to use for the run
|
|
169
|
+
**kwargs: Keyword arguments to pass to the tool
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
The result of the tool execution
|
|
173
|
+
"""
|
|
174
|
+
if self.coroutine:
|
|
175
|
+
if run_manager and signature(self.coroutine).parameters.get("callbacks"):
|
|
176
|
+
kwargs["callbacks"] = run_manager.get_child()
|
|
177
|
+
if config_param := _get_runnable_config_param(self.coroutine):
|
|
178
|
+
kwargs[config_param] = config
|
|
179
|
+
return await self.coroutine(*args, **kwargs)
|
|
180
|
+
|
|
181
|
+
# If self.coroutine is None, then this will delegate to the default
|
|
182
|
+
# implementation which is expected to delegate to _run on a separate thread.
|
|
183
|
+
return await super()._arun(
|
|
184
|
+
*args, config=config, run_manager=run_manager, **kwargs
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
@classmethod
|
|
188
|
+
def from_function(
|
|
189
|
+
cls,
|
|
190
|
+
func: Callable[..., Any] | None = None,
|
|
191
|
+
coroutine: Callable[..., Awaitable[Any]] | None = None,
|
|
192
|
+
name: str | None = None,
|
|
193
|
+
description: str | None = None,
|
|
194
|
+
return_direct: bool = False, # noqa: FBT001,FBT002
|
|
195
|
+
args_schema: ArgsSchema | None = None,
|
|
196
|
+
infer_schema: bool = True, # noqa: FBT001,FBT002
|
|
197
|
+
*,
|
|
198
|
+
response_format: Literal["content", "content_and_artifact"] = "content",
|
|
199
|
+
parse_docstring: bool = False,
|
|
200
|
+
error_on_invalid_docstring: bool = False,
|
|
201
|
+
**kwargs: Any,
|
|
202
|
+
) -> StructuredTool:
|
|
203
|
+
"""Create tool from a given function.
|
|
204
|
+
|
|
205
|
+
A classmethod that helps to create a tool from a function.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
func: The function from which to create a tool.
|
|
209
|
+
coroutine: The async function from which to create a tool.
|
|
210
|
+
name: The name of the tool.
|
|
211
|
+
|
|
212
|
+
Defaults to the function name.
|
|
213
|
+
description: The description of the tool.
|
|
214
|
+
|
|
215
|
+
Defaults to the function docstring.
|
|
216
|
+
return_direct: Whether to return the result directly or as a callback.
|
|
217
|
+
args_schema: The schema of the tool's input arguments.
|
|
218
|
+
infer_schema: Whether to infer the schema from the function's signature.
|
|
219
|
+
response_format: The tool response format.
|
|
220
|
+
|
|
221
|
+
If `'content'` then the output of the tool is interpreted as the
|
|
222
|
+
contents of a `ToolMessage`. If `'content_and_artifact'` then the output
|
|
223
|
+
is expected to be a two-tuple corresponding to the `(content, artifact)`
|
|
224
|
+
of a `ToolMessage`.
|
|
225
|
+
parse_docstring: If `infer_schema` and `parse_docstring`, will attempt
|
|
226
|
+
to parse parameter descriptions from Google Style function docstrings.
|
|
227
|
+
error_on_invalid_docstring: if `parse_docstring` is provided, configure
|
|
228
|
+
whether to raise `ValueError` on invalid Google Style docstrings.
|
|
229
|
+
**kwargs: Additional arguments to pass to the tool
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
The tool.
|
|
233
|
+
|
|
234
|
+
Raises:
|
|
235
|
+
ValueError: If the function is not provided.
|
|
236
|
+
ValueError: If the function does not have a docstring and description
|
|
237
|
+
is not provided.
|
|
238
|
+
TypeError: If the `args_schema` is not a `BaseModel` or dict.
|
|
239
|
+
|
|
240
|
+
Examples:
|
|
241
|
+
```python
|
|
242
|
+
def add(a: int, b: int) -> int:
|
|
243
|
+
\"\"\"Add two numbers\"\"\"
|
|
244
|
+
return a + b
|
|
245
|
+
tool = StructuredTool.from_function(add)
|
|
246
|
+
tool.run(1, 2) # 3
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
"""
|
|
250
|
+
if func is not None:
|
|
251
|
+
source_function = func
|
|
252
|
+
elif coroutine is not None:
|
|
253
|
+
source_function = coroutine
|
|
254
|
+
else:
|
|
255
|
+
msg = "Function and/or coroutine must be provided"
|
|
256
|
+
raise ValueError(msg)
|
|
257
|
+
name = name or source_function.__name__
|
|
258
|
+
if args_schema is None and infer_schema:
|
|
259
|
+
# schema name is appended within function
|
|
260
|
+
args_schema = create_schema_from_function(
|
|
261
|
+
name,
|
|
262
|
+
source_function,
|
|
263
|
+
parse_docstring=parse_docstring,
|
|
264
|
+
error_on_invalid_docstring=error_on_invalid_docstring,
|
|
265
|
+
filter_args=_filter_schema_args(source_function),
|
|
266
|
+
)
|
|
267
|
+
description_ = description
|
|
268
|
+
if description is None and not parse_docstring:
|
|
269
|
+
description_ = source_function.__doc__ or None
|
|
270
|
+
if description_ is None and args_schema:
|
|
271
|
+
if isinstance(args_schema, type) and is_basemodel_subclass(args_schema):
|
|
272
|
+
description_ = args_schema.__doc__
|
|
273
|
+
if (
|
|
274
|
+
description_
|
|
275
|
+
and "A base class for creating Pydantic models" in description_
|
|
276
|
+
):
|
|
277
|
+
description_ = ""
|
|
278
|
+
elif not description_:
|
|
279
|
+
description_ = None
|
|
280
|
+
elif isinstance(args_schema, dict):
|
|
281
|
+
description_ = args_schema.get("description")
|
|
282
|
+
else:
|
|
283
|
+
msg = (
|
|
284
|
+
"Invalid args_schema: expected BaseModel or dict, "
|
|
285
|
+
f"got {args_schema}"
|
|
286
|
+
)
|
|
287
|
+
raise TypeError(msg)
|
|
288
|
+
if description_ is None:
|
|
289
|
+
msg = "Function must have a docstring if description not provided."
|
|
290
|
+
raise ValueError(msg)
|
|
291
|
+
if description is None:
|
|
292
|
+
# Only apply if using the function's docstring
|
|
293
|
+
description_ = textwrap.dedent(description_).strip()
|
|
294
|
+
|
|
295
|
+
# Description example:
|
|
296
|
+
# search_api(query: str) - Searches the API for the query.
|
|
297
|
+
description_ = f"{description_.strip()}"
|
|
298
|
+
return cls(
|
|
299
|
+
name=name,
|
|
300
|
+
func=func,
|
|
301
|
+
coroutine=coroutine,
|
|
302
|
+
args_schema=args_schema,
|
|
303
|
+
description=description_,
|
|
304
|
+
return_direct=return_direct,
|
|
305
|
+
response_format=response_format,
|
|
306
|
+
**kwargs,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
@functools.cached_property
|
|
310
|
+
def _injected_args_keys(self) -> frozenset[str]:
|
|
311
|
+
fn = self.func or self.coroutine
|
|
312
|
+
if fn is None:
|
|
313
|
+
return _EMPTY_SET
|
|
314
|
+
return _get_injected_args_keys_from_signature(fn)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _filter_schema_args(func: Callable[..., Any]) -> list[str]:
|
|
318
|
+
filter_args = list(FILTERED_ARGS)
|
|
319
|
+
if config_param := _get_runnable_config_param(func):
|
|
320
|
+
filter_args.append(config_param)
|
|
321
|
+
# filter_args.extend(_get_non_model_params(type_hints))
|
|
322
|
+
return filter_args
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Tracers are classes for tracing runs."""
|
|
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.tracers.base import BaseTracer
|
|
9
|
+
from coco_core.tracers.coco import CocoTracer
|
|
10
|
+
from coco_core.tracers.log_stream import (
|
|
11
|
+
LogStreamCallbackHandler,
|
|
12
|
+
RunLog,
|
|
13
|
+
RunLogPatch,
|
|
14
|
+
)
|
|
15
|
+
from coco_core.tracers.schemas import Run
|
|
16
|
+
from coco_core.tracers.stdout import ConsoleCallbackHandler
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
|
+
"BaseTracer",
|
|
20
|
+
"ConsoleCallbackHandler",
|
|
21
|
+
"CocoTracer",
|
|
22
|
+
"LogStreamCallbackHandler",
|
|
23
|
+
"Run",
|
|
24
|
+
"RunLog",
|
|
25
|
+
"RunLogPatch",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
_dynamic_imports = {
|
|
29
|
+
"BaseTracer": "base",
|
|
30
|
+
"CocoTracer": "coco",
|
|
31
|
+
"LogStreamCallbackHandler": "log_stream",
|
|
32
|
+
"RunLog": "log_stream",
|
|
33
|
+
"RunLogPatch": "log_stream",
|
|
34
|
+
"Run": "schemas",
|
|
35
|
+
"ConsoleCallbackHandler": "stdout",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def __getattr__(attr_name: str) -> object:
|
|
40
|
+
module_name = _dynamic_imports.get(attr_name)
|
|
41
|
+
result = import_attr(attr_name, module_name, __spec__.parent)
|
|
42
|
+
globals()[attr_name] = result
|
|
43
|
+
return result
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def __dir__() -> list[str]:
|
|
47
|
+
return list(__all__)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Compatibility helpers for Pydantic v1/v2 with langsmith `Run` objects.
|
|
2
|
+
|
|
3
|
+
!!! note
|
|
4
|
+
|
|
5
|
+
The generic helpers (`pydantic_to_dict`, `pydantic_copy`) detect Pydanti version
|
|
6
|
+
based on the langsmith `Run` model. They're intended for langsmith objects (`Run`,
|
|
7
|
+
`Example`) which migrate together.
|
|
8
|
+
|
|
9
|
+
For general Pydantic v1/v2 handling, see `coco_core.utils.pydantic`.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any, TypeVar
|
|
15
|
+
|
|
16
|
+
from coco_core.tracers.schemas import Run
|
|
17
|
+
|
|
18
|
+
# Detect Pydantic version once at import time based on Run model
|
|
19
|
+
_RUN_IS_PYDANTIC_V2 = hasattr(Run, "model_dump")
|
|
20
|
+
|
|
21
|
+
T = TypeVar("T")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def run_to_dict(run: Run, **kwargs: Any) -> dict[str, Any]:
|
|
25
|
+
"""Convert run to dict, compatible with both Pydantic v1 and v2.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
run: The run to convert.
|
|
29
|
+
**kwargs: Additional arguments passed to `model_dump`/`dict`.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Dictionary representation of the run.
|
|
33
|
+
"""
|
|
34
|
+
if _RUN_IS_PYDANTIC_V2:
|
|
35
|
+
return run.model_dump(**kwargs)
|
|
36
|
+
return run.dict(**kwargs) # type: ignore[deprecated]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def run_copy(run: Run, **kwargs: Any) -> Run:
|
|
40
|
+
"""Copy run, compatible with both Pydantic v1 and v2.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
run: The run to copy.
|
|
44
|
+
**kwargs: Additional arguments passed to `model_copy`/`copy`.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
A copy of the run.
|
|
48
|
+
"""
|
|
49
|
+
if _RUN_IS_PYDANTIC_V2:
|
|
50
|
+
return run.model_copy(**kwargs)
|
|
51
|
+
return run.copy(**kwargs) # type: ignore[deprecated]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def run_construct(**kwargs: Any) -> Run:
|
|
55
|
+
"""Construct run without validation, compatible with both Pydantic v1 and v2.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
**kwargs: Fields to set on the run.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
A new `Run` instance constructed without validation.
|
|
62
|
+
"""
|
|
63
|
+
if _RUN_IS_PYDANTIC_V2:
|
|
64
|
+
return Run.model_construct(**kwargs)
|
|
65
|
+
return Run.construct(**kwargs) # type: ignore[deprecated]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def pydantic_to_dict(obj: Any, **kwargs: Any) -> dict[str, Any]:
|
|
69
|
+
"""Convert any Pydantic model to dict, compatible with both v1 and v2.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
obj: The Pydantic model to convert.
|
|
73
|
+
**kwargs: Additional arguments passed to `model_dump`/`dict`.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Dictionary representation of the model.
|
|
77
|
+
"""
|
|
78
|
+
if _RUN_IS_PYDANTIC_V2:
|
|
79
|
+
return obj.model_dump(**kwargs) # type: ignore[no-any-return]
|
|
80
|
+
return obj.dict(**kwargs) # type: ignore[no-any-return]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def pydantic_copy(obj: T, **kwargs: Any) -> T:
|
|
84
|
+
"""Copy any Pydantic model, compatible with both v1 and v2.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
obj: The Pydantic model to copy.
|
|
88
|
+
**kwargs: Additional arguments passed to `model_copy`/`copy`.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
A copy of the model.
|
|
92
|
+
"""
|
|
93
|
+
if _RUN_IS_PYDANTIC_V2:
|
|
94
|
+
return obj.model_copy(**kwargs) # type: ignore[attr-defined,no-any-return]
|
|
95
|
+
return obj.copy(**kwargs) # type: ignore[attr-defined,no-any-return]
|