nanobot-voice-interface/web_runtime.py

129 lines
4.1 KiB
Python
Raw Normal View History

from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from card_store import (
delete_cards_for_chat,
ensure_card_store_dirs,
sync_templates_context_file,
)
from inbox_service import InboxService
from nanobot_api_client import send_nanobot_api_request
from rtc_manager import RtcSessionManager
from session_store import ensure_session_store_dirs
from supertonic_gateway import SuperTonicGateway
from tool_job_service import ToolJobService
from ui_event_bus import UiEventBus
from workbench_store import ensure_workbench_store_dirs
def normalize_event_chat_id(normalize_chat_id, raw_chat_id: Any) -> str | None:
chat_id = normalize_chat_id(str(raw_chat_id or ""))
return chat_id or None
@dataclass(slots=True)
class WebAppRuntime:
gateway: SuperTonicGateway
event_bus: UiEventBus
inbox_service: InboxService
tool_job_service: ToolJobService
rtc_manager: RtcSessionManager | None
list_sessions: Callable[[], list[dict[str, Any]]]
normalize_chat_id: Callable[[str], str]
def open_inbox_items(self, *, limit: int = 4) -> list[dict[str, Any]]:
return self.inbox_service.open_items(limit=limit)
async def publish_sessions_changed(self) -> None:
await self.event_bus.publish(
{
"type": "sessions.changed",
"sessions": self.list_sessions(),
},
broadcast=True,
)
async def publish_cards_changed(self, chat_id: Any = None) -> None:
target_chat_id = normalize_event_chat_id(self.normalize_chat_id, chat_id)
await self.event_bus.publish(
{"type": "cards.changed", "chat_id": target_chat_id},
chat_id=target_chat_id,
broadcast=target_chat_id is None,
)
async def publish_workbench_changed(self, chat_id: Any = None) -> None:
target_chat_id = normalize_event_chat_id(self.normalize_chat_id, chat_id)
await self.event_bus.publish(
{"type": "workbench.changed", "chat_id": target_chat_id},
chat_id=target_chat_id,
broadcast=target_chat_id is None,
)
async def publish_inbox_changed(self) -> None:
await self.event_bus.publish(
{
"type": "inbox.changed",
"items": self.open_inbox_items(),
},
broadcast=True,
)
async def shutdown(self) -> None:
await self.tool_job_service.shutdown()
if self.rtc_manager is not None:
await self.rtc_manager.shutdown()
await self.gateway.shutdown()
@staticmethod
def delete_cards_for_chat(chat_id: str) -> int:
return delete_cards_for_chat(chat_id)
def create_web_runtime(
*,
repo_dir: Path,
inbox_dir: Path,
tool_job_timeout_seconds: float,
tool_job_retention_seconds: float,
list_sessions,
normalize_chat_id,
) -> WebAppRuntime:
ensure_card_store_dirs()
ensure_session_store_dirs()
ensure_workbench_store_dirs()
inbox_dir.mkdir(parents=True, exist_ok=True)
sync_templates_context_file()
gateway = SuperTonicGateway()
event_bus = UiEventBus()
inbox_service = InboxService(repo_dir=repo_dir, inbox_dir=inbox_dir)
tool_job_service = ToolJobService(
send_request=lambda method, params: send_nanobot_api_request(
method,
params,
timeout_seconds=tool_job_timeout_seconds,
),
timeout_seconds=tool_job_timeout_seconds,
retention_seconds=tool_job_retention_seconds,
)
runtime = WebAppRuntime(
gateway=gateway,
event_bus=event_bus,
inbox_service=inbox_service,
tool_job_service=tool_job_service,
rtc_manager=None,
list_sessions=list_sessions,
normalize_chat_id=normalize_chat_id,
)
runtime.rtc_manager = RtcSessionManager(
gateway=gateway,
publish_cards_changed=runtime.publish_cards_changed,
publish_workbench_changed=runtime.publish_workbench_changed,
publish_sessions_changed=runtime.publish_sessions_changed,
)
return runtime