68 lines
2.6 KiB
Python
68 lines
2.6 KiB
Python
"""Runtime orchestrator joining Search Gateway, registry, audit and events."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
import os
|
|
|
|
from .search_audit_store import SearchAuditStore
|
|
from .search_event_bridge import SearchEventBridge
|
|
from .search_gateway import SearchGateway, SearchRequest
|
|
from .search_provider_registry import SearchProviderRegistry
|
|
from .trace_tool_credential_governance import (
|
|
GovernanceActor,
|
|
GovernanceScope,
|
|
GovernanceStore,
|
|
ToolDescriptor,
|
|
TraceContext,
|
|
)
|
|
|
|
|
|
class SearchGatewayOrchestrator:
|
|
def __init__(self, *, governance_store: GovernanceStore | None = None):
|
|
self.registry = SearchProviderRegistry()
|
|
self.audit = SearchAuditStore()
|
|
self.events = SearchEventBridge()
|
|
self.governance = governance_store or GovernanceStore(
|
|
os.environ.get("AIOS_SEARCH_GOVERNANCE_DB", "runtime/search-governance.sqlite3")
|
|
)
|
|
registrar = GovernanceActor(
|
|
"search-runtime-bootstrap", GovernanceScope("system"), ("system.admin",)
|
|
)
|
|
for provider in self.registry.ordered():
|
|
self.governance.register_tool(
|
|
ToolDescriptor(
|
|
f"search.{provider.name}", "tool", GovernanceScope("global"), ("tool.search",)
|
|
),
|
|
registrar,
|
|
)
|
|
self.gateway = SearchGateway(self.registry.ordered(), governance_store=self.governance)
|
|
|
|
def execute(self, payload: dict[str, Any], *, actor: GovernanceActor | dict[str, Any] | None = None, trace: TraceContext | None = None) -> dict[str, Any]:
|
|
if actor is None or trace is None:
|
|
from .trace_tool_credential_governance import GovernanceError
|
|
raise GovernanceError("TOOL_MCP_AUTHZ_DENY")
|
|
self.governance.record_trace(trace, actor)
|
|
request = SearchRequest(**payload)
|
|
self.events.search_started(request.to_dict())
|
|
response = self.gateway.search(request, actor=actor, trace=trace)
|
|
result = response.to_dict()
|
|
for attempt in result.get("attempts", []):
|
|
self.events.provider_attempted(result["request_id"], attempt)
|
|
if len(result.get("attempts", [])) > 1:
|
|
self.events.fallback_triggered(result["request_id"], "multiple providers attempted")
|
|
self.audit.append(result)
|
|
self.events.search_completed(result)
|
|
return result
|
|
|
|
def status(self) -> dict[str, Any]:
|
|
return {
|
|
"registry": self.registry.status(),
|
|
"audit": "enabled",
|
|
"events": "enabled",
|
|
"governance": "required",
|
|
}
|
|
|
|
|
|
__all__ = ["SearchGatewayOrchestrator"]
|