Skip to main content

mofa_foundation/prompt/
hot_reload.rs

1//! Hot-reload support for Prompt templates
2//!
3//! 基于 mofa-plugins 提供的热重载机制,实现 Prompt 模板的自动更新
4
5use super::plugin::{PromptTemplatePlugin, RhaiScriptPromptPlugin};
6// 只导入明确需要的类型,避免冲突
7use mofa_plugins::hot_reload::{
8    // 不导入 HotReloadConfig,而是在代码中直接指定类型
9    HotReloadManager,
10    ReloadEvent,
11};
12use std::path::Path;
13use std::sync::Arc;
14use tokio::sync::RwLock;
15use tracing::{info, warn};
16
17/// 支持热重载的 Rhai 脚本 Prompt 模板插件
18pub struct HotReloadableRhaiPromptPlugin {
19    /// 内部 Prompt 插件
20    inner: Arc<RwLock<RhaiScriptPromptPlugin>>,
21    /// 热重载管理器
22    hot_reload_manager: Arc<RwLock<HotReloadManager>>,
23}
24
25impl HotReloadableRhaiPromptPlugin {
26    /// 创建新的支持热重载的 Prompt 模板插件
27    pub async fn new(script_path: impl AsRef<Path>) -> Self {
28        let script_path = script_path.as_ref().to_path_buf();
29        let inner = Arc::new(RwLock::new(RhaiScriptPromptPlugin::new(&script_path)));
30
31        // 初始化热重载配置 - 使用 mofa_plugins 的配置格式
32        let plugin_reload_config = mofa_plugins::hot_reload::HotReloadConfig::default();
33
34        // 创建热重载管理器
35        let hot_reload_manager = HotReloadManager::new(plugin_reload_config);
36
37        // 加载初始模板 - 暂时不实现
38        {
39            // 这里可以添加未来的模板加载逻辑
40        }
41
42        // 创建并返回实例
43        Self {
44            inner,
45            hot_reload_manager: Arc::new(RwLock::new(hot_reload_manager)),
46        }
47    }
48
49    /// 启动热重载监听
50    pub async fn start_reload_watcher(&self) {
51        let script_path = {
52            let inner_guard = self.inner.read().await;
53            inner_guard.script_path().to_path_buf()
54        };
55
56        let inner_clone = self.inner.clone();
57
58        // 添加目录监听
59        {
60            let manager_guard = self.hot_reload_manager.write().await;
61            if let Err(e) = manager_guard.add_watch_path(&script_path).await {
62                warn!("Failed to add watch path: {}", e);
63                return;
64            }
65        }
66
67        // 启动热重载管理器
68        {
69            let mut manager_guard = self.hot_reload_manager.write().await;
70            if let Err(e) = manager_guard.start().await {
71                warn!("Failed to start hot-reload manager: {}", e);
72                return;
73            }
74        }
75
76        info!(
77            "Hot-reload prompt template watcher started for path: {:?}",
78            script_path
79        );
80
81        // 订阅热重载事件
82        let mut event_subscriber = self.hot_reload_manager.read().await.subscribe();
83
84        // 处理热重载事件
85        tokio::spawn(async move {
86            while let Ok(event) = event_subscriber.recv().await {
87                match event {
88                    ReloadEvent::ReloadCompleted {
89                        plugin_id,
90                        path,
91                        duration,
92                        .. // 忽略 success 字段,因为我们当前不使用它
93                    } => {
94                        info!(
95                            "Plugin {} reloaded in {:?} from path {:?}",
96                            plugin_id, duration, path
97                        );
98                        // 刷新模板
99                        let inner_guard = inner_clone.write().await;
100                        if let Err(e) = inner_guard.refresh_templates().await {
101                            warn!("Failed to refresh templates: {}", e);
102                        }
103                    }
104
105                    ReloadEvent::ReloadFailed {
106                        plugin_id,
107                        path,
108                        error,
109                        attempt,
110                    } => {
111                        warn!(
112                            "Plugin {} reload failed (attempt {}): {} at path {:?}",
113                            plugin_id, attempt, error, path
114                        );
115                    }
116
117                    ReloadEvent::PluginDiscovered { path } => {
118                        info!("New plugin discovered at path {:?}", path);
119                        // 刷新模板
120                        let inner_guard = inner_clone.write().await;
121                        if let Err(e) = inner_guard.refresh_templates().await {
122                            warn!("Failed to refresh templates: {}", e);
123                        }
124                    }
125
126                    ReloadEvent::PluginRemoved {
127                        plugin_id,
128                        path,
129                    } => {
130                        info!(
131                            "Plugin {} removed from path {:?}",
132                            plugin_id, path
133                        );
134                        // 刷新模板
135                        let inner_guard = inner_clone.write().await;
136                        if let Err(e) = inner_guard.refresh_templates().await {
137                            warn!("Failed to refresh templates: {}", e);
138                        }
139                    }
140
141                    _ => {} // Ignore other events
142                }
143            }
144        });
145    }
146
147    /// 停止热重载监听
148    pub async fn stop_reload_watcher(&self) {
149        let mut manager_guard = self.hot_reload_manager.write().await;
150        if let Err(e) = manager_guard.stop().await {
151            warn!("Failed to stop hot-reload manager: {}", e);
152        }
153    }
154
155    /// 获取内部插件实例
156    pub async fn inner(&self) -> Arc<RwLock<RhaiScriptPromptPlugin>> {
157        self.inner.clone()
158    }
159
160    /// 设置当前活动的场景
161    pub async fn set_active_scenario(&self, scenario: impl Into<String>) {
162        let scenario = scenario.into();
163        info!("Switching to scenario: {}", scenario);
164
165        let inner_guard = self.inner.write().await;
166        inner_guard.set_active_scenario(scenario).await;
167    }
168}
169
170#[async_trait::async_trait]
171impl super::plugin::PromptTemplatePlugin for HotReloadableRhaiPromptPlugin {
172    async fn get_prompt_template(
173        &self,
174        scenario: &str,
175    ) -> Option<Arc<super::template::PromptTemplate>> {
176        let inner_guard = self.inner.read().await;
177        inner_guard.get_prompt_template(scenario).await
178    }
179
180    async fn get_active_scenario(&self) -> String {
181        let inner_guard = self.inner.read().await;
182        inner_guard.get_active_scenario().await
183    }
184
185    async fn set_active_scenario(&self, scenario: &str) {
186        let inner_guard = self.inner.write().await;
187        inner_guard.set_active_scenario(scenario).await;
188    }
189
190    async fn get_available_scenarios(&self) -> Vec<String> {
191        let inner_guard = self.inner.read().await;
192        inner_guard.get_available_scenarios().await
193    }
194
195    async fn refresh_templates(&self) -> mofa_kernel::plugin::PluginResult<()> {
196        let inner_guard = self.inner.write().await;
197        inner_guard.refresh_templates().await
198    }
199}