Files
nanobot/nanobot/webui/gateway_services.py
T
chengyongruandGitHub 462a0dfb0f refactor(channels): make built-in channels self-contained (#4908)
* refactor(channels): own setup and instance contracts

* refactor(channels): isolate management contracts

* refactor(channels): normalize activation contracts

* fix(channels): enforce management contracts

* refactor(channels): finish setup ownership migration

* fix(channels): harden management contracts

* fix(channels): enforce lazy loading and runtime ownership

* fix(feishu): make multi-instance startup idempotent

* fix(webui): render channel setup contracts cleanly

* fix(feishu): stop websocket clients cleanly

* fix(channels): enforce persistence and activation gates

* fix(channels): preserve global feature action scope

* fix(channels): apply defaults for single plugins

* fix(channels): enforce management contract boundaries

* refactor(feishu): remove identity helper indirection

* fix(channels): preserve management setup contracts

* refactor(channels): generalize instance settings UI

* refactor(channels): package channel plugins with web UI metadata

* refactor(channels): make built-ins self-contained packages

* test(channels): colocate tests with channel packages

* fix(dingtalk): use official brand icon

* feat(channels): colocate webui translations

* docs(channels): clarify plugin ownership

* test(exec): remove output wait race

* refactor(channels): unify plugin descriptors

* fix(channels): enforce descriptor-owned contracts

* refactor(channels): finish package-owned plugin setup

* refactor(channels): use repository-owned packages only

* fix(channels): self-describe dependencies and runtime state

* fix(channels): warn about legacy entry points
2026-07-19 23:30:49 +08:00

112 lines
4.0 KiB
Python

"""Composition helpers for the embedded WebUI gateway."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable
from loguru import logger as default_logger
from nanobot.webui.gateway_tokens import GatewayTokenStore
from nanobot.webui.ingress_policy import DEFAULT_WEBUI_INGRESS_POLICY, WebUIIngressPolicy
from nanobot.webui.media_gateway import WebUIMediaGateway
from nanobot.webui.transcript import WebUITranscriptRecorder
from nanobot.webui.workspaces import WebUIWorkspaceController
from nanobot.webui.ws_http import GatewayHTTPHandler
@dataclass(frozen=True)
class GatewayServices:
"""Explicit dependencies shared by WebSocket transport and HTTP routes."""
http: GatewayHTTPHandler
tokens: GatewayTokenStore
media: WebUIMediaGateway
ingress: WebUIIngressPolicy
transcripts: WebUITranscriptRecorder
workspaces: WebUIWorkspaceController
session_manager: Any | None
cron_service: Any | None
local_trigger_store: Any | None
cron_pending_job_ids: Callable[[str], set[str]] | None
local_trigger_pending_ids: Callable[[str], set[str]] | None
def build_gateway_services(
*,
config: Any,
bus: Any,
session_manager: Any | None,
static_dist_path: Path | None,
workspace_path: Path,
default_restrict_to_workspace: bool,
runtime_model_name: Any | None,
runtime_surface: str,
runtime_capabilities_overrides: dict[str, Any] | None,
disabled_skills: set[str] | None = None,
cron_service: Any | None = None,
local_trigger_store: Any | None = None,
cron_pending_job_ids: Callable[[str], set[str]] | None = None,
local_trigger_pending_ids: Callable[[str], set[str]] | None = None,
channel_feature_action: Callable[..., Any] | None = None,
channel_runtime_status: Callable[[], dict[str, Any]] | None = None,
logger: Any = default_logger,
) -> GatewayServices:
tokens = GatewayTokenStore()
ingress = DEFAULT_WEBUI_INGRESS_POLICY
minimum_frame_bytes = ingress.minimum_full_policy_frame_bytes()
if config.max_message_bytes < minimum_frame_bytes:
logger.warning(
"WebSocket maxMessageBytes={} is below the WebUI ingress policy capacity={}; "
"policy-valid messages may still hit the transport frame guard",
config.max_message_bytes,
minimum_frame_bytes,
)
media = WebUIMediaGateway(
workspace_path=workspace_path,
logger=logger,
attachment_limits=ingress.attachments,
)
transcripts = WebUITranscriptRecorder(log=logger)
workspaces = WebUIWorkspaceController(
session_manager=session_manager,
default_workspace=workspace_path,
default_restrict_to_workspace=default_restrict_to_workspace,
)
http = GatewayHTTPHandler(
config=config,
session_manager=session_manager,
static_dist_path=static_dist_path,
runtime_model_name=runtime_model_name,
runtime_surface=runtime_surface,
runtime_capabilities_overrides=runtime_capabilities_overrides,
bus=bus,
tokens=tokens,
media=media,
ingress=ingress,
workspaces=workspaces,
skills_workspace_path=workspace_path,
disabled_skills=disabled_skills,
cron_service=cron_service,
local_trigger_store=local_trigger_store,
cron_pending_job_ids=cron_pending_job_ids,
local_trigger_pending_ids=local_trigger_pending_ids,
channel_feature_action=channel_feature_action,
channel_runtime_status=channel_runtime_status,
log=logger,
)
return GatewayServices(
http=http,
tokens=tokens,
media=media,
ingress=ingress,
transcripts=transcripts,
workspaces=workspaces,
session_manager=session_manager,
cron_service=cron_service,
local_trigger_store=local_trigger_store,
cron_pending_job_ids=cron_pending_job_ids,
local_trigger_pending_ids=local_trigger_pending_ids,
)