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,151 @@
|
|
|
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 (
|
|
11
|
+
EmptyChannelError,
|
|
12
|
+
ErrorCode,
|
|
13
|
+
InvalidUpdateError,
|
|
14
|
+
create_error_message,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = ("LastValue", "LastValueAfterFinish")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LastValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
|
21
|
+
"""Stores the last value received, can receive at most one value per step."""
|
|
22
|
+
|
|
23
|
+
__slots__ = ("value",)
|
|
24
|
+
|
|
25
|
+
value: Value | Any
|
|
26
|
+
|
|
27
|
+
def __init__(self, typ: Any, key: str = "") -> None:
|
|
28
|
+
super().__init__(typ, key)
|
|
29
|
+
self.value = MISSING
|
|
30
|
+
|
|
31
|
+
def __eq__(self, value: object) -> bool:
|
|
32
|
+
return isinstance(value, LastValue)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def ValueType(self) -> type[Value]:
|
|
36
|
+
"""The type of the value stored in the channel."""
|
|
37
|
+
return self.typ
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def UpdateType(self) -> type[Value]:
|
|
41
|
+
"""The type of the update received by the channel."""
|
|
42
|
+
return self.typ
|
|
43
|
+
|
|
44
|
+
def copy(self) -> Self:
|
|
45
|
+
"""Return a copy of the channel."""
|
|
46
|
+
empty = self.__class__(self.typ, self.key)
|
|
47
|
+
empty.value = self.value
|
|
48
|
+
return empty
|
|
49
|
+
|
|
50
|
+
def from_checkpoint(self, checkpoint: Value) -> Self:
|
|
51
|
+
empty = self.__class__(self.typ, self.key)
|
|
52
|
+
if checkpoint is not MISSING:
|
|
53
|
+
empty.value = checkpoint
|
|
54
|
+
return empty
|
|
55
|
+
|
|
56
|
+
def update(self, values: Sequence[Value]) -> bool:
|
|
57
|
+
if len(values) == 0:
|
|
58
|
+
return False
|
|
59
|
+
if len(values) != 1:
|
|
60
|
+
msg = create_error_message(
|
|
61
|
+
message=f"At key '{self.key}': Can receive only one value per step. Use an Annotated key to handle multiple values.",
|
|
62
|
+
error_code=ErrorCode.INVALID_CONCURRENT_GRAPH_UPDATE,
|
|
63
|
+
)
|
|
64
|
+
raise InvalidUpdateError(msg)
|
|
65
|
+
|
|
66
|
+
self.value = values[-1]
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
def get(self) -> Value:
|
|
70
|
+
if self.value is MISSING:
|
|
71
|
+
raise EmptyChannelError()
|
|
72
|
+
return self.value
|
|
73
|
+
|
|
74
|
+
def is_available(self) -> bool:
|
|
75
|
+
return self.value is not MISSING
|
|
76
|
+
|
|
77
|
+
def checkpoint(self) -> Value:
|
|
78
|
+
return self.value
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class LastValueAfterFinish(
|
|
82
|
+
Generic[Value], BaseChannel[Value, Value, tuple[Value, bool]]
|
|
83
|
+
):
|
|
84
|
+
"""Stores the last value received, but only made available after finish().
|
|
85
|
+
Once made available, clears the value."""
|
|
86
|
+
|
|
87
|
+
__slots__ = ("value", "finished")
|
|
88
|
+
|
|
89
|
+
value: Value | Any
|
|
90
|
+
finished: bool
|
|
91
|
+
|
|
92
|
+
def __init__(self, typ: Any, key: str = "") -> None:
|
|
93
|
+
super().__init__(typ, key)
|
|
94
|
+
self.value = MISSING
|
|
95
|
+
self.finished = False
|
|
96
|
+
|
|
97
|
+
def __eq__(self, value: object) -> bool:
|
|
98
|
+
return isinstance(value, LastValueAfterFinish)
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def ValueType(self) -> type[Value]:
|
|
102
|
+
"""The type of the value stored in the channel."""
|
|
103
|
+
return self.typ
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def UpdateType(self) -> type[Value]:
|
|
107
|
+
"""The type of the update received by the channel."""
|
|
108
|
+
return self.typ
|
|
109
|
+
|
|
110
|
+
def checkpoint(self) -> tuple[Value | Any, bool] | Any:
|
|
111
|
+
if self.value is MISSING:
|
|
112
|
+
return MISSING
|
|
113
|
+
return (self.value, self.finished)
|
|
114
|
+
|
|
115
|
+
def from_checkpoint(self, checkpoint: tuple[Value | Any, bool] | Any) -> Self:
|
|
116
|
+
empty = self.__class__(self.typ)
|
|
117
|
+
empty.key = self.key
|
|
118
|
+
if checkpoint is not MISSING:
|
|
119
|
+
empty.value, empty.finished = checkpoint
|
|
120
|
+
return empty
|
|
121
|
+
|
|
122
|
+
def update(self, values: Sequence[Value | Any]) -> bool:
|
|
123
|
+
if len(values) == 0:
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
self.finished = False
|
|
127
|
+
self.value = values[-1]
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
def consume(self) -> bool:
|
|
131
|
+
if self.finished:
|
|
132
|
+
self.finished = False
|
|
133
|
+
self.value = MISSING
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
def finish(self) -> bool:
|
|
139
|
+
if not self.finished and self.value is not MISSING:
|
|
140
|
+
self.finished = True
|
|
141
|
+
return True
|
|
142
|
+
else:
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
def get(self) -> Value:
|
|
146
|
+
if self.value is MISSING or not self.finished:
|
|
147
|
+
raise EmptyChannelError()
|
|
148
|
+
return self.value
|
|
149
|
+
|
|
150
|
+
def is_available(self) -> bool:
|
|
151
|
+
return self.value is not MISSING and self.finished
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import Generic
|
|
3
|
+
|
|
4
|
+
from typing_extensions import Self
|
|
5
|
+
|
|
6
|
+
from coco_graph._internal._typing import MISSING
|
|
7
|
+
from coco_graph.channels.base import BaseChannel, Value
|
|
8
|
+
from coco_graph.errors import EmptyChannelError, InvalidUpdateError
|
|
9
|
+
|
|
10
|
+
__all__ = ("NamedBarrierValue", "NamedBarrierValueAfterFinish")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NamedBarrierValue(Generic[Value], BaseChannel[Value, Value, set[Value]]):
|
|
14
|
+
"""A channel that waits until all named values are received before making the value available."""
|
|
15
|
+
|
|
16
|
+
__slots__ = ("names", "seen")
|
|
17
|
+
|
|
18
|
+
names: set[Value]
|
|
19
|
+
seen: set[Value]
|
|
20
|
+
|
|
21
|
+
def __init__(self, typ: type[Value], names: set[Value]) -> None:
|
|
22
|
+
super().__init__(typ)
|
|
23
|
+
self.names = names
|
|
24
|
+
self.seen: set[str] = set()
|
|
25
|
+
|
|
26
|
+
def __eq__(self, value: object) -> bool:
|
|
27
|
+
return isinstance(value, NamedBarrierValue) and value.names == self.names
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def ValueType(self) -> type[Value]:
|
|
31
|
+
"""The type of the value stored in the channel."""
|
|
32
|
+
return self.typ
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def UpdateType(self) -> type[Value]:
|
|
36
|
+
"""The type of the update received by the channel."""
|
|
37
|
+
return self.typ
|
|
38
|
+
|
|
39
|
+
def copy(self) -> Self:
|
|
40
|
+
"""Return a copy of the channel."""
|
|
41
|
+
empty = self.__class__(self.typ, self.names)
|
|
42
|
+
empty.key = self.key
|
|
43
|
+
empty.seen = self.seen.copy()
|
|
44
|
+
return empty
|
|
45
|
+
|
|
46
|
+
def checkpoint(self) -> set[Value]:
|
|
47
|
+
return self.seen
|
|
48
|
+
|
|
49
|
+
def from_checkpoint(self, checkpoint: set[Value]) -> Self:
|
|
50
|
+
empty = self.__class__(self.typ, self.names)
|
|
51
|
+
empty.key = self.key
|
|
52
|
+
if checkpoint is not MISSING:
|
|
53
|
+
empty.seen = checkpoint
|
|
54
|
+
return empty
|
|
55
|
+
|
|
56
|
+
def update(self, values: Sequence[Value]) -> bool:
|
|
57
|
+
updated = False
|
|
58
|
+
for value in values:
|
|
59
|
+
if value in self.names:
|
|
60
|
+
if value not in self.seen:
|
|
61
|
+
self.seen.add(value)
|
|
62
|
+
updated = True
|
|
63
|
+
else:
|
|
64
|
+
raise InvalidUpdateError(
|
|
65
|
+
f"At key '{self.key}': Value {value} not in {self.names}"
|
|
66
|
+
)
|
|
67
|
+
return updated
|
|
68
|
+
|
|
69
|
+
def get(self) -> Value:
|
|
70
|
+
if self.seen != self.names:
|
|
71
|
+
raise EmptyChannelError()
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def is_available(self) -> bool:
|
|
75
|
+
return self.seen == self.names
|
|
76
|
+
|
|
77
|
+
def consume(self) -> bool:
|
|
78
|
+
if self.seen == self.names:
|
|
79
|
+
self.seen = set()
|
|
80
|
+
return True
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class NamedBarrierValueAfterFinish(
|
|
85
|
+
Generic[Value], BaseChannel[Value, Value, set[Value]]
|
|
86
|
+
):
|
|
87
|
+
"""A channel that waits until all named values are received before making the value ready to be made available. It is only made available after finish() is called."""
|
|
88
|
+
|
|
89
|
+
__slots__ = ("names", "seen", "finished")
|
|
90
|
+
|
|
91
|
+
names: set[Value]
|
|
92
|
+
seen: set[Value]
|
|
93
|
+
|
|
94
|
+
def __init__(self, typ: type[Value], names: set[Value]) -> None:
|
|
95
|
+
super().__init__(typ)
|
|
96
|
+
self.names = names
|
|
97
|
+
self.seen: set[str] = set()
|
|
98
|
+
self.finished = False
|
|
99
|
+
|
|
100
|
+
def __eq__(self, value: object) -> bool:
|
|
101
|
+
return (
|
|
102
|
+
isinstance(value, NamedBarrierValueAfterFinish)
|
|
103
|
+
and value.names == self.names
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def ValueType(self) -> type[Value]:
|
|
108
|
+
"""The type of the value stored in the channel."""
|
|
109
|
+
return self.typ
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def UpdateType(self) -> type[Value]:
|
|
113
|
+
"""The type of the update received by the channel."""
|
|
114
|
+
return self.typ
|
|
115
|
+
|
|
116
|
+
def copy(self) -> Self:
|
|
117
|
+
"""Return a copy of the channel."""
|
|
118
|
+
empty = self.__class__(self.typ, self.names)
|
|
119
|
+
empty.key = self.key
|
|
120
|
+
empty.seen = self.seen.copy()
|
|
121
|
+
empty.finished = self.finished
|
|
122
|
+
return empty
|
|
123
|
+
|
|
124
|
+
def checkpoint(self) -> tuple[set[Value], bool]:
|
|
125
|
+
return (self.seen, self.finished)
|
|
126
|
+
|
|
127
|
+
def from_checkpoint(self, checkpoint: tuple[set[Value], bool]) -> Self:
|
|
128
|
+
empty = self.__class__(self.typ, self.names)
|
|
129
|
+
empty.key = self.key
|
|
130
|
+
if checkpoint is not MISSING:
|
|
131
|
+
empty.seen, empty.finished = checkpoint
|
|
132
|
+
return empty
|
|
133
|
+
|
|
134
|
+
def update(self, values: Sequence[Value]) -> bool:
|
|
135
|
+
updated = False
|
|
136
|
+
for value in values:
|
|
137
|
+
if value in self.names:
|
|
138
|
+
if value not in self.seen:
|
|
139
|
+
self.seen.add(value)
|
|
140
|
+
updated = True
|
|
141
|
+
else:
|
|
142
|
+
raise InvalidUpdateError(
|
|
143
|
+
f"At key '{self.key}': Value {value} not in {self.names}"
|
|
144
|
+
)
|
|
145
|
+
return updated
|
|
146
|
+
|
|
147
|
+
def get(self) -> Value:
|
|
148
|
+
if not self.finished or self.seen != self.names:
|
|
149
|
+
raise EmptyChannelError()
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
def is_available(self) -> bool:
|
|
153
|
+
return self.finished and self.seen == self.names
|
|
154
|
+
|
|
155
|
+
def consume(self) -> bool:
|
|
156
|
+
if self.finished and self.seen == self.names:
|
|
157
|
+
self.finished = False
|
|
158
|
+
self.seen = set()
|
|
159
|
+
return True
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
def finish(self) -> bool:
|
|
163
|
+
if not self.finished and self.seen == self.names:
|
|
164
|
+
self.finished = True
|
|
165
|
+
return True
|
|
166
|
+
else:
|
|
167
|
+
return False
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterator, 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__ = ("Topic",)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _flatten(values: Sequence[Value | list[Value]]) -> Iterator[Value]:
|
|
16
|
+
for value in values:
|
|
17
|
+
if isinstance(value, list):
|
|
18
|
+
yield from value
|
|
19
|
+
else:
|
|
20
|
+
yield value
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Topic(
|
|
24
|
+
Generic[Value],
|
|
25
|
+
BaseChannel[Sequence[Value], Value | list[Value], list[Value]],
|
|
26
|
+
):
|
|
27
|
+
"""A configurable PubSub Topic.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
typ: The type of the value stored in the channel.
|
|
31
|
+
accumulate: Whether to accumulate values across steps. If `False`, the channel will be emptied after each step.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
__slots__ = ("values", "accumulate")
|
|
35
|
+
|
|
36
|
+
def __init__(self, typ: type[Value], accumulate: bool = False) -> None:
|
|
37
|
+
super().__init__(typ)
|
|
38
|
+
# attrs
|
|
39
|
+
self.accumulate = accumulate
|
|
40
|
+
# state
|
|
41
|
+
self.values = list[Value]()
|
|
42
|
+
|
|
43
|
+
def __eq__(self, value: object) -> bool:
|
|
44
|
+
return isinstance(value, Topic) and value.accumulate == self.accumulate
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def ValueType(self) -> Any:
|
|
48
|
+
"""The type of the value stored in the channel."""
|
|
49
|
+
return Sequence[self.typ] # type: ignore[name-defined]
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def UpdateType(self) -> Any:
|
|
53
|
+
"""The type of the update received by the channel."""
|
|
54
|
+
return self.typ | list[self.typ] # type: ignore[name-defined]
|
|
55
|
+
|
|
56
|
+
def copy(self) -> Self:
|
|
57
|
+
"""Return a copy of the channel."""
|
|
58
|
+
empty = self.__class__(self.typ, self.accumulate)
|
|
59
|
+
empty.key = self.key
|
|
60
|
+
empty.values = self.values.copy()
|
|
61
|
+
return empty
|
|
62
|
+
|
|
63
|
+
def checkpoint(self) -> list[Value]:
|
|
64
|
+
return self.values
|
|
65
|
+
|
|
66
|
+
def from_checkpoint(self, checkpoint: list[Value]) -> Self:
|
|
67
|
+
empty = self.__class__(self.typ, self.accumulate)
|
|
68
|
+
empty.key = self.key
|
|
69
|
+
if checkpoint is not MISSING:
|
|
70
|
+
if isinstance(checkpoint, tuple):
|
|
71
|
+
# backwards compatibility
|
|
72
|
+
empty.values = checkpoint[1]
|
|
73
|
+
else:
|
|
74
|
+
empty.values = checkpoint
|
|
75
|
+
return empty
|
|
76
|
+
|
|
77
|
+
def update(self, values: Sequence[Value | list[Value]]) -> bool:
|
|
78
|
+
updated = False
|
|
79
|
+
if not self.accumulate:
|
|
80
|
+
updated = bool(self.values)
|
|
81
|
+
self.values = list[Value]()
|
|
82
|
+
if flat_values := tuple(_flatten(values)):
|
|
83
|
+
updated = True
|
|
84
|
+
self.values.extend(flat_values)
|
|
85
|
+
return updated
|
|
86
|
+
|
|
87
|
+
def get(self) -> Sequence[Value]:
|
|
88
|
+
if self.values:
|
|
89
|
+
return list(self.values)
|
|
90
|
+
else:
|
|
91
|
+
raise EmptyChannelError
|
|
92
|
+
|
|
93
|
+
def is_available(self) -> bool:
|
|
94
|
+
return bool(self.values)
|
|
@@ -0,0 +1,73 @@
|
|
|
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, InvalidUpdateError
|
|
11
|
+
|
|
12
|
+
__all__ = ("UntrackedValue",)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class UntrackedValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
|
16
|
+
"""Stores the last value received, never checkpointed."""
|
|
17
|
+
|
|
18
|
+
__slots__ = ("value", "guard")
|
|
19
|
+
|
|
20
|
+
guard: bool
|
|
21
|
+
value: Value | Any
|
|
22
|
+
|
|
23
|
+
def __init__(self, typ: type[Value], guard: bool = True) -> None:
|
|
24
|
+
super().__init__(typ)
|
|
25
|
+
self.guard = guard
|
|
26
|
+
self.value = MISSING
|
|
27
|
+
|
|
28
|
+
def __eq__(self, value: object) -> bool:
|
|
29
|
+
return isinstance(value, UntrackedValue) and value.guard == self.guard
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def ValueType(self) -> type[Value]:
|
|
33
|
+
"""The type of the value stored in the channel."""
|
|
34
|
+
return self.typ
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def UpdateType(self) -> type[Value]:
|
|
38
|
+
"""The type of the update received by the channel."""
|
|
39
|
+
return self.typ
|
|
40
|
+
|
|
41
|
+
def copy(self) -> Self:
|
|
42
|
+
"""Return a copy of the channel."""
|
|
43
|
+
empty = self.__class__(self.typ, self.guard)
|
|
44
|
+
empty.key = self.key
|
|
45
|
+
empty.value = self.value
|
|
46
|
+
return empty
|
|
47
|
+
|
|
48
|
+
def checkpoint(self) -> Value | Any:
|
|
49
|
+
return MISSING
|
|
50
|
+
|
|
51
|
+
def from_checkpoint(self, checkpoint: Value) -> Self:
|
|
52
|
+
empty = self.__class__(self.typ, self.guard)
|
|
53
|
+
empty.key = self.key
|
|
54
|
+
return empty
|
|
55
|
+
|
|
56
|
+
def update(self, values: Sequence[Value]) -> bool:
|
|
57
|
+
if len(values) == 0:
|
|
58
|
+
return False
|
|
59
|
+
if len(values) != 1 and self.guard:
|
|
60
|
+
raise InvalidUpdateError(
|
|
61
|
+
f"At key '{self.key}': UntrackedValue(guard=True) can receive only one value per step. Use guard=False if you want to store any one of multiple values."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
self.value = values[-1]
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
def get(self) -> Value:
|
|
68
|
+
if self.value is MISSING:
|
|
69
|
+
raise EmptyChannelError()
|
|
70
|
+
return self.value
|
|
71
|
+
|
|
72
|
+
def is_available(self) -> bool:
|
|
73
|
+
return self.value is not MISSING
|