feat(memory): add graph runtime import store

This commit is contained in:
tegwick 2026-05-15 01:56:51 +02:00
parent 5c450fcaa5
commit 8daab687b2
12 changed files with 1103 additions and 4 deletions

View file

@ -2,5 +2,10 @@
from .asset_registry import InMemoryAssetRegistryRepository
from .blob_storage import InMemoryBlobStorage
from .graph_store import InMemoryMemoryGraphRepository
__all__ = ["InMemoryAssetRegistryRepository", "InMemoryBlobStorage"]
__all__ = [
"InMemoryAssetRegistryRepository",
"InMemoryBlobStorage",
"InMemoryMemoryGraphRepository",
]

View file

@ -0,0 +1,126 @@
"""In-memory operational memory graph repository."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Iterable
from kontextual_engine.core import (
LifecycleState,
MemoryEdgeRecord,
MemoryEventRecord,
MemoryNodeRecord,
MemoryProfileRecord,
)
from kontextual_engine.errors import DuplicateResourceError, NotFoundError, ValidationError
@dataclass
class InMemoryMemoryGraphRepository:
profiles: dict[str, MemoryProfileRecord] = field(default_factory=dict)
nodes: dict[str, MemoryNodeRecord] = field(default_factory=dict)
edges: dict[str, MemoryEdgeRecord] = field(default_factory=dict)
events: dict[str, MemoryEventRecord] = field(default_factory=dict)
def save_memory_profile(self, profile: MemoryProfileRecord) -> MemoryProfileRecord:
self.profiles[str(profile.profile_id)] = profile
return profile
def get_memory_profile(self, profile_id: str) -> MemoryProfileRecord:
try:
return self.profiles[profile_id]
except KeyError as exc:
raise NotFoundError("Memory profile not found", details={"profile_id": profile_id}) from exc
def save_memory_node(self, node: MemoryNodeRecord) -> MemoryNodeRecord:
self.nodes[str(node.node_id)] = node
return node
def get_memory_node(self, node_id: str) -> MemoryNodeRecord:
try:
return self.nodes[node_id]
except KeyError as exc:
raise NotFoundError("Memory node not found", details={"node_id": node_id}) from exc
def list_memory_nodes(
self,
*,
graph_id: str | None = None,
kind: str | None = None,
lifecycle: LifecycleState | str | None = None,
) -> list[MemoryNodeRecord]:
nodes: Iterable[MemoryNodeRecord] = self.nodes.values()
if graph_id is not None:
nodes = [node for node in nodes if node.graph_id == graph_id]
if kind is not None:
nodes = [node for node in nodes if node.kind == kind]
if lifecycle is not None:
lifecycle = LifecycleState(lifecycle)
nodes = [node for node in nodes if node.lifecycle == lifecycle]
return sorted(nodes, key=lambda node: (node.graph_id, node.kind, node.contract_node_id))
def save_memory_edge(self, edge: MemoryEdgeRecord) -> MemoryEdgeRecord:
source = self.get_memory_node(edge.source_node_id)
target = self.get_memory_node(edge.target_node_id)
if source.graph_id != edge.graph_id or target.graph_id != edge.graph_id:
raise ValidationError(
"Memory edge endpoints must belong to the edge graph.",
details={
"edge_id": edge.edge_id,
"graph_id": edge.graph_id,
"source_graph_id": source.graph_id,
"target_graph_id": target.graph_id,
},
)
self.edges[str(edge.edge_id)] = edge
return edge
def get_memory_edge(self, edge_id: str) -> MemoryEdgeRecord:
try:
return self.edges[edge_id]
except KeyError as exc:
raise NotFoundError("Memory edge not found", details={"edge_id": edge_id}) from exc
def list_memory_edges(
self,
*,
graph_id: str | None = None,
source_node_id: str | None = None,
target_node_id: str | None = None,
) -> list[MemoryEdgeRecord]:
edges: Iterable[MemoryEdgeRecord] = self.edges.values()
if graph_id is not None:
edges = [edge for edge in edges if edge.graph_id == graph_id]
if source_node_id is not None:
edges = [edge for edge in edges if edge.source_node_id == source_node_id]
if target_node_id is not None:
edges = [edge for edge in edges if edge.target_node_id == target_node_id]
return sorted(edges, key=lambda edge: (edge.graph_id, edge.kind, edge.contract_edge_id))
def append_memory_event(self, event: MemoryEventRecord) -> MemoryEventRecord:
if event.event_id in self.events:
raise DuplicateResourceError(
"Memory event already exists; event storage is append-only.",
details={"event_id": event.event_id, "contract_event_id": event.contract_event_id},
)
self.events[str(event.event_id)] = event
return event
def get_memory_event(self, event_id: str) -> MemoryEventRecord:
try:
return self.events[event_id]
except KeyError as exc:
raise NotFoundError("Memory event not found", details={"event_id": event_id}) from exc
def list_memory_events(
self,
*,
graph_id: str | None = None,
kind: str | None = None,
) -> list[MemoryEventRecord]:
events: Iterable[MemoryEventRecord] = self.events.values()
if graph_id is not None:
events = [event for event in events if event.graph_id == graph_id]
if kind is not None:
events = [event for event in events if event.kind == kind]
return sorted(events, key=lambda event: (event.graph_id, event.timestamp, event.contract_event_id))