feat(memory): add graph runtime import store
This commit is contained in:
parent
5c450fcaa5
commit
8daab687b2
12 changed files with 1103 additions and 4 deletions
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
126
src/kontextual_engine/adapters/memory/graph_store.py
Normal file
126
src/kontextual_engine/adapters/memory/graph_store.py
Normal 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))
|
||||
Loading…
Add table
Add a link
Reference in a new issue