Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a0230c-b06c-7641-808a-e191b6d1da49
259 lines
9.1 KiB
Python
259 lines
9.1 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import hashlib
|
|
import json
|
|
from collections.abc import Mapping
|
|
from copy import deepcopy
|
|
from datetime import datetime, timezone
|
|
from typing import Any, Protocol
|
|
from uuid import UUID, uuid4
|
|
|
|
from hub_core.contracts import CONTRACT_VERSION
|
|
from hub_core.runtime.models import (
|
|
EventCommand,
|
|
MessageCommand,
|
|
PortAccepted,
|
|
PortCollection,
|
|
PortRecord,
|
|
Provenance,
|
|
RegistryRegistration,
|
|
)
|
|
from hub_core.runtime.repository_navigation import NavigationProjection
|
|
from hub_core.runtime.workload_projection import WorkloadProjection
|
|
|
|
|
|
class PortStore(Protocol):
|
|
"""Persistence boundary for the initial hub-core runtime ports."""
|
|
|
|
backend_name: str
|
|
|
|
async def readiness_checks(self) -> dict[str, str]: ...
|
|
|
|
async def register_extension(
|
|
self,
|
|
registration: RegistryRegistration,
|
|
correlation_id: UUID,
|
|
) -> PortAccepted: ...
|
|
|
|
async def send_message(self, command: MessageCommand) -> PortAccepted: ...
|
|
|
|
async def list_messages(self, address: str, conversation_id: UUID | None) -> PortCollection: ...
|
|
|
|
async def append_progress(self, command: EventCommand) -> PortAccepted: ...
|
|
|
|
async def append_interaction(self, command: EventCommand) -> PortAccepted: ...
|
|
|
|
async def query_projection(self, projection_id: str) -> PortRecord | None: ...
|
|
|
|
async def get_repository_navigation(self) -> NavigationProjection | None: ...
|
|
|
|
async def replace_repository_navigation(
|
|
self, projection: NavigationProjection
|
|
) -> None: ...
|
|
|
|
async def mark_repository_navigation_stale(
|
|
self, *, checked_at: datetime, diagnostic: Mapping[str, Any]
|
|
) -> None: ...
|
|
|
|
async def get_workload_projection(self) -> WorkloadProjection | None: ...
|
|
|
|
async def replace_workload_projection(self, projection: WorkloadProjection) -> None: ...
|
|
|
|
async def mark_workload_projection_stale(
|
|
self, *, checked_at: datetime, diagnostic: Mapping[str, Any]
|
|
) -> None: ...
|
|
|
|
|
|
class InMemoryPortStore:
|
|
"""Deterministic ephemeral backend for local runtime and conformance tests.
|
|
|
|
Production readiness rejects this backend unless explicitly allowed. The
|
|
store deliberately keeps progress and interaction event families separate.
|
|
"""
|
|
|
|
backend_name = "memory"
|
|
|
|
def __init__(self) -> None:
|
|
self._lock = asyncio.Lock()
|
|
self._registrations: dict[str, dict[str, Any]] = {}
|
|
self._messages: list[dict[str, Any]] = []
|
|
self._progress_events: list[dict[str, Any]] = []
|
|
self._interaction_events: list[dict[str, Any]] = []
|
|
self._repository_navigation: NavigationProjection | None = None
|
|
self._workload_projection: WorkloadProjection | None = None
|
|
|
|
async def readiness_checks(self) -> dict[str, str]:
|
|
return {"database": "not_applicable"}
|
|
|
|
async def register_extension(
|
|
self,
|
|
registration: RegistryRegistration,
|
|
correlation_id: UUID,
|
|
) -> PortAccepted:
|
|
hub_slug = str(registration.descriptor["hub_slug"])
|
|
value = registration.model_dump(mode="json")
|
|
async with self._lock:
|
|
duplicate = self._registrations.get(hub_slug) == value
|
|
self._registrations[hub_slug] = deepcopy(value)
|
|
return PortAccepted(
|
|
id=hub_slug,
|
|
status="duplicate" if duplicate else "accepted",
|
|
correlation_id=correlation_id,
|
|
)
|
|
|
|
async def send_message(self, command: MessageCommand) -> PortAccepted:
|
|
message_id = uuid4()
|
|
value = {
|
|
"id": str(message_id),
|
|
"created_at": _now().isoformat(),
|
|
**command.model_dump(mode="json"),
|
|
}
|
|
async with self._lock:
|
|
self._messages.append(value)
|
|
return PortAccepted(
|
|
id=str(message_id),
|
|
status="accepted",
|
|
correlation_id=command.correlation_id,
|
|
)
|
|
|
|
async def list_messages(self, address: str, conversation_id: UUID | None) -> PortCollection:
|
|
async with self._lock:
|
|
values = [
|
|
deepcopy(message)
|
|
for message in self._messages
|
|
if address in message["to_addresses"]
|
|
and (
|
|
conversation_id is None
|
|
or message.get("conversation_id") == str(conversation_id)
|
|
)
|
|
]
|
|
return PortCollection(items=[self._record("message", value) for value in values])
|
|
|
|
async def append_progress(self, command: EventCommand) -> PortAccepted:
|
|
return await self._append_event(command, self._progress_events, "progress")
|
|
|
|
async def append_interaction(self, command: EventCommand) -> PortAccepted:
|
|
return await self._append_event(command, self._interaction_events, "interaction")
|
|
|
|
async def query_projection(self, projection_id: str) -> PortRecord | None:
|
|
async with self._lock:
|
|
sources: Mapping[str, Any] = {
|
|
"hub_registry": list(self._registrations.values()),
|
|
"messages": self._messages,
|
|
"progress_events": self._progress_events,
|
|
"interaction_events": self._interaction_events,
|
|
}
|
|
if projection_id not in sources:
|
|
return None
|
|
items = deepcopy(sources[projection_id])
|
|
return self._record(
|
|
projection_id,
|
|
{
|
|
"projection_id": projection_id,
|
|
"items": items,
|
|
"rebuild_from": _rebuild_sources(projection_id),
|
|
},
|
|
)
|
|
|
|
async def get_repository_navigation(self) -> NavigationProjection | None:
|
|
async with self._lock:
|
|
return deepcopy(self._repository_navigation)
|
|
|
|
async def replace_repository_navigation(
|
|
self, projection: NavigationProjection
|
|
) -> None:
|
|
async with self._lock:
|
|
self._repository_navigation = deepcopy(projection)
|
|
|
|
async def mark_repository_navigation_stale(
|
|
self, *, checked_at: datetime, diagnostic: Mapping[str, Any]
|
|
) -> None:
|
|
async with self._lock:
|
|
current = self._repository_navigation
|
|
if current is None:
|
|
return
|
|
self._repository_navigation = NavigationProjection(
|
|
projection_status="stale",
|
|
source_snapshot=deepcopy(current.source_snapshot),
|
|
source_checked_at=checked_at,
|
|
rebuilt_at=current.rebuilt_at,
|
|
content_hash=current.content_hash,
|
|
repositories=deepcopy(current.repositories),
|
|
facets=deepcopy(current.facets),
|
|
diagnostics=(deepcopy(dict(diagnostic)),),
|
|
)
|
|
|
|
async def get_workload_projection(self) -> WorkloadProjection | None:
|
|
async with self._lock:
|
|
return deepcopy(self._workload_projection)
|
|
|
|
async def replace_workload_projection(self, projection: WorkloadProjection) -> None:
|
|
async with self._lock:
|
|
self._workload_projection = deepcopy(projection)
|
|
|
|
async def mark_workload_projection_stale(
|
|
self, *, checked_at: datetime, diagnostic: Mapping[str, Any]
|
|
) -> None:
|
|
async with self._lock:
|
|
current = self._workload_projection
|
|
if current is None:
|
|
return
|
|
self._workload_projection = WorkloadProjection(
|
|
projection_status="stale",
|
|
source_snapshot=deepcopy(current.source_snapshot),
|
|
source_checked_at=checked_at,
|
|
rebuilt_at=current.rebuilt_at,
|
|
content_hash=current.content_hash,
|
|
workloads=deepcopy(current.workloads),
|
|
diagnostics=(deepcopy(dict(diagnostic)),),
|
|
)
|
|
|
|
async def _append_event(
|
|
self,
|
|
command: EventCommand,
|
|
target: list[dict[str, Any]],
|
|
family: str,
|
|
) -> PortAccepted:
|
|
event_id = uuid4()
|
|
value = {
|
|
"id": str(event_id),
|
|
"family": family,
|
|
"recorded_at": _now().isoformat(),
|
|
**command.model_dump(mode="json"),
|
|
}
|
|
async with self._lock:
|
|
target.append(value)
|
|
return PortAccepted(
|
|
id=str(event_id),
|
|
status="accepted",
|
|
correlation_id=command.correlation_id,
|
|
)
|
|
|
|
def _record(self, kind: str, value: dict[str, Any]) -> PortRecord:
|
|
encoded = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
|
|
record_id = str(value.get("id") or kind)
|
|
return PortRecord(
|
|
id=record_id,
|
|
data=deepcopy(value),
|
|
provenance=Provenance(
|
|
source_system="hub-core-memory",
|
|
source_ref=f"memory://{kind}/{record_id}",
|
|
schema_version=CONTRACT_VERSION,
|
|
content_hash=hashlib.sha256(encoded).hexdigest(),
|
|
indexed_at=_now(),
|
|
),
|
|
)
|
|
|
|
|
|
def _now() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
def _rebuild_sources(projection_id: str) -> list[str]:
|
|
return {
|
|
"hub_registry": ["hub_descriptors", "hub_manifests"],
|
|
"messages": ["messages"],
|
|
"progress_events": ["progress_events"],
|
|
"interaction_events": ["interaction_events"],
|
|
}[projection_id]
|