generated from coulomb/repo-seed
557 lines
22 KiB
Python
557 lines
22 KiB
Python
"""Runtime memory graph records mapped from Markitect contracts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
from .metadata import LifecycleState
|
|
from .primitives import compact_dict, mapping_digest, utc_now
|
|
|
|
|
|
MARKITECT_MEMORY_GRAPH_SCHEMA_VERSION = "markitect.memory.graph.v1"
|
|
MARKITECT_MEMORY_PROFILE_SCHEMA_VERSION = "markitect.memory.profile.v1"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MemorySourceSpan:
|
|
path: str
|
|
snapshot_id: str | None = None
|
|
unit_kind: str | None = None
|
|
unit_index: int | None = None
|
|
line_start: int | None = None
|
|
line_end: int | None = None
|
|
selector: str | None = None
|
|
engine: str | None = None
|
|
|
|
@classmethod
|
|
def from_contract(cls, data: dict[str, Any]) -> "MemorySourceSpan":
|
|
return cls(
|
|
path=str(data["path"]),
|
|
snapshot_id=_optional_str(data.get("snapshot_id")),
|
|
unit_kind=_optional_str(data.get("unit_kind")),
|
|
unit_index=_optional_int(data.get("unit_index")),
|
|
line_start=_optional_int(data.get("line_start")),
|
|
line_end=_optional_int(data.get("line_end")),
|
|
selector=_optional_str(data.get("selector")),
|
|
engine=_optional_str(data.get("engine")),
|
|
)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return compact_dict(
|
|
{
|
|
"path": self.path,
|
|
"snapshot_id": self.snapshot_id,
|
|
"unit_kind": self.unit_kind,
|
|
"unit_index": self.unit_index,
|
|
"line_start": self.line_start,
|
|
"line_end": self.line_end,
|
|
"selector": self.selector,
|
|
"engine": self.engine,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MemoryProfileRecord:
|
|
contract_profile_id: str
|
|
schema_version: str
|
|
title: str = ""
|
|
intent: str = ""
|
|
memory_kinds: tuple[str, ...] = ()
|
|
stores: dict[str, Any] = field(default_factory=dict)
|
|
limits: dict[str, Any] = field(default_factory=dict)
|
|
latency: dict[str, Any] = field(default_factory=dict)
|
|
retention: dict[str, Any] = field(default_factory=dict)
|
|
refresh: dict[str, Any] = field(default_factory=dict)
|
|
compaction: dict[str, Any] = field(default_factory=dict)
|
|
activation: dict[str, Any] = field(default_factory=dict)
|
|
policy: dict[str, Any] = field(default_factory=dict)
|
|
observability: dict[str, Any] = field(default_factory=dict)
|
|
failure: dict[str, Any] = field(default_factory=dict)
|
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
profile_id: str | None = None
|
|
imported_at: str = field(default_factory=lambda: utc_now().isoformat())
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.profile_id is None:
|
|
object.__setattr__(
|
|
self,
|
|
"profile_id",
|
|
runtime_id("memprofile", self.schema_version, self.contract_profile_id),
|
|
)
|
|
object.__setattr__(self, "memory_kinds", tuple(self.memory_kinds))
|
|
|
|
@classmethod
|
|
def from_markitect_contract(cls, data: dict[str, Any]) -> "MemoryProfileRecord":
|
|
schema_version = str(data.get("schema_version") or data.get("schema") or "")
|
|
if schema_version != MARKITECT_MEMORY_PROFILE_SCHEMA_VERSION:
|
|
raise ValueError(f"Unsupported memory profile schema `{schema_version}`.")
|
|
contract_id = str(data.get("id") or "")
|
|
if not contract_id:
|
|
raise ValueError("Memory profile contract must declare an id.")
|
|
return cls(
|
|
contract_profile_id=contract_id,
|
|
schema_version=schema_version,
|
|
title=str(data.get("title") or data.get("name") or ""),
|
|
intent=str(data.get("intent") or data.get("description") or ""),
|
|
memory_kinds=tuple(str(item) for item in data.get("memory_kinds", ())),
|
|
stores=dict(data.get("stores") or {}),
|
|
limits=dict(data.get("limits") or {}),
|
|
latency=dict(data.get("latency") or data.get("latency_targets") or {}),
|
|
retention=dict(data.get("retention") or data.get("retention_policy") or {}),
|
|
refresh=dict(data.get("refresh") or {}),
|
|
compaction=dict(data.get("compaction") or {}),
|
|
activation=dict(data.get("activation") or data.get("context_budget") or {}),
|
|
policy=dict(data.get("policy") or {}),
|
|
observability=dict(data.get("observability") or {}),
|
|
failure=dict(data.get("failure") or {}),
|
|
metadata=dict(data.get("metadata") or {}),
|
|
)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return compact_dict(
|
|
{
|
|
"profile_id": self.profile_id,
|
|
"contract_profile_id": self.contract_profile_id,
|
|
"schema_version": self.schema_version,
|
|
"title": self.title,
|
|
"intent": self.intent,
|
|
"memory_kinds": list(self.memory_kinds),
|
|
"stores": dict(self.stores),
|
|
"limits": dict(self.limits),
|
|
"latency": dict(self.latency),
|
|
"retention": dict(self.retention),
|
|
"refresh": dict(self.refresh),
|
|
"compaction": dict(self.compaction),
|
|
"activation": dict(self.activation),
|
|
"policy": dict(self.policy),
|
|
"observability": dict(self.observability),
|
|
"failure": dict(self.failure),
|
|
"metadata": dict(self.metadata),
|
|
"imported_at": self.imported_at,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MemoryNodeRecord:
|
|
graph_id: str
|
|
contract_graph_id: str
|
|
contract_node_id: str
|
|
kind: str
|
|
text: str = ""
|
|
namespace: dict[str, Any] = field(default_factory=dict)
|
|
source_spans: tuple[MemorySourceSpan, ...] = ()
|
|
provenance: tuple[dict[str, Any], ...] = ()
|
|
freshness: dict[str, Any] = field(default_factory=dict)
|
|
confidence: float | None = None
|
|
policy: dict[str, Any] = field(default_factory=dict)
|
|
lifecycle: LifecycleState = LifecycleState.ACTIVE
|
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
node_id: str | None = None
|
|
created_at: str = field(default_factory=lambda: utc_now().isoformat())
|
|
updated_at: str = field(default_factory=lambda: utc_now().isoformat())
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.node_id is None:
|
|
object.__setattr__(
|
|
self,
|
|
"node_id",
|
|
runtime_id("memnode", self.contract_graph_id, self.contract_node_id),
|
|
)
|
|
object.__setattr__(self, "source_spans", tuple(self.source_spans))
|
|
object.__setattr__(self, "provenance", tuple(dict(item) for item in self.provenance))
|
|
object.__setattr__(self, "lifecycle", LifecycleState(self.lifecycle))
|
|
|
|
@classmethod
|
|
def from_markitect_contract(
|
|
cls,
|
|
*,
|
|
graph_id: str,
|
|
contract_graph_id: str,
|
|
data: dict[str, Any],
|
|
graph_namespace: dict[str, Any] | None = None,
|
|
) -> "MemoryNodeRecord":
|
|
contract_node_id = str(data.get("id") or "")
|
|
if not contract_node_id:
|
|
raise ValueError("Memory graph node must declare an id.")
|
|
spans = data.get("source_spans") or data.get("spans") or []
|
|
return cls(
|
|
graph_id=graph_id,
|
|
contract_graph_id=contract_graph_id,
|
|
contract_node_id=contract_node_id,
|
|
kind=str(data.get("kind") or data.get("type") or ""),
|
|
text=str(data.get("text") or data.get("content") or data.get("summary") or ""),
|
|
namespace=dict(data.get("namespace") or graph_namespace or {}),
|
|
source_spans=tuple(MemorySourceSpan.from_contract(span) for span in _mapping_list(spans)),
|
|
provenance=tuple(_mapping_list(data.get("provenance"))),
|
|
freshness=dict(data.get("freshness") or {}),
|
|
confidence=_optional_float(data.get("confidence")),
|
|
policy=dict(data.get("policy") or {}),
|
|
metadata=dict(data.get("metadata") or {}),
|
|
)
|
|
|
|
@property
|
|
def resource_id(self) -> str:
|
|
return f"memory-node:{self.node_id}"
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return compact_dict(
|
|
{
|
|
"node_id": self.node_id,
|
|
"graph_id": self.graph_id,
|
|
"contract_graph_id": self.contract_graph_id,
|
|
"contract_node_id": self.contract_node_id,
|
|
"kind": self.kind,
|
|
"text": self.text,
|
|
"namespace": dict(self.namespace),
|
|
"source_spans": [span.to_dict() for span in self.source_spans],
|
|
"provenance": [dict(item) for item in self.provenance],
|
|
"freshness": dict(self.freshness),
|
|
"confidence": self.confidence,
|
|
"policy": dict(self.policy),
|
|
"lifecycle": self.lifecycle.value,
|
|
"metadata": dict(self.metadata),
|
|
"created_at": self.created_at,
|
|
"updated_at": self.updated_at,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MemoryEdgeRecord:
|
|
graph_id: str
|
|
contract_graph_id: str
|
|
contract_edge_id: str
|
|
kind: str
|
|
source_node_id: str
|
|
target_node_id: str
|
|
source_contract_node_id: str
|
|
target_contract_node_id: str
|
|
provenance: tuple[dict[str, Any], ...] = ()
|
|
freshness: dict[str, Any] = field(default_factory=dict)
|
|
confidence: float | None = None
|
|
policy: dict[str, Any] = field(default_factory=dict)
|
|
lifecycle: LifecycleState = LifecycleState.ACTIVE
|
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
edge_id: str | None = None
|
|
created_at: str = field(default_factory=lambda: utc_now().isoformat())
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.edge_id is None:
|
|
object.__setattr__(
|
|
self,
|
|
"edge_id",
|
|
runtime_id("memedge", self.contract_graph_id, self.contract_edge_id),
|
|
)
|
|
object.__setattr__(self, "provenance", tuple(dict(item) for item in self.provenance))
|
|
object.__setattr__(self, "lifecycle", LifecycleState(self.lifecycle))
|
|
|
|
@classmethod
|
|
def from_markitect_contract(
|
|
cls,
|
|
*,
|
|
graph_id: str,
|
|
contract_graph_id: str,
|
|
data: dict[str, Any],
|
|
node_id_by_contract_id: dict[str, str],
|
|
) -> "MemoryEdgeRecord":
|
|
kind = str(data.get("kind") or data.get("type") or "")
|
|
source_contract_id = str(data.get("source") or data.get("from") or "")
|
|
target_contract_id = str(data.get("target") or data.get("to") or "")
|
|
contract_edge_id = str(
|
|
data.get("id")
|
|
or f"edge:{stable_suffix(kind, source_contract_id, target_contract_id)}"
|
|
)
|
|
try:
|
|
source_node_id = node_id_by_contract_id[source_contract_id]
|
|
target_node_id = node_id_by_contract_id[target_contract_id]
|
|
except KeyError as exc:
|
|
missing = source_contract_id if source_contract_id not in node_id_by_contract_id else target_contract_id
|
|
raise ValueError(f"Memory edge `{contract_edge_id}` references unknown node `{missing}`.") from exc
|
|
return cls(
|
|
graph_id=graph_id,
|
|
contract_graph_id=contract_graph_id,
|
|
contract_edge_id=contract_edge_id,
|
|
kind=kind,
|
|
source_node_id=source_node_id,
|
|
target_node_id=target_node_id,
|
|
source_contract_node_id=source_contract_id,
|
|
target_contract_node_id=target_contract_id,
|
|
provenance=tuple(_mapping_list(data.get("provenance"))),
|
|
freshness=dict(data.get("freshness") or {}),
|
|
confidence=_optional_float(data.get("confidence")),
|
|
policy=dict(data.get("policy") or {}),
|
|
metadata=dict(data.get("metadata") or {}),
|
|
)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return compact_dict(
|
|
{
|
|
"edge_id": self.edge_id,
|
|
"graph_id": self.graph_id,
|
|
"contract_graph_id": self.contract_graph_id,
|
|
"contract_edge_id": self.contract_edge_id,
|
|
"kind": self.kind,
|
|
"source_node_id": self.source_node_id,
|
|
"target_node_id": self.target_node_id,
|
|
"source_contract_node_id": self.source_contract_node_id,
|
|
"target_contract_node_id": self.target_contract_node_id,
|
|
"provenance": [dict(item) for item in self.provenance],
|
|
"freshness": dict(self.freshness),
|
|
"confidence": self.confidence,
|
|
"policy": dict(self.policy),
|
|
"lifecycle": self.lifecycle.value,
|
|
"metadata": dict(self.metadata),
|
|
"created_at": self.created_at,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MemoryEventRecord:
|
|
graph_id: str
|
|
contract_graph_id: str
|
|
contract_event_id: str
|
|
kind: str
|
|
timestamp: str
|
|
namespace: dict[str, Any] = field(default_factory=dict)
|
|
actor_id: str | None = None
|
|
thread: str | None = None
|
|
task: str | None = None
|
|
node_updates: tuple[dict[str, Any], ...] = ()
|
|
edge_updates: tuple[dict[str, Any], ...] = ()
|
|
package_refs: tuple[str, ...] = ()
|
|
activation_refs: tuple[str, ...] = ()
|
|
branch: dict[str, Any] = field(default_factory=dict)
|
|
policy: dict[str, Any] = field(default_factory=dict)
|
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
event_id: str | None = None
|
|
appended_at: str = field(default_factory=lambda: utc_now().isoformat())
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.event_id is None:
|
|
object.__setattr__(
|
|
self,
|
|
"event_id",
|
|
runtime_id("memevent", self.contract_graph_id, self.contract_event_id),
|
|
)
|
|
object.__setattr__(self, "node_updates", tuple(dict(item) for item in self.node_updates))
|
|
object.__setattr__(self, "edge_updates", tuple(dict(item) for item in self.edge_updates))
|
|
object.__setattr__(self, "package_refs", tuple(self.package_refs))
|
|
object.__setattr__(self, "activation_refs", tuple(self.activation_refs))
|
|
|
|
@classmethod
|
|
def from_markitect_contract(
|
|
cls,
|
|
*,
|
|
graph_id: str,
|
|
contract_graph_id: str,
|
|
data: dict[str, Any],
|
|
graph_namespace: dict[str, Any] | None = None,
|
|
) -> "MemoryEventRecord":
|
|
kind = str(data.get("kind") or data.get("type") or "recorded")
|
|
timestamp = str(data.get("timestamp") or data.get("at") or utc_now().isoformat())
|
|
contract_event_id = str(data.get("id") or f"event:{stable_suffix(kind, timestamp, data)}")
|
|
return cls(
|
|
graph_id=graph_id,
|
|
contract_graph_id=contract_graph_id,
|
|
contract_event_id=contract_event_id,
|
|
kind=kind,
|
|
timestamp=timestamp,
|
|
namespace=dict(data.get("namespace") or graph_namespace or {}),
|
|
actor_id=_optional_str(data.get("actor") or data.get("actor_id")),
|
|
thread=_optional_str(data.get("thread")),
|
|
task=_optional_str(data.get("task")),
|
|
node_updates=tuple(_mapping_list(data.get("node_updates") or data.get("nodes"))),
|
|
edge_updates=tuple(_mapping_list(data.get("edge_updates") or data.get("edges"))),
|
|
package_refs=tuple(_string_list(data.get("package_refs") or data.get("packages"))),
|
|
activation_refs=tuple(_string_list(data.get("activation_refs") or data.get("activations"))),
|
|
branch=dict(data.get("branch") or {}),
|
|
policy=dict(data.get("policy") or {}),
|
|
metadata=dict(data.get("metadata") or {}),
|
|
)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return compact_dict(
|
|
{
|
|
"event_id": self.event_id,
|
|
"graph_id": self.graph_id,
|
|
"contract_graph_id": self.contract_graph_id,
|
|
"contract_event_id": self.contract_event_id,
|
|
"kind": self.kind,
|
|
"timestamp": self.timestamp,
|
|
"namespace": dict(self.namespace),
|
|
"actor_id": self.actor_id,
|
|
"thread": self.thread,
|
|
"task": self.task,
|
|
"node_updates": [dict(item) for item in self.node_updates],
|
|
"edge_updates": [dict(item) for item in self.edge_updates],
|
|
"package_refs": list(self.package_refs),
|
|
"activation_refs": list(self.activation_refs),
|
|
"branch": dict(self.branch),
|
|
"policy": dict(self.policy),
|
|
"metadata": dict(self.metadata),
|
|
"appended_at": self.appended_at,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MemoryGraphImportResult:
|
|
graph_id: str
|
|
contract_graph_id: str
|
|
schema_version: str
|
|
title: str = ""
|
|
intent: str = ""
|
|
namespace: dict[str, Any] = field(default_factory=dict)
|
|
profile: MemoryProfileRecord | None = None
|
|
nodes: tuple[MemoryNodeRecord, ...] = ()
|
|
edges: tuple[MemoryEdgeRecord, ...] = ()
|
|
events: tuple[MemoryEventRecord, ...] = ()
|
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
|
|
def __post_init__(self) -> None:
|
|
object.__setattr__(self, "nodes", tuple(self.nodes))
|
|
object.__setattr__(self, "edges", tuple(self.edges))
|
|
object.__setattr__(self, "events", tuple(self.events))
|
|
|
|
@classmethod
|
|
def from_markitect_contract(
|
|
cls,
|
|
graph_contract: dict[str, Any],
|
|
*,
|
|
profile_contract: dict[str, Any] | None = None,
|
|
) -> "MemoryGraphImportResult":
|
|
schema_version = str(graph_contract.get("schema_version") or graph_contract.get("schema") or "")
|
|
if schema_version != MARKITECT_MEMORY_GRAPH_SCHEMA_VERSION:
|
|
raise ValueError(f"Unsupported memory graph schema `{schema_version}`.")
|
|
contract_graph_id = str(graph_contract.get("id") or "")
|
|
if not contract_graph_id:
|
|
raise ValueError("Memory graph contract must declare an id.")
|
|
graph_id = runtime_id("memgraph", schema_version, contract_graph_id)
|
|
namespace = dict(graph_contract.get("namespace") or {})
|
|
nodes = tuple(
|
|
MemoryNodeRecord.from_markitect_contract(
|
|
graph_id=graph_id,
|
|
contract_graph_id=contract_graph_id,
|
|
data=node,
|
|
graph_namespace=namespace,
|
|
)
|
|
for node in _mapping_values(graph_contract.get("nodes"))
|
|
)
|
|
node_id_by_contract_id = {node.contract_node_id: str(node.node_id) for node in nodes}
|
|
edges = tuple(
|
|
MemoryEdgeRecord.from_markitect_contract(
|
|
graph_id=graph_id,
|
|
contract_graph_id=contract_graph_id,
|
|
data=edge,
|
|
node_id_by_contract_id=node_id_by_contract_id,
|
|
)
|
|
for edge in _mapping_values(graph_contract.get("edges"))
|
|
)
|
|
events = tuple(
|
|
MemoryEventRecord.from_markitect_contract(
|
|
graph_id=graph_id,
|
|
contract_graph_id=contract_graph_id,
|
|
data=event,
|
|
graph_namespace=namespace,
|
|
)
|
|
for event in _mapping_values(graph_contract.get("events"))
|
|
)
|
|
profile = MemoryProfileRecord.from_markitect_contract(profile_contract) if profile_contract else None
|
|
return cls(
|
|
graph_id=graph_id,
|
|
contract_graph_id=contract_graph_id,
|
|
schema_version=schema_version,
|
|
title=str(graph_contract.get("title") or graph_contract.get("name") or ""),
|
|
intent=str(graph_contract.get("intent") or graph_contract.get("description") or ""),
|
|
namespace=namespace,
|
|
profile=profile,
|
|
nodes=nodes,
|
|
edges=edges,
|
|
events=events,
|
|
metadata=dict(graph_contract.get("metadata") or {}),
|
|
)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return compact_dict(
|
|
{
|
|
"graph_id": self.graph_id,
|
|
"contract_graph_id": self.contract_graph_id,
|
|
"schema_version": self.schema_version,
|
|
"title": self.title,
|
|
"intent": self.intent,
|
|
"namespace": dict(self.namespace),
|
|
"profile": self.profile.to_dict() if self.profile else None,
|
|
"nodes": [node.to_dict() for node in self.nodes],
|
|
"edges": [edge.to_dict() for edge in self.edges],
|
|
"events": [event.to_dict() for event in self.events],
|
|
"metadata": dict(self.metadata),
|
|
}
|
|
)
|
|
|
|
|
|
def runtime_id(prefix: str, *parts: Any) -> str:
|
|
return f"{prefix}_{stable_suffix(*parts)}"
|
|
|
|
|
|
def stable_suffix(*parts: Any) -> str:
|
|
digest = mapping_digest(parts)
|
|
return digest.removeprefix("sha256:")[:24]
|
|
|
|
|
|
def _mapping_values(value: Any) -> list[dict[str, Any]]:
|
|
if value is None:
|
|
return []
|
|
if isinstance(value, dict):
|
|
items: list[dict[str, Any]] = []
|
|
for key, raw in value.items():
|
|
if not isinstance(raw, dict):
|
|
continue
|
|
item = dict(raw)
|
|
item.setdefault("id", str(key))
|
|
items.append(item)
|
|
return items
|
|
return _mapping_list(value)
|
|
|
|
|
|
def _mapping_list(value: Any) -> list[dict[str, Any]]:
|
|
if value is None:
|
|
return []
|
|
if isinstance(value, dict):
|
|
return [dict(value)]
|
|
if isinstance(value, list):
|
|
return [dict(item) for item in value if isinstance(item, dict)]
|
|
if isinstance(value, tuple):
|
|
return [dict(item) for item in value if isinstance(item, dict)]
|
|
return []
|
|
|
|
|
|
def _string_list(value: Any) -> list[str]:
|
|
if value is None:
|
|
return []
|
|
if isinstance(value, (list, tuple)):
|
|
return [str(item) for item in value if item is not None]
|
|
return [str(value)]
|
|
|
|
|
|
def _optional_str(value: Any) -> str | None:
|
|
if value is None:
|
|
return None
|
|
text = str(value)
|
|
return text if text else None
|
|
|
|
|
|
def _optional_int(value: Any) -> int | None:
|
|
if value is None or value == "":
|
|
return None
|
|
return int(value)
|
|
|
|
|
|
def _optional_float(value: Any) -> float | None:
|
|
if value is None or value == "":
|
|
return None
|
|
return float(value)
|