Source code for continuum.durable
"""Public durable-agent API.
:class:`DurableAgent` checkpoints mid-run (value map + portable KV state),
resumes on a fresh process, and forks alternate timelines by editing a
computed node. The engine lives in the compiled extension; this subclass adds
checkpointing straight to a :class:`~continuum.checkpoints.CheckpointStore`
(local directory, S3, GCS) so a run can resume on another machine.
"""
from __future__ import annotations
from typing import Any
from continuum._native import DurableAgent as _NativeDurableAgent
from continuum.checkpoints import CheckpointStore
from continuum.telemetry import auto_instrument
__all__ = ["DurableAgent"]
[docs]
class DurableAgent(_NativeDurableAgent):
"""Step-sequenced agent run that can be checkpointed, resumed, and forked.
With ``CONTINUUM_OTEL=1`` in the environment, each agent exports its
reuse events through OpenTelemetry (see :mod:`continuum.telemetry`).
"""
def __init__(self) -> None:
super().__init__()
auto_instrument(self)
[docs]
def run_until_step(
self, step_index: int, store: CheckpointStore | None = None, key: str | None = None
) -> bytes:
"""Run through ``step_index`` and return the checkpoint bytes.
With ``store`` and ``key``, the checkpoint is also written there.
"""
if (store is None) != (key is None):
raise ValueError("pass both store and key, or neither")
blob = super().run_until_step(step_index)
if store is not None and key is not None:
store.put(key, blob)
return blob
[docs]
def resume_from(
self,
checkpoint: bytes | None = None,
*,
store: CheckpointStore | None = None,
key: str | None = None,
) -> list[Any]:
"""Resume from checkpoint bytes, or read them from ``store[key]``."""
if checkpoint is None:
if store is None or key is None:
raise ValueError("pass checkpoint bytes, or both store and key")
checkpoint = store.get(key)
elif store is not None or key is not None:
raise ValueError("pass checkpoint bytes or store/key, not both")
return super().resume_from(checkpoint)