This commit is contained in:
lda
2026-06-02 18:39:21 +07:00 Verified
parent fd66081070
commit 83d97ecd62
8 changed files with 919 additions and 52 deletions
+12 -43
View File
@@ -40,6 +40,7 @@ from wf_api.saved_subgraphs import SavedSubgraphTree
from ..admin_capabilities import admin_source
from ..catalog import CombinedCatalog
from .builtins import builtin_sources
from .events import BrokerEventRecorder
from .source_catalog import SourceCatalogService
from .upstream_transport import UpstreamTransportService
from .workflow_runtime import WorkflowRuntimeService
@@ -56,6 +57,7 @@ class WfMcpService:
draft_workspace_store: DraftWorkspaceStore | None = None
run_store: RunStore | None = None
tool_executor: ToolExecutor | None = None
events: BrokerEventRecorder = field(init=False)
upstream: UpstreamTransportService = field(init=False)
source_catalog: SourceCatalogService = field(init=False)
workflow_runtime: WorkflowRuntimeService = field(init=False)
@@ -67,9 +69,10 @@ class WfMcpService:
must not guess workflow persistence from the MCP catalog/auth store because
CLI, MCP, and future HTTP frontends may share or swap those stores.
"""
self.events = BrokerEventRecorder(self.event_bus)
self.upstream = UpstreamTransportService(
store=self.store,
event_sink=self._record_event,
event_sink=self.events.record_event,
tool_executor=self.tool_executor,
)
self.source_catalog = SourceCatalogService(
@@ -79,7 +82,7 @@ class WfMcpService:
connection_list_all=self.connections.list_all,
tool_executor_for=self.upstream.tool_executor_for,
load_auth=self.upstream.load_auth,
emit_event=self._record_event,
emit_event=self.events.record_event,
default_catalog_max_age_seconds=self.default_catalog_max_age_seconds,
)
if self.include_builtin_specs:
@@ -89,7 +92,7 @@ class WfMcpService:
self.workflow_runtime = WorkflowRuntimeService(
source_catalog=self.source_catalog,
artifact_store=self.artifact_store,
emit_event=self._record_event,
emit_event=self.events.record_event,
)
@property
@@ -412,7 +415,7 @@ class WfMcpService:
)
def list_events(self) -> list[McpEvent]:
return self.event_bus.list_events()
return self.events.list_events()
def register_capability_source(self, source: CapabilitySource) -> None:
"""Register a capability source as canonical service state."""
@@ -422,7 +425,7 @@ class WfMcpService:
return self.source_catalog.get_qualified_spec(qualified_name)
def _record_event(self, event: McpEvent) -> None:
self.event_bus.publish(event)
self.events.record_event(event)
def _record_catalog_change_events(
self,
@@ -432,42 +435,8 @@ class WfMcpService:
reason: str,
) -> None:
"""Emit local change events that future MCP notifications can project."""
counts = {
"node_count": len(snapshot.nodes),
"resource_count": len(snapshot.resources),
"prompt_count": len(snapshot.prompts),
}
if snapshot.nodes:
self._record_event(
make_event(
"tools_changed",
connection_id=connection_id,
payload={"reason": reason, "node_count": counts["node_count"]},
)
)
if snapshot.resources:
self._record_event(
make_event(
"resources_changed",
connection_id=connection_id,
payload={
"reason": reason,
"resource_count": counts["resource_count"],
},
)
)
if snapshot.prompts:
self._record_event(
make_event(
"prompts_changed",
connection_id=connection_id,
payload={"reason": reason, "prompt_count": counts["prompt_count"]},
)
)
self._record_event(
make_event(
"catalog_changed",
connection_id=connection_id,
payload={"reason": reason, **counts},
)
self.events.record_catalog_change_events(
connection_id,
snapshot,
reason=reason,
)
+84
View File
@@ -0,0 +1,84 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from wf_mcp.events import EventBus, McpEvent, make_event
from wf_mcp.models import CatalogSnapshot
@dataclass(slots=True)
class BrokerEventRecorder:
"""Broker-local event recorder backed by the existing EventBus.
This class records and fans out local service events. MCP notifications are
still projected by subscribers/resources elsewhere; this is only the broker
event emission boundary.
"""
event_bus: EventBus
def record_event(self, event: McpEvent) -> None:
self.event_bus.publish(event)
def record_kind(
self,
event_type: str,
*,
connection_id: str | None = None,
capability_id: str | None = None,
workflow_name: str | None = None,
payload: dict[str, Any] | None = None,
) -> None:
self.record_event(
make_event(
event_type,
connection_id=connection_id,
capability_id=capability_id,
workflow_name=workflow_name,
payload=payload or {},
)
)
def list_events(self) -> list[McpEvent]:
return self.event_bus.list_events()
def record_catalog_change_events(
self,
connection_id: str,
snapshot: CatalogSnapshot,
*,
reason: str,
) -> None:
"""Emit local change events that future MCP notifications can project."""
counts = {
"node_count": len(snapshot.nodes),
"resource_count": len(snapshot.resources),
"prompt_count": len(snapshot.prompts),
}
if snapshot.nodes:
self.record_kind(
"tools_changed",
connection_id=connection_id,
payload={"reason": reason, "node_count": counts["node_count"]},
)
if snapshot.resources:
self.record_kind(
"resources_changed",
connection_id=connection_id,
payload={
"reason": reason,
"resource_count": counts["resource_count"],
},
)
if snapshot.prompts:
self.record_kind(
"prompts_changed",
connection_id=connection_id,
payload={"reason": reason, "prompt_count": counts["prompt_count"]},
)
self.record_kind(
"catalog_changed",
connection_id=connection_id,
payload={"reason": reason, **counts},
)
@@ -14,20 +14,19 @@ from wf_api.operation_context import (
WorkflowRuntimeRunner,
WorkflowSpecProvider,
)
from wf_mcp.events import make_event
from .core import WfMcpService
from .events import BrokerEventRecorder
from .workflow_runtime import WorkflowRuntimeService
@dataclass(frozen=True, slots=True)
class WfMcpWorkflowEventRecorder(WorkflowEventRecorder):
"""Adapter-owned event recorder backed by WfMcpService."""
"""Adapter-owned event recorder backed by BrokerEventRecorder."""
service: WfMcpService
events: BrokerEventRecorder
def record_event(self, event: Any) -> None:
self.service._record_event(event) # noqa: SLF001
self.events.record_event(event)
def record_workflow_event(
self,
@@ -36,8 +35,10 @@ class WfMcpWorkflowEventRecorder(WorkflowEventRecorder):
capability_id: str,
payload: dict[str, Any],
) -> None:
self.service._record_event( # noqa: SLF001
make_event(event_type, capability_id=capability_id, payload=payload)
self.events.record_kind(
event_type,
capability_id=capability_id,
payload=payload,
)
@@ -136,7 +137,7 @@ def context_from_service(service: WfMcpService) -> WorkflowOperationContext:
draft_workspace_store=service.draft_workspace_store,
run_store=service.run_store,
capability_sources=specs.capability_sources,
events=WfMcpWorkflowEventRecorder(service),
events=WfMcpWorkflowEventRecorder(service.events),
specs=specs,
artifacts=WfMcpWorkflowArtifactCataloger(service),
runtime=WfMcpWorkflowRuntimeRunner(service.workflow_runtime),