1
1
import logging
2
- from collections .abc import Sequence
2
+ from collections .abc import Mapping , Sequence
3
3
from mimetypes import guess_type
4
+ from typing import Optional
5
+
6
+ from pydantic import BaseModel
4
7
5
8
from configs import dify_config
6
9
from core .helper import marketplace
18
21
from core .plugin .manager .asset import PluginAssetManager
19
22
from core .plugin .manager .debugging import PluginDebuggingManager
20
23
from core .plugin .manager .plugin import PluginInstallationManager
24
+ from extensions .ext_redis import redis_client
21
25
22
26
logger = logging .getLogger (__name__ )
23
27
24
28
25
29
class PluginService :
30
+ class LatestPluginCache (BaseModel ):
31
+ plugin_id : str
32
+ version : str
33
+ unique_identifier : str
34
+
35
+ REDIS_KEY_PREFIX = "plugin_service:latest_plugin:"
36
+ REDIS_TTL = 60 * 5 # 5 minutes
37
+
38
+ @staticmethod
39
+ def fetch_latest_plugin_version (plugin_ids : Sequence [str ]) -> Mapping [str , Optional [LatestPluginCache ]]:
40
+ """
41
+ Fetch the latest plugin version
42
+ """
43
+ result : dict [str , Optional [PluginService .LatestPluginCache ]] = {}
44
+
45
+ try :
46
+ cache_not_exists = []
47
+
48
+ # Try to get from Redis first
49
+ for plugin_id in plugin_ids :
50
+ cached_data = redis_client .get (f"{ PluginService .REDIS_KEY_PREFIX } { plugin_id } " )
51
+ if cached_data :
52
+ result [plugin_id ] = PluginService .LatestPluginCache .model_validate_json (cached_data )
53
+ else :
54
+ cache_not_exists .append (plugin_id )
55
+
56
+ if cache_not_exists :
57
+ manifests = {
58
+ manifest .plugin_id : manifest
59
+ for manifest in marketplace .batch_fetch_plugin_manifests (cache_not_exists )
60
+ }
61
+
62
+ for plugin_id , manifest in manifests .items ():
63
+ latest_plugin = PluginService .LatestPluginCache (
64
+ plugin_id = plugin_id ,
65
+ version = manifest .latest_version ,
66
+ unique_identifier = manifest .latest_package_identifier ,
67
+ )
68
+
69
+ # Store in Redis
70
+ redis_client .setex (
71
+ f"{ PluginService .REDIS_KEY_PREFIX } { plugin_id } " ,
72
+ PluginService .REDIS_TTL ,
73
+ latest_plugin .model_dump_json (),
74
+ )
75
+
76
+ result [plugin_id ] = latest_plugin
77
+
78
+ # pop plugin_id from cache_not_exists
79
+ cache_not_exists .remove (plugin_id )
80
+
81
+ for plugin_id in cache_not_exists :
82
+ result [plugin_id ] = None
83
+
84
+ return result
85
+ except Exception :
86
+ logger .exception ("failed to fetch latest plugin version" )
87
+ return result
88
+
26
89
@staticmethod
27
90
def get_debugging_key (tenant_id : str ) -> str :
28
91
"""
@@ -40,19 +103,19 @@ def list(tenant_id: str) -> list[PluginEntity]:
40
103
plugins = manager .list_plugins (tenant_id )
41
104
plugin_ids = [plugin .plugin_id for plugin in plugins if plugin .source == PluginInstallationSource .Marketplace ]
42
105
try :
43
- manifests = {
44
- manifest .plugin_id : manifest for manifest in marketplace .batch_fetch_plugin_manifests (plugin_ids )
45
- }
106
+ manifests = PluginService .fetch_latest_plugin_version (plugin_ids )
46
107
except Exception :
47
108
manifests = {}
48
109
logger .exception ("failed to fetch plugin manifests" )
49
110
50
111
for plugin in plugins :
51
112
if plugin .source == PluginInstallationSource .Marketplace :
52
113
if plugin .plugin_id in manifests :
53
- # set latest_version
54
- plugin .latest_version = manifests [plugin .plugin_id ].latest_version
55
- plugin .latest_unique_identifier = manifests [plugin .plugin_id ].latest_package_identifier
114
+ latest_plugin_cache = manifests [plugin .plugin_id ]
115
+ if latest_plugin_cache :
116
+ # set latest_version
117
+ plugin .latest_version = latest_plugin_cache .version
118
+ plugin .latest_unique_identifier = latest_plugin_cache .unique_identifier
56
119
57
120
return plugins
58
121
0 commit comments