phase-memory/src/phase_memory/bridge.py

97 lines
3.8 KiB
Python

"""Markitect package bridge helpers."""
from __future__ import annotations
from typing import Any, Protocol
from .contracts import ContractIngressResult, graph_from_markitect, profile_from_markitect, selection_from_markitect
from .models import Diagnostic
from .utils import stable_digest
MARKITECT_PACKAGE_REQUEST_SCHEMA = "phase_memory.markitect.package_request.v1"
MARKITECT_PACKAGE_RESPONSE_SCHEMA = "phase_memory.markitect.package_response.v1"
class MarkitectValidator(Protocol):
def validate_profile(self, data: dict[str, Any]) -> ContractIngressResult: ...
def validate_graph(self, data: dict[str, Any]) -> ContractIngressResult: ...
def validate_selection(self, data: dict[str, Any]) -> ContractIngressResult: ...
class LocalMarkitectValidator:
"""Dependency-light validation boundary using local ingress diagnostics."""
def validate_profile(self, data: dict[str, Any]) -> ContractIngressResult:
return profile_from_markitect(data)
def validate_graph(self, data: dict[str, Any]) -> ContractIngressResult:
return graph_from_markitect(data)
def validate_selection(self, data: dict[str, Any]) -> ContractIngressResult:
return selection_from_markitect(data)
class OptionalMarkitectValidator:
"""Adapter shell for optional Markitect-owned validation."""
def __init__(self, delegate: Any | None = None) -> None:
self.delegate = delegate
self.local = LocalMarkitectValidator()
def validate_profile(self, data: dict[str, Any]) -> ContractIngressResult:
if self.delegate and hasattr(self.delegate, "validate_memory_profile"):
return self.delegate.validate_memory_profile(data)
return self.local.validate_profile(data)
def validate_graph(self, data: dict[str, Any]) -> ContractIngressResult:
if self.delegate and hasattr(self.delegate, "validate_memory_graph"):
return self.delegate.validate_memory_graph(data)
return self.local.validate_graph(data)
def validate_selection(self, data: dict[str, Any]) -> ContractIngressResult:
if self.delegate and hasattr(self.delegate, "validate_memory_selection"):
return self.delegate.validate_memory_selection(data)
return self.local.validate_selection(data)
def package_request_from_selection(
selection: dict[str, Any],
*,
compiler: str,
diagnostics: tuple[Diagnostic, ...] = (),
) -> dict[str, Any]:
metadata = dict(selection.get("metadata") or {})
request_id = f"markitect-package-request:{stable_digest([selection, compiler])}"
return {
"schema_version": MARKITECT_PACKAGE_REQUEST_SCHEMA,
"id": request_id,
"selection_id": selection.get("id"),
"graph_id": selection.get("graph"),
"profile_id": selection.get("profile"),
"selected_nodes": list(selection.get("nodes", ())),
"selected_events": list(selection.get("events", ())),
"budget": {
"max_items": metadata.get("max_items"),
"max_tokens": metadata.get("max_tokens"),
"token_estimate": metadata.get("token_estimate"),
},
"policy": metadata.get("policy", {}),
"provenance": {
"selected_items": metadata.get("selected_items", {}),
"planned_by": metadata.get("planned_by"),
},
"compiler": compiler,
"compiler_diagnostics": [diagnostic.to_dict() for diagnostic in diagnostics],
"selection": dict(selection),
"dry_run": True,
}
def package_response_envelope(response: dict[str, Any], *, request_id: str) -> dict[str, Any]:
return {
"schema_version": MARKITECT_PACKAGE_RESPONSE_SCHEMA,
"request_id": request_id,
"package_ref": response.get("package_id") or response.get("package_ref") or "",
"compiler_diagnostics": list(response.get("diagnostics", ())),
"response": dict(response),
}