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,172 @@
|
|
|
1
|
+
"""Shared helpers for `get_delta_channel_history` on sqlite savers.
|
|
2
|
+
|
|
3
|
+
Mirrors the two-stage shape of `BasePostgresSaver` (ancestor walk +
|
|
4
|
+
per-channel UNION ALL writes fetch), but adapted for sqlite's
|
|
5
|
+
constraints. The structural differences:
|
|
6
|
+
|
|
7
|
+
* No JSONB — to inspect `channel_values` for a checkpoint we must
|
|
8
|
+
deserialize the full blob. Stage 1 streams the cursor row-by-row and
|
|
9
|
+
deserializes only the rows the merged walk visits, freeing each blob
|
|
10
|
+
before advancing.
|
|
11
|
+
* No separate blob table — `channel_values` lives inline in the
|
|
12
|
+
checkpoint, so seeds come back from stage 1 with no second fetch.
|
|
13
|
+
* Single merged walk (not K independent walks): each visited cid is
|
|
14
|
+
deserialized exactly once, regardless of how many channels are still
|
|
15
|
+
seeking their seed.
|
|
16
|
+
|
|
17
|
+
The streaming design keeps peak in-flight memory at roughly one
|
|
18
|
+
deserialized checkpoint at a time, instead of holding the entire
|
|
19
|
+
ancestor chain's worth of raw blobs as a `fetchall()`-materialized list.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from collections.abc import Mapping, Sequence
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from coco_graph.checkpoint.base import DeltaChannelHistory, PendingWrite
|
|
28
|
+
|
|
29
|
+
# Stage 1 streams ancestors of `target_cid` newest-first. The `<=`
|
|
30
|
+
# predicate keeps target itself in the stream so we can read its
|
|
31
|
+
# `parent_checkpoint_id` from the first row without a separate lookup;
|
|
32
|
+
# the caller skips target's own writes/seed (matches the
|
|
33
|
+
# `BaseCheckpointSaver` contract).
|
|
34
|
+
DELTA_STAGE1_SQL = (
|
|
35
|
+
"SELECT checkpoint_id, parent_checkpoint_id, type, checkpoint "
|
|
36
|
+
"FROM checkpoints "
|
|
37
|
+
"WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id <= ? "
|
|
38
|
+
"ORDER BY checkpoint_id DESC"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def build_delta_stage2_sql(*, chain_lens: Sequence[int]) -> str:
|
|
43
|
+
"""Stage-2 per-channel UNION ALL fetching writes from `writes`.
|
|
44
|
+
|
|
45
|
+
One branch per channel with a non-empty chain. Each branch inlines its
|
|
46
|
+
own `IN (?, ?, ...)` placeholder list because sqlite has no array-bind
|
|
47
|
+
equivalent of postgres's `= ANY(%s)`. Caller passes parameters in
|
|
48
|
+
matching order: `[thread_id, checkpoint_ns, channel, *chain_cids]` per
|
|
49
|
+
branch.
|
|
50
|
+
|
|
51
|
+
Returns an empty string when no channel has a chain (caller skips
|
|
52
|
+
executing in that case). Per-channel UNION ALL avoids the over-fetch
|
|
53
|
+
of a single `channel = ANY(channels)` filter when channels have
|
|
54
|
+
different chain depths — same rationale as postgres.
|
|
55
|
+
"""
|
|
56
|
+
branches: list[str] = []
|
|
57
|
+
for n in chain_lens:
|
|
58
|
+
cid_placeholders = ",".join("?" * n)
|
|
59
|
+
branches.append(
|
|
60
|
+
"SELECT checkpoint_id, channel, task_id, idx, type, value "
|
|
61
|
+
"FROM writes "
|
|
62
|
+
"WHERE thread_id = ? AND checkpoint_ns = ? AND channel = ? "
|
|
63
|
+
f"AND checkpoint_id IN ({cid_placeholders})"
|
|
64
|
+
)
|
|
65
|
+
return " UNION ALL ".join(branches)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def step_walk_with_row(
|
|
69
|
+
*,
|
|
70
|
+
cid: str,
|
|
71
|
+
parent_cid: str | None,
|
|
72
|
+
type_tag: str,
|
|
73
|
+
blob: bytes,
|
|
74
|
+
target_id: str,
|
|
75
|
+
serde: Any,
|
|
76
|
+
chain_by_ch: dict[str, list[str]],
|
|
77
|
+
seed_val_by_ch: dict[str, Any],
|
|
78
|
+
walk_state: dict[str, Any],
|
|
79
|
+
seeded: set[str],
|
|
80
|
+
channels: Sequence[str],
|
|
81
|
+
) -> bool:
|
|
82
|
+
"""Process one streamed stage-1 row in the merged ancestor walk.
|
|
83
|
+
|
|
84
|
+
The cursor returns (cid, parent_cid, type, blob) rows in
|
|
85
|
+
`checkpoint_id` DESC order starting at target. The first row is
|
|
86
|
+
target itself; we read its parent_cid to seed the walk and otherwise
|
|
87
|
+
skip it (target's own writes/seed are not part of the contract).
|
|
88
|
+
|
|
89
|
+
For each subsequent row, if `cid` matches the walk's current
|
|
90
|
+
position, we deserialize the blob, append the cid to every
|
|
91
|
+
not-yet-seeded channel's chain, and check `channel_values` for
|
|
92
|
+
seeds. The deserialized checkpoint is dropped before advancing — no
|
|
93
|
+
cross-row cache, so peak in-flight is one deserialized checkpoint.
|
|
94
|
+
|
|
95
|
+
Off-path rows (different branch on the same thread) advance the
|
|
96
|
+
cursor without doing any work.
|
|
97
|
+
|
|
98
|
+
Returns True when every requested channel is seeded — the caller
|
|
99
|
+
can stop iterating and close the cursor.
|
|
100
|
+
"""
|
|
101
|
+
if "started" not in walk_state:
|
|
102
|
+
if cid == target_id:
|
|
103
|
+
walk_state["started"] = True
|
|
104
|
+
walk_state["cur_cid"] = parent_cid
|
|
105
|
+
walk_state["active"] = {ch for ch in channels if ch not in seeded}
|
|
106
|
+
# Not target yet (or target not present): keep streaming.
|
|
107
|
+
return False
|
|
108
|
+
active: set[str] = walk_state["active"]
|
|
109
|
+
if not active:
|
|
110
|
+
return True
|
|
111
|
+
if cid != walk_state["cur_cid"]:
|
|
112
|
+
# Off-path row from a sibling branch — skip without deserializing.
|
|
113
|
+
return False
|
|
114
|
+
for ch in active:
|
|
115
|
+
chain_by_ch[ch].append(cid)
|
|
116
|
+
ckpt = serde.loads_typed((type_tag, blob))
|
|
117
|
+
channel_values: Mapping[str, Any] = ckpt.get("channel_values") or {}
|
|
118
|
+
for ch in [ch for ch in active if ch in channel_values]:
|
|
119
|
+
seed_val_by_ch[ch] = channel_values[ch]
|
|
120
|
+
seeded.add(ch)
|
|
121
|
+
active.discard(ch)
|
|
122
|
+
del ckpt, channel_values
|
|
123
|
+
walk_state["cur_cid"] = parent_cid
|
|
124
|
+
return not active
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def build_delta_channels_writes_history(
|
|
128
|
+
*,
|
|
129
|
+
channels: Sequence[str],
|
|
130
|
+
chain_by_ch: Mapping[str, list[str]],
|
|
131
|
+
seed_val_by_ch: Mapping[str, Any],
|
|
132
|
+
seeded: set[str],
|
|
133
|
+
stage2_rows: Sequence[tuple[str, str, str, int, str, bytes]],
|
|
134
|
+
serde: Any,
|
|
135
|
+
) -> dict[str, DeltaChannelHistory]:
|
|
136
|
+
"""Demux stage-2 rows per channel; produce per-channel histories.
|
|
137
|
+
|
|
138
|
+
Stage-2 rows are `(checkpoint_id, channel, task_id, idx, type, value)`.
|
|
139
|
+
Final write order is oldest→newest globally and `(task_id, idx)` within
|
|
140
|
+
a checkpoint, matching the contract on `DeltaChannelHistory.writes`.
|
|
141
|
+
|
|
142
|
+
`seed` is omitted when the walk reached a true root with no snapshot
|
|
143
|
+
found (channel never entered `seeded`); consumers treat absence as
|
|
144
|
+
"start empty".
|
|
145
|
+
"""
|
|
146
|
+
writes_by_ch_by_cid: dict[str, dict[str, list[tuple[str, bytes, str, int]]]] = {
|
|
147
|
+
ch: {} for ch in channels
|
|
148
|
+
}
|
|
149
|
+
for cid, ch, task_id, idx, type_tag, value_blob in stage2_rows:
|
|
150
|
+
writes_by_ch_by_cid.setdefault(ch, {}).setdefault(cid, []).append(
|
|
151
|
+
(type_tag, value_blob, task_id, idx)
|
|
152
|
+
)
|
|
153
|
+
for cid_map in writes_by_ch_by_cid.values():
|
|
154
|
+
for ws in cid_map.values():
|
|
155
|
+
ws.sort(key=lambda w: (w[2], w[3]))
|
|
156
|
+
|
|
157
|
+
result: dict[str, DeltaChannelHistory] = {}
|
|
158
|
+
for ch in channels:
|
|
159
|
+
chain_cids = chain_by_ch.get(ch, [])
|
|
160
|
+
cid_writes = writes_by_ch_by_cid.get(ch, {})
|
|
161
|
+
collected: list[PendingWrite] = []
|
|
162
|
+
# Chain is newest-first; iterate oldest-first for the public order.
|
|
163
|
+
for cid in reversed(chain_cids):
|
|
164
|
+
for type_tag, value_blob, task_id, _idx in cid_writes.get(cid, []):
|
|
165
|
+
collected.append(
|
|
166
|
+
(task_id, ch, serde.loads_typed((type_tag, value_blob)))
|
|
167
|
+
)
|
|
168
|
+
entry: DeltaChannelHistory = {"writes": collected}
|
|
169
|
+
if ch in seeded:
|
|
170
|
+
entry["seed"] = seed_val_by_ch[ch]
|
|
171
|
+
result[ch] = entry
|
|
172
|
+
return result
|