Complete adapter conformance coverage

This commit is contained in:
2026-05-18 20:38:00 +02:00
parent 2a4301d22f
commit 850979ccf1
5 changed files with 72 additions and 2 deletions

View File

@@ -56,8 +56,10 @@ The service module includes reusable conformance helpers for:
- graph stores - graph stores
- event logs - event logs
- context package compilers - context package compilers
- semantic indexes
- policy gateways - policy gateways
- audit sinks - audit sinks
- runtime registries
External adapters should pass these helpers before being wired into a runtime. External adapters should pass these helpers before being wired into a runtime.

View File

@@ -260,6 +260,43 @@ class JsonlAuditSink:
return {"recorded": True, "index": index, "event": stored} return {"recorded": True, "index": index, "event": stored}
class InMemorySemanticIndex:
def __init__(self) -> None:
self._nodes_by_graph: dict[str, list[MemoryNode]] = {}
def upsert_nodes(self, nodes: list[MemoryNode]) -> dict[str, Any]:
for node in nodes:
graph_id = str(node.metadata.get("graph_id") or "local")
existing = [item for item in self._nodes_by_graph.get(graph_id, []) if item.node_id != node.node_id]
existing.append(node)
self._nodes_by_graph[graph_id] = sorted(existing, key=lambda item: item.node_id)
return {"upserted": len(nodes)}
def query(self, *, graph_id: str, query: str, limit: int = 10) -> list[dict[str, Any]]:
terms = {term.lower() for term in query.split() if term.strip()}
results: list[dict[str, Any]] = []
for node in self._nodes_by_graph.get(graph_id, []):
text = f"{node.kind} {node.text}".lower()
score = sum(1 for term in terms if term in text)
if score:
results.append({"id": node.node_id, "score": score, "kind": node.kind})
return sorted(results, key=lambda item: (-item["score"], item["id"]))[:limit]
class InMemoryRuntimeRegistry:
def __init__(self) -> None:
self._envelopes: dict[str, dict[str, Any]] = {}
def publish_runtime_envelope(self, envelope: dict[str, Any]) -> dict[str, Any]:
reference = str(envelope.get("operation_id") or envelope.get("id") or f"envelope:{len(self._envelopes)}")
stored = dict(envelope)
self._envelopes[reference] = stored
return {"published": True, "reference": reference, "envelope": stored}
def fetch_runtime_envelope(self, reference: str) -> dict[str, Any]:
return dict(self._envelopes[reference])
def _safe_name(identifier: str) -> str: def _safe_name(identifier: str) -> str:
safe = "".join(char if char.isalnum() or char in ("-", "_", ".") else "_" for char in identifier) safe = "".join(char if char.isalnum() or char in ("-", "_", ".") else "_" for char in identifier)
return safe or "anonymous" return safe or "anonymous"

View File

@@ -5,7 +5,15 @@ from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any from typing import Any
from .adapters import AllowAllPolicyGateway, InMemoryMemoryEventLog, InMemoryMemoryGraphStore, NoopContextPackageCompiler, RecordingAuditSink from .adapters import (
AllowAllPolicyGateway,
InMemoryMemoryEventLog,
InMemoryMemoryGraphStore,
InMemoryRuntimeRegistry,
InMemorySemanticIndex,
NoopContextPackageCompiler,
RecordingAuditSink,
)
from .models import Diagnostic, MemoryEvent, MemoryNode, PolicyDecision, ProfileIntent from .models import Diagnostic, MemoryEvent, MemoryNode, PolicyDecision, ProfileIntent
from .runtime import PhaseMemoryRuntime from .runtime import PhaseMemoryRuntime
@@ -179,6 +187,22 @@ def assert_audit_sink_conformance(sink) -> None:
assert receipt.get("recorded") is True assert receipt.get("recorded") is True
def assert_semantic_index_conformance(index) -> None:
node = MemoryNode("node.semantic", "decision", "Conformance search target", metadata={"graph_id": "graph.conformance"})
receipt = index.upsert_nodes([node])
results = index.query(graph_id="graph.conformance", query="search target", limit=5)
assert receipt.get("upserted") == 1
assert results and results[0]["id"] == node.node_id
def assert_runtime_registry_conformance(registry) -> None:
envelope = {"operation_id": "op.conformance", "operation": "conformance"}
receipt = registry.publish_runtime_envelope(envelope)
fetched = registry.fetch_runtime_envelope(receipt["reference"])
assert receipt["published"] is True
assert fetched["operation_id"] == "op.conformance"
def default_conformance_adapters() -> dict[str, Any]: def default_conformance_adapters() -> dict[str, Any]:
return { return {
"graph_store": InMemoryMemoryGraphStore(), "graph_store": InMemoryMemoryGraphStore(),
@@ -186,4 +210,6 @@ def default_conformance_adapters() -> dict[str, Any]:
"context_compiler": NoopContextPackageCompiler(), "context_compiler": NoopContextPackageCompiler(),
"policy_gateway": AllowAllPolicyGateway(), "policy_gateway": AllowAllPolicyGateway(),
"audit_sink": RecordingAuditSink(), "audit_sink": RecordingAuditSink(),
"semantic_index": InMemorySemanticIndex(),
"runtime_registry": InMemoryRuntimeRegistry(),
} }

View File

@@ -10,6 +10,8 @@ from phase_memory.service import (
assert_event_log_conformance, assert_event_log_conformance,
assert_graph_store_conformance, assert_graph_store_conformance,
assert_policy_gateway_conformance, assert_policy_gateway_conformance,
assert_runtime_registry_conformance,
assert_semantic_index_conformance,
default_conformance_adapters, default_conformance_adapters,
health_report, health_report,
kontextual_delegation_envelope, kontextual_delegation_envelope,
@@ -53,6 +55,8 @@ def test_default_adapter_conformance_helpers() -> None:
assert_context_compiler_conformance(adapters["context_compiler"]) assert_context_compiler_conformance(adapters["context_compiler"])
assert_policy_gateway_conformance(adapters["policy_gateway"]) assert_policy_gateway_conformance(adapters["policy_gateway"])
assert_audit_sink_conformance(adapters["audit_sink"]) assert_audit_sink_conformance(adapters["audit_sink"])
assert_semantic_index_conformance(adapters["semantic_index"])
assert_runtime_registry_conformance(adapters["runtime_registry"])
def test_kontextual_delegation_envelope_is_explicit() -> None: def test_kontextual_delegation_envelope_is_explicit() -> None:

View File

@@ -66,7 +66,8 @@ Implemented outputs:
- Health reports expose adapter availability, config diagnostics, stale memory - Health reports expose adapter availability, config diagnostics, stale memory
counts, pending review counts, and store counts. counts, pending review counts, and store counts.
- Adapter conformance helpers cover graph stores, event logs, context package - Adapter conformance helpers cover graph stores, event logs, context package
compilers, policy gateways, and audit sinks. compilers, semantic indexes, policy gateways, audit sinks, and runtime
registries.
- `kontextual_delegation_envelope` documents the JSON boundary that avoids - `kontextual_delegation_envelope` documents the JSON boundary that avoids
circular imports while preserving ownership between phase-memory and circular imports while preserving ownership between phase-memory and
`kontextual-engine`. `kontextual-engine`.