from dataclasses import dataclass from typing import Any, Dict from clients.edge.errors import is_expected_edge_offline_error, log_edge_failure, summarize_edge_exception from core.cache import cache from core.database import engine, init_database from core.settings import ( AGENT_MD_TEMPLATES_FILE, DATABASE_ECHO, DATABASE_ENGINE, DATABASE_URL_DISPLAY, DEFAULT_AGENTS_MD, DEFAULT_BOT_SYSTEM_TIMEZONE, DEFAULT_IDENTITY_MD, DEFAULT_SOUL_MD, DEFAULT_TOOLS_MD, DEFAULT_USER_MD, PROJECT_ROOT, REDIS_ENABLED, REDIS_PREFIX, REDIS_URL, TOPIC_PRESET_TEMPLATES, TOPIC_PRESETS_TEMPLATES_FILE, load_agent_md_templates, load_topic_presets_template, ) from providers.provision.edge import EdgeProvisionProvider from providers.provision.local import LocalProvisionProvider from providers.registry import ProviderRegistry from providers.runtime.edge import EdgeRuntimeProvider from providers.runtime.local import LocalRuntimeProvider from providers.selector import get_provision_provider, get_runtime_provider from providers.target import ProviderTarget, normalize_provider_target, provider_target_from_config, provider_target_to_dict from providers.workspace.edge import EdgeWorkspaceProvider from providers.workspace.local import LocalWorkspaceProvider from services.app_lifecycle_service import AppLifecycleService from services.bot_channel_service import BotChannelService from services.bot_command_service import BotCommandService from services.bot_config_state_service import BotConfigStateService from services.bot_infra_service import BotInfraService from services.bot_lifecycle_service import BotLifecycleService from services.bot_message_service import BotMessageService from services.bot_query_service import BotQueryService from services.bot_runtime_snapshot_service import BotRuntimeSnapshotService from services.dashboard_auth_service import DashboardAuthService from services.image_service import ImageService from services.node_registry_service import NodeRegistryService from services.platform_activity_service import ( prune_expired_activity_events, record_activity_event, ) from services.platform_settings_service import ( get_chat_pull_page_size, get_platform_settings_snapshot, get_speech_runtime_settings, ) from services.platform_usage_service import ( bind_usage_message, create_usage_request, fail_latest_usage, finalize_usage_from_packet, ) from services.provider_test_service import ProviderTestService from services.runtime_event_service import RuntimeEventService from services.runtime_service import RuntimeService from services.skill_service import SkillService from services.system_service import SystemService from services.topic_runtime import publish_runtime_topic_packet from services.workspace_service import WorkspaceService from bootstrap.app_runtime_support import ( attach_runtime_services, build_image_runtime_service, build_speech_transcription_runtime_service, build_system_runtime_service, include_dashboard_api, reconcile_image_registry, register_provider_runtime, ) @dataclass class AppRuntimeAssembly: dashboard_auth_service: DashboardAuthService system_service: SystemService app_lifecycle_service: AppLifecycleService def assemble_app_runtime( *, app: Any, logger: Any, bots_workspace_root: str, data_root: str, docker_manager: Any, config_manager: Any, speech_service: Any, bot_id_pattern: Any, ) -> AppRuntimeAssembly: node_registry_service = NodeRegistryService() skill_service = SkillService() dashboard_auth_service = DashboardAuthService(engine=engine) provider_registry = ProviderRegistry() bot_infra_service = BotInfraService( app=app, engine=engine, config_manager=config_manager, node_registry_service=node_registry_service, logger=logger, bots_workspace_root=bots_workspace_root, default_soul_md=DEFAULT_SOUL_MD, default_agents_md=DEFAULT_AGENTS_MD, default_user_md=DEFAULT_USER_MD, default_tools_md=DEFAULT_TOOLS_MD, default_identity_md=DEFAULT_IDENTITY_MD, default_bot_system_timezone=DEFAULT_BOT_SYSTEM_TIMEZONE, normalize_provider_target=normalize_provider_target, provider_target_from_config=provider_target_from_config, provider_target_to_dict=provider_target_to_dict, resolve_provider_bundle_key=lambda target: provider_registry.resolve_bundle_key(target), get_provision_provider=get_provision_provider, read_env_store=lambda bot_id: bot_config_state_service.read_env_store(bot_id), read_bot_runtime_snapshot=lambda bot: _read_bot_runtime_snapshot(bot), normalize_media_list=lambda raw, bot_id: _normalize_media_list(raw, bot_id), ) node_registry_service.register_node(bot_infra_service.local_managed_node()) app.state.node_registry_service = node_registry_service _read_bot_config = bot_infra_service.read_bot_config _write_bot_config = bot_infra_service.write_bot_config _default_provider_target = bot_infra_service.default_provider_target _read_bot_provider_target = bot_infra_service.read_bot_provider_target _resolve_bot_provider_target_for_instance = bot_infra_service.resolve_bot_provider_target_for_instance _clear_provider_target_override = bot_infra_service.clear_provider_target_override _apply_provider_target_to_bot = bot_infra_service.apply_provider_target_to_bot _local_managed_node = bot_infra_service.local_managed_node _provider_target_from_node = bot_infra_service.provider_target_from_node _node_display_name = bot_infra_service.node_display_name _node_metadata = bot_infra_service.node_metadata _serialize_provider_target_summary = bot_infra_service.serialize_provider_target_summary _resolve_edge_client = bot_infra_service.resolve_edge_client _resolve_edge_state_context = bot_infra_service.resolve_edge_state_context _read_edge_state_data = bot_infra_service.read_edge_state_data _write_edge_state_data = bot_infra_service.write_edge_state_data _read_bot_resources = bot_infra_service.read_bot_resources _migrate_bot_resources_store = bot_infra_service.migrate_bot_resources_store _normalize_channel_extra = bot_infra_service.normalize_channel_extra _read_global_delivery_flags = bot_infra_service.read_global_delivery_flags _channel_api_to_cfg = bot_infra_service.channel_api_to_cfg _get_bot_channels_from_config = bot_infra_service.get_bot_channels_from_config _normalize_initial_channels = bot_infra_service.normalize_initial_channels _parse_message_media = bot_infra_service.parse_message_media _normalize_env_params = bot_infra_service.normalize_env_params _get_default_system_timezone = bot_infra_service.get_default_system_timezone _normalize_system_timezone = bot_infra_service.normalize_system_timezone _resolve_bot_env_params = bot_infra_service.resolve_bot_env_params _safe_float = bot_infra_service.safe_float _safe_int = bot_infra_service.safe_int _normalize_resource_limits = bot_infra_service.normalize_resource_limits _sync_workspace_channels = bot_infra_service.sync_workspace_channels _set_bot_provider_target = bot_infra_service.set_bot_provider_target _sync_bot_workspace_via_provider = bot_infra_service.sync_bot_workspace_via_provider _workspace_root = bot_infra_service.workspace_root _cron_store_path = bot_infra_service.cron_store_path _env_store_path = bot_infra_service.env_store_path _clear_bot_sessions = bot_infra_service.clear_bot_sessions _clear_bot_dashboard_direct_session = bot_infra_service.clear_bot_dashboard_direct_session _ensure_provider_target_supported = bot_infra_service.ensure_provider_target_supported _resolve_workspace_path = bot_infra_service.resolve_workspace_path _calc_dir_size_bytes = bot_infra_service.calc_dir_size_bytes _is_video_attachment_path = bot_infra_service.is_video_attachment_path _is_visual_attachment_path = bot_infra_service.is_visual_attachment_path bot_config_state_service = BotConfigStateService( read_edge_state_data=_read_edge_state_data, write_edge_state_data=_write_edge_state_data, read_bot_config=_read_bot_config, write_bot_config=_write_bot_config, invalidate_bot_detail_cache=lambda *args, **kwargs: _invalidate_bot_detail_cache(*args, **kwargs), env_store_path=_env_store_path, cron_store_path=_cron_store_path, normalize_env_params=_normalize_env_params, ) def _write_env_store(bot_id: str, env_params: Dict[str, str]) -> None: bot_config_state_service.write_env_store(bot_id, env_params) local_provision_provider = LocalProvisionProvider(sync_workspace_func=_sync_workspace_channels) local_runtime_provider = LocalRuntimeProvider( docker_manager=docker_manager, on_state_change=lambda *args, **kwargs: docker_callback(*args, **kwargs), provision_provider=local_provision_provider, read_runtime_snapshot=lambda *args, **kwargs: _read_bot_runtime_snapshot(*args, **kwargs), resolve_env_params=_resolve_bot_env_params, write_env_store=_write_env_store, invalidate_bot_cache=lambda *args, **kwargs: _invalidate_bot_detail_cache(*args, **kwargs), record_agent_loop_ready_warning=lambda *args, **kwargs: _record_agent_loop_ready_warning(*args, **kwargs), safe_float=_safe_float, safe_int=_safe_int, ) local_workspace_provider = LocalWorkspaceProvider() edge_provision_provider = EdgeProvisionProvider( read_provider_target=_read_bot_provider_target, resolve_edge_client=_resolve_edge_client, read_runtime_snapshot=lambda *args, **kwargs: _read_bot_runtime_snapshot(*args, **kwargs), read_bot_channels=_get_bot_channels_from_config, read_node_metadata=_node_metadata, ) edge_runtime_provider = EdgeRuntimeProvider( read_provider_target=_read_bot_provider_target, resolve_edge_client=_resolve_edge_client, read_runtime_snapshot=lambda *args, **kwargs: _read_bot_runtime_snapshot(*args, **kwargs), resolve_env_params=_resolve_bot_env_params, read_bot_channels=_get_bot_channels_from_config, read_node_metadata=_node_metadata, ) edge_workspace_provider = EdgeWorkspaceProvider( read_provider_target=_read_bot_provider_target, resolve_edge_client=_resolve_edge_client, read_node_metadata=_node_metadata, ) local_provider_target = ProviderTarget( node_id="local", transport_kind="edge", runtime_kind="docker", core_adapter="nanobot", ) register_provider_runtime( app=app, provider_registry=provider_registry, local_provider_target=local_provider_target, local_provision_provider=local_provision_provider, local_runtime_provider=local_runtime_provider, local_workspace_provider=local_workspace_provider, edge_provision_provider=edge_provision_provider, edge_runtime_provider=edge_runtime_provider, edge_workspace_provider=edge_workspace_provider, resolve_bot_provider_target_for_instance=_resolve_bot_provider_target_for_instance, resolve_edge_client=_resolve_edge_client, ) bot_runtime_snapshot_service = BotRuntimeSnapshotService( engine=engine, logger=logger, docker_manager=docker_manager, default_soul_md=DEFAULT_SOUL_MD, default_agents_md=DEFAULT_AGENTS_MD, default_user_md=DEFAULT_USER_MD, default_tools_md=DEFAULT_TOOLS_MD, default_identity_md=DEFAULT_IDENTITY_MD, workspace_root=_workspace_root, resolve_edge_state_context=_resolve_edge_state_context, read_bot_config=_read_bot_config, resolve_bot_env_params=_resolve_bot_env_params, resolve_bot_provider_target_for_instance=_resolve_bot_provider_target_for_instance, read_global_delivery_flags=_read_global_delivery_flags, safe_float=_safe_float, safe_int=_safe_int, get_default_system_timezone=_get_default_system_timezone, read_bot_resources=_read_bot_resources, node_display_name=_node_display_name, get_runtime_provider=get_runtime_provider, invalidate_bot_detail_cache=lambda *args, **kwargs: _invalidate_bot_detail_cache(*args, **kwargs), record_activity_event=record_activity_event, ) _read_bot_runtime_snapshot = bot_runtime_snapshot_service.read_bot_runtime_snapshot _serialize_bot = bot_runtime_snapshot_service.serialize_bot _serialize_bot_list_item = bot_runtime_snapshot_service.serialize_bot_list_item _refresh_bot_runtime_status = bot_runtime_snapshot_service.refresh_bot_runtime_status _record_agent_loop_ready_warning = bot_runtime_snapshot_service.record_agent_loop_ready_warning runtime_event_service = RuntimeEventService( app=app, engine=engine, cache=cache, logger=logger, publish_runtime_topic_packet=publish_runtime_topic_packet, bind_usage_message=bind_usage_message, finalize_usage_from_packet=finalize_usage_from_packet, workspace_root=_workspace_root, parse_message_media=_parse_message_media, ) _normalize_media_list = runtime_event_service.normalize_media_list _persist_runtime_packet = runtime_event_service.persist_runtime_packet _broadcast_runtime_packet = runtime_event_service.broadcast_runtime_packet docker_callback = runtime_event_service.docker_callback _cache_key_bots_list = runtime_event_service.cache_key_bots_list _cache_key_bot_detail = runtime_event_service.cache_key_bot_detail _cache_key_bot_messages = runtime_event_service.cache_key_bot_messages _cache_key_bot_messages_page = runtime_event_service.cache_key_bot_messages_page _serialize_bot_message_row = runtime_event_service.serialize_bot_message_row _resolve_local_day_range = runtime_event_service.resolve_local_day_range _cache_key_images = runtime_event_service.cache_key_images _invalidate_bot_detail_cache = runtime_event_service.invalidate_bot_detail_cache _invalidate_bot_messages_cache = runtime_event_service.invalidate_bot_messages_cache _invalidate_images_cache = runtime_event_service.invalidate_images_cache bot_command_service = BotCommandService( read_runtime_snapshot=_read_bot_runtime_snapshot, normalize_media_list=_normalize_media_list, resolve_workspace_path=_resolve_workspace_path, is_visual_attachment_path=_is_visual_attachment_path, is_video_attachment_path=_is_video_attachment_path, create_usage_request=create_usage_request, record_activity_event=record_activity_event, fail_latest_usage=fail_latest_usage, persist_runtime_packet=_persist_runtime_packet, get_main_loop=lambda app_state: getattr(app_state, "main_loop", None), broadcast_packet=_broadcast_runtime_packet, ) workspace_service = WorkspaceService() runtime_service = RuntimeService( command_service=bot_command_service, resolve_runtime_provider=get_runtime_provider, clear_bot_sessions=_clear_bot_sessions, clear_dashboard_direct_session_file=_clear_bot_dashboard_direct_session, invalidate_bot_detail_cache=_invalidate_bot_detail_cache, invalidate_bot_messages_cache=_invalidate_bot_messages_cache, record_activity_event=record_activity_event, ) app_lifecycle_service = AppLifecycleService( app=app, engine=engine, cache=cache, logger=logger, project_root=PROJECT_ROOT, database_engine=DATABASE_ENGINE, database_echo=DATABASE_ECHO, database_url_display=DATABASE_URL_DISPLAY, redis_enabled=REDIS_ENABLED, init_database=init_database, node_registry_service=node_registry_service, local_managed_node=_local_managed_node, prune_expired_activity_events=prune_expired_activity_events, migrate_bot_resources_store=_migrate_bot_resources_store, resolve_bot_provider_target_for_instance=_resolve_bot_provider_target_for_instance, default_provider_target=_default_provider_target, set_bot_provider_target=_set_bot_provider_target, apply_provider_target_to_bot=_apply_provider_target_to_bot, normalize_provider_target=normalize_provider_target, runtime_service=runtime_service, runtime_event_service=runtime_event_service, clear_provider_target_overrides=bot_infra_service.clear_provider_target_overrides, ) bot_query_service = BotQueryService( cache=cache, cache_key_bots_list=_cache_key_bots_list, cache_key_bot_detail=_cache_key_bot_detail, refresh_bot_runtime_status=_refresh_bot_runtime_status, serialize_bot=_serialize_bot, serialize_bot_list_item=_serialize_bot_list_item, read_bot_resources=_read_bot_resources, resolve_bot_provider_target=_resolve_bot_provider_target_for_instance, get_runtime_provider=get_runtime_provider, workspace_root=_workspace_root, calc_dir_size_bytes=_calc_dir_size_bytes, logger=logger, ) bot_channel_service = BotChannelService( read_bot_config=_read_bot_config, write_bot_config=_write_bot_config, sync_bot_workspace_via_provider=_sync_bot_workspace_via_provider, invalidate_bot_detail_cache=_invalidate_bot_detail_cache, get_bot_channels_from_config=_get_bot_channels_from_config, normalize_channel_extra=_normalize_channel_extra, channel_api_to_cfg=_channel_api_to_cfg, read_global_delivery_flags=_read_global_delivery_flags, ) bot_message_service = BotMessageService( cache=cache, cache_key_bot_messages=_cache_key_bot_messages, cache_key_bot_messages_page=_cache_key_bot_messages_page, serialize_bot_message_row=_serialize_bot_message_row, resolve_local_day_range=_resolve_local_day_range, invalidate_bot_messages_cache=_invalidate_bot_messages_cache, get_chat_pull_page_size=get_chat_pull_page_size, ) speech_transcription_service = build_speech_transcription_runtime_service( data_root=data_root, speech_service=speech_service, get_speech_runtime_settings=get_speech_runtime_settings, logger=logger, ) image_service = build_image_runtime_service( cache=cache, cache_key_images=_cache_key_images, invalidate_images_cache=_invalidate_images_cache, docker_manager=docker_manager, reconcile_image_registry_fn=lambda session: reconcile_image_registry(session, docker_manager=docker_manager), ) provider_test_service = ProviderTestService() system_service = build_system_runtime_service( engine=engine, cache=cache, database_engine=DATABASE_ENGINE, redis_enabled=REDIS_ENABLED, redis_url=REDIS_URL, redis_prefix=REDIS_PREFIX, agent_md_templates_file=str(AGENT_MD_TEMPLATES_FILE), topic_presets_templates_file=str(TOPIC_PRESETS_TEMPLATES_FILE), default_soul_md=DEFAULT_SOUL_MD, default_agents_md=DEFAULT_AGENTS_MD, default_user_md=DEFAULT_USER_MD, default_tools_md=DEFAULT_TOOLS_MD, default_identity_md=DEFAULT_IDENTITY_MD, topic_preset_templates=TOPIC_PRESET_TEMPLATES, get_default_system_timezone=_get_default_system_timezone, load_agent_md_templates=load_agent_md_templates, load_topic_presets_template=load_topic_presets_template, get_platform_settings_snapshot=get_platform_settings_snapshot, get_speech_runtime_settings=get_speech_runtime_settings, ) bot_lifecycle_service = BotLifecycleService( bot_id_pattern=bot_id_pattern, runtime_service=runtime_service, refresh_bot_runtime_status=_refresh_bot_runtime_status, resolve_bot_provider_target=_resolve_bot_provider_target_for_instance, provider_target_from_node=_provider_target_from_node, default_provider_target=_default_provider_target, ensure_provider_target_supported=_ensure_provider_target_supported, require_ready_image=image_service.require_ready_image, sync_bot_workspace_via_provider=_sync_bot_workspace_via_provider, apply_provider_target_to_bot=_apply_provider_target_to_bot, serialize_provider_target_summary=_serialize_provider_target_summary, serialize_bot=_serialize_bot, node_display_name=_node_display_name, invalidate_bot_detail_cache=_invalidate_bot_detail_cache, record_activity_event=record_activity_event, normalize_env_params=_normalize_env_params, normalize_system_timezone=_normalize_system_timezone, normalize_resource_limits=_normalize_resource_limits, write_env_store=_write_env_store, resolve_bot_env_params=_resolve_bot_env_params, clear_provider_target_override=_clear_provider_target_override, normalize_initial_channels=_normalize_initial_channels, is_expected_edge_offline_error=is_expected_edge_offline_error, summarize_edge_exception=summarize_edge_exception, resolve_edge_client=_resolve_edge_client, node_metadata=_node_metadata, log_edge_failure=log_edge_failure, invalidate_bot_messages_cache=_invalidate_bot_messages_cache, logger=logger, ) attach_runtime_services( app=app, bot_command_service=bot_command_service, bot_lifecycle_service=bot_lifecycle_service, app_lifecycle_service=app_lifecycle_service, bot_query_service=bot_query_service, bot_channel_service=bot_channel_service, bot_message_service=bot_message_service, bot_runtime_snapshot_service=bot_runtime_snapshot_service, image_service=image_service, provider_test_service=provider_test_service, runtime_event_service=runtime_event_service, speech_transcription_service=speech_transcription_service, system_service=system_service, workspace_service=workspace_service, runtime_service=runtime_service, ) include_dashboard_api( app=app, image_service=image_service, provider_test_service=provider_test_service, bot_lifecycle_service=bot_lifecycle_service, bot_query_service=bot_query_service, bot_channel_service=bot_channel_service, skill_service=skill_service, bot_config_state_service=bot_config_state_service, runtime_service=runtime_service, bot_message_service=bot_message_service, workspace_service=workspace_service, speech_transcription_service=speech_transcription_service, app_lifecycle_service=app_lifecycle_service, resolve_edge_state_context=_resolve_edge_state_context, logger=logger, ) return AppRuntimeAssembly( dashboard_auth_service=dashboard_auth_service, system_service=system_service, app_lifecycle_service=app_lifecycle_service, )