simbiot-harness/venv/lib/python3.12/site-packages/strands/_context_manager/stash.py
Simbiot Harness b60439152b Simbiot Harness v0.1: тактовая лестница на Strands + CQRS/ES-спина
- harness/es.py: append-only event store + проекции (образец — кооп-портал)
- harness/levels.py: уровни 1с/1м/1ч/1д, контекст ∝ масштабу
- harness/scheduler.py: уровневые потоки, идемпотентные тики, absorbed-стабы
- harness/isolated_runner.py + _llm_worker.py: LLM-тик в сабпроцессе
  (глобальный asyncio-рантайм Strands не переживает 2+ create_harness)
- cli.py: init/task/tasks/status/events + requeue (написан самим минутным агентом)
- 9router: отдельный ключ sk-simbiot, модель auto (glm-5.3 первым)
2026-09-29 15:28:50 +00:00

173 lines
6.4 KiB
Python

"""L1 stash — durable storage for offloaded context content.
When the ContextManager offloads content, the original is persisted here
so the agent can retrieve it on demand via the retrieval tool.
"""
from __future__ import annotations
import base64
import json
import logging
from typing import TYPE_CHECKING, Any
from ..storage.storage import _NamespacedStorage
from ..types.content import ContentBlock
if TYPE_CHECKING:
from ..storage.storage import Storage
from ..types.content import Message
logger = logging.getLogger(__name__)
STASH_PREFIX = "context"
class _BytesEncoder(json.JSONEncoder):
"""JSON encoder that base64-encodes bytes values."""
def default(self, obj: object) -> object:
if isinstance(obj, (bytes, bytearray)):
return base64.b64encode(obj).decode("ascii")
return super().default(obj)
def _encode(value: object) -> bytes:
return json.dumps(value, cls=_BytesEncoder).encode("utf-8")
def _decode(data: bytes) -> object:
return json.loads(data.decode("utf-8"))
def _format_stash_refs(refs: list[str]) -> str:
"""Format stash refs as a standalone bracket token for offload placeholders.
Returns an empty string when refs is empty. With one ref returns
``' [ref: <ref>]'``; with multiple returns ``' [refs: <r1>, <r2>]'``.
Note the leading space in non-empty returns.
"""
if not refs:
return ""
if len(refs) == 1:
return f" [ref: {refs[0]}]"
return f" [refs: {', '.join(refs)}]"
class Stash:
"""Namespaced storage wrapper for persisting offloaded content blocks."""
def __init__(self, storage: Storage, session_id: str, agent_id: str) -> None:
self._base_storage = storage
self._session_id = session_id
self._storage = _NamespacedStorage(storage, f"{STASH_PREFIX}/{session_id}/scopes/agent/{agent_id}")
@property
def storage_type_name(self) -> str:
"""Name of the base storage class, for diagnostic logging."""
return type(self._base_storage).__name__
async def store(self, block_id: str, block_index: int, data: bytes) -> str:
"""Store a content block and return its deterministic reference key."""
key = f"{block_id}_{block_index}"
await self._storage.write(key, data)
return key
def refs_for(self, block: ContentBlock, message: Message, block_index: int) -> list[str]:
"""Compute deterministic reference keys for a content block."""
if "toolResult" in block:
tool_result = block["toolResult"]
return [f"{tool_result['toolUseId']}_{index}" for index in range(len(tool_result["content"]))]
return [f"{message.get('tracking_id', 'unknown')}_{block_index}"]
async def store_message(self, message: Message, skip_tool_use_ids: frozenset[str] | None = None) -> None:
"""Eagerly persist all stashable blocks from a message."""
for block_index, block in enumerate(message["content"]):
if "toolResult" in block:
tool_result = block["toolResult"]
if skip_tool_use_ids and tool_result["toolUseId"] in skip_tool_use_ids:
continue
await self._store_tool_result(block)
elif "toolUse" in block or "reasoningContent" in block or "cachePoint" in block:
continue
else:
try:
await self.store(message.get("tracking_id", "unknown"), block_index, _encode(block))
except Exception:
logger.warning(
"tracking_id=<%s>, block_index=<%s> | failed to stash block",
message.get("tracking_id"),
block_index,
exc_info=True,
)
async def retrieve(self, reference: str) -> object | None:
"""Retrieve a stashed block by reference. Returns None if not found."""
data = await self._storage.read(reference)
if data is None:
return None
return _decode(data)
async def list(self) -> list[str]:
"""List all stashed reference keys."""
return await self._storage.list("")
async def delete(self, reference: str) -> None:
"""Delete a stashed entry."""
await self._storage.delete(reference)
logger.debug("reference=<%s> | deleted stash entry", reference)
async def take_snapshot(self) -> dict[str, Any]:
"""Serialize all stash entries for snapshot persistence.
Returns:
Map of reference keys to their deserialized JSON values.
"""
keys = await self.list()
entries: dict[str, Any] = {}
for key in keys:
data = await self._storage.read(key)
if data is not None:
entries[key] = _decode(data)
return entries
async def load_snapshot(self, entries: dict[str, Any]) -> None:
"""Restore stash entries from a previously captured snapshot.
Args:
entries: Map of reference keys to their JSON values (from :meth:`take_snapshot`).
"""
for key, data in entries.items():
await self._storage.write(key, _encode(data))
async def clear(self) -> None:
"""Delete all entries in this agent's stash namespace."""
keys = await self.list()
for key in keys:
await self._storage.delete(key)
async def clear_session(self) -> None:
"""Delete all stash data for this session across all agents.
Unlike :meth:`clear`, which is scoped to this agent's namespace, this
scans ``context/<session_id>/`` on the base storage to remove data from
every agent that wrote to the session.
"""
prefix = f"{STASH_PREFIX}/{self._session_id}/"
keys = await self._base_storage.list(prefix)
for key in keys:
await self._base_storage.delete(key)
async def _store_tool_result(self, block: ContentBlock) -> None:
"""Store each sub-block of a tool result individually."""
tool_result = block["toolResult"]
for block_index, item in enumerate(tool_result["content"]):
try:
await self.store(tool_result["toolUseId"], block_index, _encode(item))
except Exception:
logger.warning(
"tool_use_id=<%s>, block_index=<%s> | failed to stash sub-block",
tool_result["toolUseId"],
block_index,
exc_info=True,
)