mofa_foundation/prompt/
hot_reload.rs1use super::plugin::{PromptTemplatePlugin, RhaiScriptPromptPlugin};
6use mofa_plugins::hot_reload::{
8 HotReloadManager,
10 ReloadEvent,
11};
12use std::path::Path;
13use std::sync::Arc;
14use tokio::sync::RwLock;
15use tracing::{info, warn};
16
17pub struct HotReloadableRhaiPromptPlugin {
19 inner: Arc<RwLock<RhaiScriptPromptPlugin>>,
21 hot_reload_manager: Arc<RwLock<HotReloadManager>>,
23}
24
25impl HotReloadableRhaiPromptPlugin {
26 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 let plugin_reload_config = mofa_plugins::hot_reload::HotReloadConfig::default();
33
34 let hot_reload_manager = HotReloadManager::new(plugin_reload_config);
36
37 {
39 }
41
42 Self {
44 inner,
45 hot_reload_manager: Arc::new(RwLock::new(hot_reload_manager)),
46 }
47 }
48
49 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 {
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 {
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 let mut event_subscriber = self.hot_reload_manager.read().await.subscribe();
83
84 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 .. } => {
94 info!(
95 "Plugin {} reloaded in {:?} from path {:?}",
96 plugin_id, duration, path
97 );
98 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 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 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 _ => {} }
143 }
144 });
145 }
146
147 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 pub async fn inner(&self) -> Arc<RwLock<RhaiScriptPromptPlugin>> {
157 self.inner.clone()
158 }
159
160 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}