| import logging | import logging | ||||
| from collections.abc import Sequence | |||||
| from collections.abc import Mapping, Sequence | |||||
| from mimetypes import guess_type | from mimetypes import guess_type | ||||
| from typing import Optional | |||||
| from pydantic import BaseModel | |||||
| from configs import dify_config | from configs import dify_config | ||||
| from core.helper import marketplace | from core.helper import marketplace | ||||
| from core.plugin.manager.asset import PluginAssetManager | from core.plugin.manager.asset import PluginAssetManager | ||||
| from core.plugin.manager.debugging import PluginDebuggingManager | from core.plugin.manager.debugging import PluginDebuggingManager | ||||
| from core.plugin.manager.plugin import PluginInstallationManager | from core.plugin.manager.plugin import PluginInstallationManager | ||||
| from extensions.ext_redis import redis_client | |||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
| class PluginService: | class PluginService: | ||||
| class LatestPluginCache(BaseModel): | |||||
| plugin_id: str | |||||
| version: str | |||||
| unique_identifier: str | |||||
| REDIS_KEY_PREFIX = "plugin_service:latest_plugin:" | |||||
| REDIS_TTL = 60 * 5 # 5 minutes | |||||
| @staticmethod | |||||
| def fetch_latest_plugin_version(plugin_ids: Sequence[str]) -> Mapping[str, Optional[LatestPluginCache]]: | |||||
| """ | |||||
| Fetch the latest plugin version | |||||
| """ | |||||
| result: dict[str, Optional[PluginService.LatestPluginCache]] = {} | |||||
| try: | |||||
| cache_not_exists = [] | |||||
| # Try to get from Redis first | |||||
| for plugin_id in plugin_ids: | |||||
| cached_data = redis_client.get(f"{PluginService.REDIS_KEY_PREFIX}{plugin_id}") | |||||
| if cached_data: | |||||
| result[plugin_id] = PluginService.LatestPluginCache.model_validate_json(cached_data) | |||||
| else: | |||||
| cache_not_exists.append(plugin_id) | |||||
| if cache_not_exists: | |||||
| manifests = { | |||||
| manifest.plugin_id: manifest | |||||
| for manifest in marketplace.batch_fetch_plugin_manifests(cache_not_exists) | |||||
| } | |||||
| for plugin_id, manifest in manifests.items(): | |||||
| latest_plugin = PluginService.LatestPluginCache( | |||||
| plugin_id=plugin_id, | |||||
| version=manifest.latest_version, | |||||
| unique_identifier=manifest.latest_package_identifier, | |||||
| ) | |||||
| # Store in Redis | |||||
| redis_client.setex( | |||||
| f"{PluginService.REDIS_KEY_PREFIX}{plugin_id}", | |||||
| PluginService.REDIS_TTL, | |||||
| latest_plugin.model_dump_json(), | |||||
| ) | |||||
| result[plugin_id] = latest_plugin | |||||
| # pop plugin_id from cache_not_exists | |||||
| cache_not_exists.remove(plugin_id) | |||||
| for plugin_id in cache_not_exists: | |||||
| result[plugin_id] = None | |||||
| return result | |||||
| except Exception: | |||||
| logger.exception("failed to fetch latest plugin version") | |||||
| return result | |||||
| @staticmethod | @staticmethod | ||||
| def get_debugging_key(tenant_id: str) -> str: | def get_debugging_key(tenant_id: str) -> str: | ||||
| """ | """ | ||||
| plugins = manager.list_plugins(tenant_id) | plugins = manager.list_plugins(tenant_id) | ||||
| plugin_ids = [plugin.plugin_id for plugin in plugins if plugin.source == PluginInstallationSource.Marketplace] | plugin_ids = [plugin.plugin_id for plugin in plugins if plugin.source == PluginInstallationSource.Marketplace] | ||||
| try: | try: | ||||
| manifests = { | |||||
| manifest.plugin_id: manifest for manifest in marketplace.batch_fetch_plugin_manifests(plugin_ids) | |||||
| } | |||||
| manifests = PluginService.fetch_latest_plugin_version(plugin_ids) | |||||
| except Exception: | except Exception: | ||||
| manifests = {} | manifests = {} | ||||
| logger.exception("failed to fetch plugin manifests") | logger.exception("failed to fetch plugin manifests") | ||||
| for plugin in plugins: | for plugin in plugins: | ||||
| if plugin.source == PluginInstallationSource.Marketplace: | if plugin.source == PluginInstallationSource.Marketplace: | ||||
| if plugin.plugin_id in manifests: | if plugin.plugin_id in manifests: | ||||
| # set latest_version | |||||
| plugin.latest_version = manifests[plugin.plugin_id].latest_version | |||||
| plugin.latest_unique_identifier = manifests[plugin.plugin_id].latest_package_identifier | |||||
| latest_plugin_cache = manifests[plugin.plugin_id] | |||||
| if latest_plugin_cache: | |||||
| # set latest_version | |||||
| plugin.latest_version = latest_plugin_cache.version | |||||
| plugin.latest_unique_identifier = latest_plugin_cache.unique_identifier | |||||
| return plugins | return plugins | ||||