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,120 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import datetime
|
|
5
|
+
import sqlite3
|
|
6
|
+
import threading
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
8
|
+
|
|
9
|
+
from coco_graph.cache.base import BaseCache, FullKey, Namespace, ValueT
|
|
10
|
+
from coco_graph.checkpoint.serde.base import SerializerProtocol
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SqliteCache(BaseCache[ValueT]):
|
|
14
|
+
"""File-based cache using SQLite."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
path: str,
|
|
20
|
+
serde: SerializerProtocol | None = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Initialize the cache with a file path."""
|
|
23
|
+
super().__init__(serde=serde)
|
|
24
|
+
# SQLite backing store
|
|
25
|
+
self._conn = sqlite3.connect(
|
|
26
|
+
path,
|
|
27
|
+
check_same_thread=False,
|
|
28
|
+
)
|
|
29
|
+
# Serialize access to the shared connection across threads
|
|
30
|
+
self._lock = threading.RLock()
|
|
31
|
+
# Better concurrency & atomicity
|
|
32
|
+
self._conn.execute("PRAGMA journal_mode=WAL;")
|
|
33
|
+
# Schema: key -> (expiry, encoding, value)
|
|
34
|
+
self._conn.execute(
|
|
35
|
+
"""CREATE TABLE IF NOT EXISTS cache (
|
|
36
|
+
ns TEXT,
|
|
37
|
+
key TEXT,
|
|
38
|
+
expiry REAL,
|
|
39
|
+
encoding TEXT NOT NULL,
|
|
40
|
+
val BLOB NOT NULL,
|
|
41
|
+
PRIMARY KEY (ns, key)
|
|
42
|
+
)"""
|
|
43
|
+
)
|
|
44
|
+
self._conn.commit()
|
|
45
|
+
|
|
46
|
+
def get(self, keys: Sequence[FullKey]) -> dict[FullKey, ValueT]:
|
|
47
|
+
"""Get the cached values for the given keys."""
|
|
48
|
+
with self._lock, self._conn:
|
|
49
|
+
now = datetime.datetime.now(datetime.timezone.utc).timestamp()
|
|
50
|
+
if not keys:
|
|
51
|
+
return {}
|
|
52
|
+
placeholders = ",".join("(?, ?)" for _ in keys)
|
|
53
|
+
params: list[str] = []
|
|
54
|
+
for ns_tuple, key in keys:
|
|
55
|
+
params.extend((",".join(ns_tuple), key))
|
|
56
|
+
cursor = self._conn.execute(
|
|
57
|
+
f"SELECT ns, key, expiry, encoding, val FROM cache WHERE (ns, key) IN ({placeholders})",
|
|
58
|
+
tuple(params),
|
|
59
|
+
)
|
|
60
|
+
values: dict[FullKey, ValueT] = {}
|
|
61
|
+
rows = cursor.fetchall()
|
|
62
|
+
for ns, key, expiry, encoding, raw in rows:
|
|
63
|
+
if expiry is not None and now > expiry:
|
|
64
|
+
# purge expired entry
|
|
65
|
+
self._conn.execute(
|
|
66
|
+
"DELETE FROM cache WHERE (ns, key) = (?, ?)", (ns, key)
|
|
67
|
+
)
|
|
68
|
+
continue
|
|
69
|
+
values[(tuple(ns.split(",")), key)] = self.serde.loads_typed(
|
|
70
|
+
(encoding, raw)
|
|
71
|
+
)
|
|
72
|
+
return values
|
|
73
|
+
|
|
74
|
+
async def aget(self, keys: Sequence[FullKey]) -> dict[FullKey, ValueT]:
|
|
75
|
+
"""Asynchronously get the cached values for the given keys."""
|
|
76
|
+
return await asyncio.to_thread(self.get, keys)
|
|
77
|
+
|
|
78
|
+
def set(self, mapping: Mapping[FullKey, tuple[ValueT, int | None]]) -> None:
|
|
79
|
+
"""Set the cached values for the given keys and TTLs."""
|
|
80
|
+
with self._lock, self._conn:
|
|
81
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
|
82
|
+
for key, (value, ttl) in mapping.items():
|
|
83
|
+
if ttl is not None:
|
|
84
|
+
delta = datetime.timedelta(seconds=ttl)
|
|
85
|
+
expiry: float | None = (now + delta).timestamp()
|
|
86
|
+
else:
|
|
87
|
+
expiry = None
|
|
88
|
+
encoding, raw = self.serde.dumps_typed(value)
|
|
89
|
+
self._conn.execute(
|
|
90
|
+
"INSERT OR REPLACE INTO cache (ns, key, expiry, encoding, val) VALUES (?, ?, ?, ?, ?)",
|
|
91
|
+
(",".join(key[0]), key[1], expiry, encoding, raw),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
async def aset(self, mapping: Mapping[FullKey, tuple[ValueT, int | None]]) -> None:
|
|
95
|
+
"""Asynchronously set the cached values for the given keys and TTLs."""
|
|
96
|
+
await asyncio.to_thread(self.set, mapping)
|
|
97
|
+
|
|
98
|
+
def clear(self, namespaces: Sequence[Namespace] | None = None) -> None:
|
|
99
|
+
"""Delete the cached values for the given namespaces.
|
|
100
|
+
If no namespaces are provided, clear all cached values."""
|
|
101
|
+
with self._lock, self._conn:
|
|
102
|
+
if namespaces is None:
|
|
103
|
+
self._conn.execute("DELETE FROM cache")
|
|
104
|
+
else:
|
|
105
|
+
placeholders = ",".join("?" for _ in namespaces)
|
|
106
|
+
self._conn.execute(
|
|
107
|
+
f"DELETE FROM cache WHERE (ns) IN ({placeholders})",
|
|
108
|
+
tuple(",".join(key) for key in namespaces),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
async def aclear(self, namespaces: Sequence[Namespace] | None = None) -> None:
|
|
112
|
+
"""Asynchronously delete the cached values for the given namespaces.
|
|
113
|
+
If no namespaces are provided, clear all cached values."""
|
|
114
|
+
await asyncio.to_thread(self.clear, namespaces)
|
|
115
|
+
|
|
116
|
+
def __del__(self) -> None:
|
|
117
|
+
try:
|
|
118
|
+
self._conn.close()
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
coco_graph/callbacks.py
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"""Graph lifecycle callback interfaces and event payloads.
|
|
2
|
+
|
|
3
|
+
This module defines the public callback surface for observing CocoGraph-specific
|
|
4
|
+
lifecycle transitions such as interrupt and resume.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Literal, TypeAlias, TypeVar
|
|
12
|
+
from uuid import UUID
|
|
13
|
+
|
|
14
|
+
from coco_core.callbacks import BaseCallbackHandler, BaseCallbackManager
|
|
15
|
+
from coco_core.callbacks.manager import ahandle_event, handle_event
|
|
16
|
+
from coco_core.runnables import RunnableConfig
|
|
17
|
+
|
|
18
|
+
from coco_graph.types import Interrupt
|
|
19
|
+
|
|
20
|
+
__all__ = (
|
|
21
|
+
"GraphCallbackHandler",
|
|
22
|
+
"GraphInterruptEvent",
|
|
23
|
+
"GraphLifecycleEvent",
|
|
24
|
+
"GraphLifecycleStatus",
|
|
25
|
+
"GraphResumeEvent",
|
|
26
|
+
"get_async_graph_callback_manager_for_config",
|
|
27
|
+
"get_sync_graph_callback_manager_for_config",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
GraphLifecycleStatus: TypeAlias = Literal[
|
|
32
|
+
"input",
|
|
33
|
+
"pending",
|
|
34
|
+
"done",
|
|
35
|
+
"interrupt_before",
|
|
36
|
+
"interrupt_after",
|
|
37
|
+
"out_of_steps",
|
|
38
|
+
]
|
|
39
|
+
"""Allowed lifecycle statuses reported in graph lifecycle callback events."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True)
|
|
43
|
+
class GraphInterruptEvent:
|
|
44
|
+
"""Graph lifecycle event emitted when execution pauses for interrupts."""
|
|
45
|
+
|
|
46
|
+
run_id: UUID | None
|
|
47
|
+
"""Run id for the current graph execution, if available."""
|
|
48
|
+
|
|
49
|
+
status: GraphLifecycleStatus
|
|
50
|
+
"""Loop status when the interrupt was captured."""
|
|
51
|
+
|
|
52
|
+
checkpoint_id: str
|
|
53
|
+
"""Checkpoint id associated with the interrupted execution."""
|
|
54
|
+
|
|
55
|
+
checkpoint_ns: tuple[str, ...]
|
|
56
|
+
"""Checkpoint namespace path for the current graph or subgraph."""
|
|
57
|
+
|
|
58
|
+
interrupts: tuple[Interrupt, ...]
|
|
59
|
+
"""Interrupt payloads that caused the graph to pause."""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True)
|
|
63
|
+
class GraphResumeEvent:
|
|
64
|
+
"""Graph lifecycle event emitted when execution resumes from a checkpoint."""
|
|
65
|
+
|
|
66
|
+
run_id: UUID | None
|
|
67
|
+
"""Run id for the current graph execution, if available."""
|
|
68
|
+
|
|
69
|
+
status: GraphLifecycleStatus
|
|
70
|
+
"""Loop status when the resume was captured."""
|
|
71
|
+
|
|
72
|
+
checkpoint_id: str
|
|
73
|
+
"""Checkpoint id the graph resumed from."""
|
|
74
|
+
|
|
75
|
+
checkpoint_ns: tuple[str, ...]
|
|
76
|
+
"""Checkpoint namespace path for the current graph or subgraph."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
GraphLifecycleEvent: TypeAlias = GraphInterruptEvent | GraphResumeEvent
|
|
80
|
+
"""Union of all public graph lifecycle callback event payloads.
|
|
81
|
+
|
|
82
|
+
Use this alias when a callback or helper can receive either interrupt or resume
|
|
83
|
+
lifecycle events.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class GraphCallbackHandler(BaseCallbackHandler):
|
|
88
|
+
"""Base class for graph-level lifecycle callbacks.
|
|
89
|
+
|
|
90
|
+
Subclass this handler to observe graph lifecycle transitions that are
|
|
91
|
+
specific to CocoGraph execution, rather than generic Coco runnable
|
|
92
|
+
callbacks.
|
|
93
|
+
|
|
94
|
+
Instances can be passed through `config["callbacks"]` when invoking a
|
|
95
|
+
graph. Only handlers that inherit from `GraphCallbackHandler` receive these
|
|
96
|
+
lifecycle events.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def on_interrupt(self, event: GraphInterruptEvent) -> Any:
|
|
100
|
+
"""Run when graph execution pauses due to one or more interrupts.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
event: Interrupt lifecycle event payload.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def on_resume(self, event: GraphResumeEvent) -> Any:
|
|
107
|
+
"""Run when graph execution resumes from a persisted checkpoint.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
event: Resume lifecycle event payload.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
_MISSING = object()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _filter_graph_handlers(
|
|
118
|
+
handlers: list[BaseCallbackHandler],
|
|
119
|
+
) -> list[GraphCallbackHandler]:
|
|
120
|
+
return [h for h in handlers if isinstance(h, GraphCallbackHandler)]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _init_base_manager(
|
|
124
|
+
manager: BaseCallbackManager,
|
|
125
|
+
handlers: Sequence[GraphCallbackHandler] | None,
|
|
126
|
+
inheritable_handlers: Sequence[GraphCallbackHandler] | None,
|
|
127
|
+
parent_run_id: UUID | None,
|
|
128
|
+
*,
|
|
129
|
+
tags: list[str] | None,
|
|
130
|
+
inheritable_tags: list[str] | None,
|
|
131
|
+
metadata: dict[str, Any] | None,
|
|
132
|
+
inheritable_metadata: dict[str, Any] | None,
|
|
133
|
+
run_id: UUID | None,
|
|
134
|
+
) -> None:
|
|
135
|
+
base_handlers: list[BaseCallbackHandler] = []
|
|
136
|
+
base_inheritable_handlers: list[BaseCallbackHandler] = []
|
|
137
|
+
if handlers is not None:
|
|
138
|
+
base_handlers.extend(handlers)
|
|
139
|
+
if inheritable_handlers is not None:
|
|
140
|
+
base_inheritable_handlers.extend(inheritable_handlers)
|
|
141
|
+
BaseCallbackManager.__init__(
|
|
142
|
+
manager,
|
|
143
|
+
handlers=base_handlers,
|
|
144
|
+
inheritable_handlers=base_inheritable_handlers,
|
|
145
|
+
parent_run_id=parent_run_id,
|
|
146
|
+
tags=tags,
|
|
147
|
+
inheritable_tags=inheritable_tags,
|
|
148
|
+
metadata=metadata,
|
|
149
|
+
inheritable_metadata=inheritable_metadata,
|
|
150
|
+
)
|
|
151
|
+
manager.run_id = run_id # type: ignore[attr-defined]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _configure_graph_callbacks(
|
|
155
|
+
cls: type[_GraphManagerT],
|
|
156
|
+
callbacks: object | None,
|
|
157
|
+
*,
|
|
158
|
+
run_id: UUID | None,
|
|
159
|
+
) -> _GraphManagerT:
|
|
160
|
+
if callbacks is None:
|
|
161
|
+
return cls(run_id=run_id)
|
|
162
|
+
if isinstance(callbacks, cls):
|
|
163
|
+
return callbacks.copy(run_id=run_id)
|
|
164
|
+
if isinstance(callbacks, (_GraphCallbackManager, _AsyncGraphCallbackManager)):
|
|
165
|
+
# Cross-type: extract handlers into the requested cls.
|
|
166
|
+
return cls(
|
|
167
|
+
handlers=_filter_graph_handlers(callbacks.handlers),
|
|
168
|
+
inheritable_handlers=_filter_graph_handlers(callbacks.inheritable_handlers),
|
|
169
|
+
parent_run_id=callbacks.parent_run_id,
|
|
170
|
+
tags=callbacks.tags.copy(),
|
|
171
|
+
inheritable_tags=callbacks.inheritable_tags.copy(),
|
|
172
|
+
metadata=callbacks.metadata.copy(),
|
|
173
|
+
inheritable_metadata=callbacks.inheritable_metadata.copy(),
|
|
174
|
+
run_id=run_id,
|
|
175
|
+
)
|
|
176
|
+
if isinstance(callbacks, BaseCallbackManager):
|
|
177
|
+
return cls(
|
|
178
|
+
handlers=_filter_graph_handlers(callbacks.handlers),
|
|
179
|
+
inheritable_handlers=_filter_graph_handlers(callbacks.inheritable_handlers),
|
|
180
|
+
parent_run_id=callbacks.parent_run_id,
|
|
181
|
+
tags=callbacks.tags.copy(),
|
|
182
|
+
inheritable_tags=callbacks.inheritable_tags.copy(),
|
|
183
|
+
metadata=callbacks.metadata.copy(),
|
|
184
|
+
inheritable_metadata=callbacks.inheritable_metadata.copy(),
|
|
185
|
+
run_id=run_id,
|
|
186
|
+
)
|
|
187
|
+
if isinstance(callbacks, GraphCallbackHandler):
|
|
188
|
+
return cls((callbacks,), run_id=run_id)
|
|
189
|
+
if isinstance(callbacks, (str, bytes)) or not isinstance(callbacks, Sequence):
|
|
190
|
+
raise TypeError("callbacks must be a handler, sequence, or manager")
|
|
191
|
+
return cls(_filter_graph_handlers(list(callbacks)), run_id=run_id)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _copy_graph_manager(
|
|
195
|
+
manager: _GraphCallbackManager | _AsyncGraphCallbackManager,
|
|
196
|
+
cls: type[_GraphManagerT],
|
|
197
|
+
run_id: UUID | None | object,
|
|
198
|
+
) -> _GraphManagerT:
|
|
199
|
+
resolved_run_id: UUID | None
|
|
200
|
+
if run_id is _MISSING:
|
|
201
|
+
resolved_run_id = manager.run_id
|
|
202
|
+
else:
|
|
203
|
+
if run_id is not None and not isinstance(run_id, UUID):
|
|
204
|
+
raise TypeError("run_id must be a UUID or None")
|
|
205
|
+
resolved_run_id = run_id
|
|
206
|
+
|
|
207
|
+
return cls(
|
|
208
|
+
handlers=_filter_graph_handlers(manager.handlers),
|
|
209
|
+
inheritable_handlers=_filter_graph_handlers(manager.inheritable_handlers),
|
|
210
|
+
parent_run_id=manager.parent_run_id,
|
|
211
|
+
tags=manager.tags.copy(),
|
|
212
|
+
inheritable_tags=manager.inheritable_tags.copy(),
|
|
213
|
+
metadata=manager.metadata.copy(),
|
|
214
|
+
inheritable_metadata=manager.inheritable_metadata.copy(),
|
|
215
|
+
run_id=resolved_run_id,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class _GraphCallbackManager(BaseCallbackManager):
|
|
220
|
+
"""Sync dispatcher for graph lifecycle events."""
|
|
221
|
+
|
|
222
|
+
run_id: UUID | None
|
|
223
|
+
|
|
224
|
+
def __init__(
|
|
225
|
+
self,
|
|
226
|
+
handlers: Sequence[GraphCallbackHandler] | None = None,
|
|
227
|
+
inheritable_handlers: Sequence[GraphCallbackHandler] | None = None,
|
|
228
|
+
parent_run_id: UUID | None = None,
|
|
229
|
+
*,
|
|
230
|
+
tags: list[str] | None = None,
|
|
231
|
+
inheritable_tags: list[str] | None = None,
|
|
232
|
+
metadata: dict[str, Any] | None = None,
|
|
233
|
+
inheritable_metadata: dict[str, Any] | None = None,
|
|
234
|
+
run_id: UUID | None = None,
|
|
235
|
+
) -> None:
|
|
236
|
+
_init_base_manager(
|
|
237
|
+
self,
|
|
238
|
+
handlers,
|
|
239
|
+
inheritable_handlers,
|
|
240
|
+
parent_run_id,
|
|
241
|
+
tags=tags,
|
|
242
|
+
inheritable_tags=inheritable_tags,
|
|
243
|
+
metadata=metadata,
|
|
244
|
+
inheritable_metadata=inheritable_metadata,
|
|
245
|
+
run_id=run_id,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
def copy(
|
|
249
|
+
self,
|
|
250
|
+
*,
|
|
251
|
+
run_id: UUID | None | object = _MISSING,
|
|
252
|
+
) -> _GraphCallbackManager:
|
|
253
|
+
return _copy_graph_manager(self, _GraphCallbackManager, run_id)
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
def configure(
|
|
257
|
+
cls,
|
|
258
|
+
callbacks: object | None = None,
|
|
259
|
+
*,
|
|
260
|
+
run_id: UUID | None = None,
|
|
261
|
+
) -> _GraphCallbackManager:
|
|
262
|
+
return _configure_graph_callbacks(cls, callbacks, run_id=run_id)
|
|
263
|
+
|
|
264
|
+
def on_interrupt(self, event: GraphInterruptEvent) -> None:
|
|
265
|
+
handle_event(
|
|
266
|
+
self.handlers,
|
|
267
|
+
"on_interrupt",
|
|
268
|
+
None,
|
|
269
|
+
event,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def on_resume(self, event: GraphResumeEvent) -> None:
|
|
273
|
+
handle_event(
|
|
274
|
+
self.handlers,
|
|
275
|
+
"on_resume",
|
|
276
|
+
None,
|
|
277
|
+
event,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class _AsyncGraphCallbackManager(BaseCallbackManager):
|
|
282
|
+
"""Async dispatcher for graph lifecycle events."""
|
|
283
|
+
|
|
284
|
+
run_id: UUID | None
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def is_async(self) -> bool:
|
|
288
|
+
"""Return whether the manager is async."""
|
|
289
|
+
return True
|
|
290
|
+
|
|
291
|
+
def __init__(
|
|
292
|
+
self,
|
|
293
|
+
handlers: Sequence[GraphCallbackHandler] | None = None,
|
|
294
|
+
inheritable_handlers: Sequence[GraphCallbackHandler] | None = None,
|
|
295
|
+
parent_run_id: UUID | None = None,
|
|
296
|
+
*,
|
|
297
|
+
tags: list[str] | None = None,
|
|
298
|
+
inheritable_tags: list[str] | None = None,
|
|
299
|
+
metadata: dict[str, Any] | None = None,
|
|
300
|
+
inheritable_metadata: dict[str, Any] | None = None,
|
|
301
|
+
run_id: UUID | None = None,
|
|
302
|
+
) -> None:
|
|
303
|
+
_init_base_manager(
|
|
304
|
+
self,
|
|
305
|
+
handlers,
|
|
306
|
+
inheritable_handlers,
|
|
307
|
+
parent_run_id,
|
|
308
|
+
tags=tags,
|
|
309
|
+
inheritable_tags=inheritable_tags,
|
|
310
|
+
metadata=metadata,
|
|
311
|
+
inheritable_metadata=inheritable_metadata,
|
|
312
|
+
run_id=run_id,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def copy(
|
|
316
|
+
self,
|
|
317
|
+
*,
|
|
318
|
+
run_id: UUID | None | object = _MISSING,
|
|
319
|
+
) -> _AsyncGraphCallbackManager:
|
|
320
|
+
return _copy_graph_manager(self, _AsyncGraphCallbackManager, run_id)
|
|
321
|
+
|
|
322
|
+
@classmethod
|
|
323
|
+
def configure(
|
|
324
|
+
cls,
|
|
325
|
+
callbacks: object | None = None,
|
|
326
|
+
*,
|
|
327
|
+
run_id: UUID | None = None,
|
|
328
|
+
) -> _AsyncGraphCallbackManager:
|
|
329
|
+
return _configure_graph_callbacks(cls, callbacks, run_id=run_id)
|
|
330
|
+
|
|
331
|
+
async def on_interrupt(self, event: GraphInterruptEvent) -> None:
|
|
332
|
+
await ahandle_event(
|
|
333
|
+
self.handlers,
|
|
334
|
+
"on_interrupt",
|
|
335
|
+
None,
|
|
336
|
+
event,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
async def on_resume(self, event: GraphResumeEvent) -> None:
|
|
340
|
+
await ahandle_event(
|
|
341
|
+
self.handlers,
|
|
342
|
+
"on_resume",
|
|
343
|
+
None,
|
|
344
|
+
event,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
_GraphManagerT = TypeVar(
|
|
349
|
+
"_GraphManagerT", _GraphCallbackManager, _AsyncGraphCallbackManager
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
GraphCallbacks: TypeAlias = (
|
|
353
|
+
_GraphCallbackManager
|
|
354
|
+
| _AsyncGraphCallbackManager
|
|
355
|
+
| BaseCallbackManager
|
|
356
|
+
| GraphCallbackHandler
|
|
357
|
+
| Sequence[BaseCallbackHandler]
|
|
358
|
+
| Sequence[GraphCallbackHandler]
|
|
359
|
+
| None
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def get_sync_graph_callback_manager_for_config(
|
|
364
|
+
config: RunnableConfig,
|
|
365
|
+
*,
|
|
366
|
+
run_id: UUID | None = None,
|
|
367
|
+
) -> _GraphCallbackManager:
|
|
368
|
+
"""Build a sync graph lifecycle callback manager from a runnable config.
|
|
369
|
+
|
|
370
|
+
This helper filters `config["callbacks"]` down to handlers that inherit
|
|
371
|
+
from `GraphCallbackHandler` and binds the provided `run_id` onto the
|
|
372
|
+
returned manager.
|
|
373
|
+
"""
|
|
374
|
+
return _GraphCallbackManager.configure(
|
|
375
|
+
config.get("callbacks"),
|
|
376
|
+
run_id=run_id,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def get_async_graph_callback_manager_for_config(
|
|
381
|
+
config: RunnableConfig,
|
|
382
|
+
*,
|
|
383
|
+
run_id: UUID | None = None,
|
|
384
|
+
) -> _AsyncGraphCallbackManager:
|
|
385
|
+
"""Build an async graph lifecycle callback manager from a runnable config.
|
|
386
|
+
|
|
387
|
+
This helper filters `config["callbacks"]` down to handlers that inherit
|
|
388
|
+
from `GraphCallbackHandler` and binds the provided `run_id` onto the
|
|
389
|
+
returned manager.
|
|
390
|
+
"""
|
|
391
|
+
return _AsyncGraphCallbackManager.configure(
|
|
392
|
+
config.get("callbacks"),
|
|
393
|
+
run_id=run_id,
|
|
394
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from coco_graph.channels.any_value import AnyValue
|
|
2
|
+
from coco_graph.channels.base import BaseChannel
|
|
3
|
+
from coco_graph.channels.binop import BinaryOperatorAggregate
|
|
4
|
+
from coco_graph.channels.delta import DeltaChannel
|
|
5
|
+
from coco_graph.channels.ephemeral_value import EphemeralValue
|
|
6
|
+
from coco_graph.channels.last_value import LastValue, LastValueAfterFinish
|
|
7
|
+
from coco_graph.channels.named_barrier_value import (
|
|
8
|
+
NamedBarrierValue,
|
|
9
|
+
NamedBarrierValueAfterFinish,
|
|
10
|
+
)
|
|
11
|
+
from coco_graph.channels.topic import Topic
|
|
12
|
+
from coco_graph.channels.untracked_value import UntrackedValue
|
|
13
|
+
|
|
14
|
+
__all__ = (
|
|
15
|
+
# base
|
|
16
|
+
"BaseChannel",
|
|
17
|
+
# value types
|
|
18
|
+
"AnyValue",
|
|
19
|
+
"LastValue",
|
|
20
|
+
"LastValueAfterFinish",
|
|
21
|
+
"UntrackedValue",
|
|
22
|
+
"EphemeralValue",
|
|
23
|
+
"BinaryOperatorAggregate",
|
|
24
|
+
"DeltaChannel",
|
|
25
|
+
"NamedBarrierValue",
|
|
26
|
+
"NamedBarrierValueAfterFinish",
|
|
27
|
+
# topics
|
|
28
|
+
"Topic",
|
|
29
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import Any, Generic
|
|
5
|
+
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
|
|
8
|
+
from coco_graph._internal._typing import MISSING
|
|
9
|
+
from coco_graph.channels.base import BaseChannel, Value
|
|
10
|
+
from coco_graph.errors import EmptyChannelError
|
|
11
|
+
|
|
12
|
+
__all__ = ("AnyValue",)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AnyValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
|
16
|
+
"""Stores the last value received, assumes that if multiple values are
|
|
17
|
+
received, they are all equal."""
|
|
18
|
+
|
|
19
|
+
__slots__ = ("typ", "value")
|
|
20
|
+
|
|
21
|
+
value: Value | Any
|
|
22
|
+
|
|
23
|
+
def __init__(self, typ: Any, key: str = "") -> None:
|
|
24
|
+
super().__init__(typ, key)
|
|
25
|
+
self.value = MISSING
|
|
26
|
+
|
|
27
|
+
def __eq__(self, value: object) -> bool:
|
|
28
|
+
return isinstance(value, AnyValue)
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def ValueType(self) -> type[Value]:
|
|
32
|
+
"""The type of the value stored in the channel."""
|
|
33
|
+
return self.typ
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def UpdateType(self) -> type[Value]:
|
|
37
|
+
"""The type of the update received by the channel."""
|
|
38
|
+
return self.typ
|
|
39
|
+
|
|
40
|
+
def copy(self) -> Self:
|
|
41
|
+
"""Return a copy of the channel."""
|
|
42
|
+
empty = self.__class__(self.typ, self.key)
|
|
43
|
+
empty.value = self.value
|
|
44
|
+
return empty
|
|
45
|
+
|
|
46
|
+
def from_checkpoint(self, checkpoint: Value) -> Self:
|
|
47
|
+
empty = self.__class__(self.typ, self.key)
|
|
48
|
+
if checkpoint is not MISSING:
|
|
49
|
+
empty.value = checkpoint
|
|
50
|
+
return empty
|
|
51
|
+
|
|
52
|
+
def update(self, values: Sequence[Value]) -> bool:
|
|
53
|
+
if len(values) == 0:
|
|
54
|
+
if self.value is MISSING:
|
|
55
|
+
return False
|
|
56
|
+
else:
|
|
57
|
+
self.value = MISSING
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
self.value = values[-1]
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
def get(self) -> Value:
|
|
64
|
+
if self.value is MISSING:
|
|
65
|
+
raise EmptyChannelError()
|
|
66
|
+
return self.value
|
|
67
|
+
|
|
68
|
+
def is_available(self) -> bool:
|
|
69
|
+
return self.value is not MISSING
|
|
70
|
+
|
|
71
|
+
def checkpoint(self) -> Value:
|
|
72
|
+
return self.value
|