Skip to main content

funera_core/
env.rs

1use std::sync::Arc;
2
3use async_openai::config::OpenAIConfig;
4
5#[cfg(feature = "tool")]
6use crate::re_act::tool::{Tool, ToolRegistry};
7#[cfg(feature = "skill")]
8use crate::re_act::skills::{Skill, SkillRegistry};
9#[cfg(feature = "sandbox")]
10use crate::security::sandbox::SandboxPolicy;
11use serde_json::Value as JsonValue;
12use tokio::sync::{
13    watch::{self, error::RecvError},
14    RwLock,
15};
16
17pub struct FuneraEnv {
18    #[cfg(feature = "tool")]
19    pub tool_registry: Arc<RwLock<ToolRegistry>>,
20    #[cfg(feature = "skill")]
21    pub skill_registry: Arc<RwLock<SkillRegistry>>,
22    llm_client: async_openai::Client<OpenAIConfig>,
23    model: String,
24    #[cfg(feature = "tool")]
25    tool_tx: watch::Sender<JsonValue>,
26    client_tx: watch::Sender<async_openai::Client<OpenAIConfig>>,
27    model_tx: watch::Sender<String>,
28    #[cfg(feature = "skill")]
29    skill_tx: watch::Sender<String>,
30    #[cfg(feature = "sandbox")]
31    sandbox_policy: SandboxPolicy,
32}
33
34impl FuneraEnv {
35    pub fn new(
36        llm_client: async_openai::Client<OpenAIConfig>,
37        model: impl Into<String>,
38    ) -> (Self, FuneraEnvWatcher) {
39        let model = model.into();
40        let (client_tx, client_rx) = watch::channel(llm_client.clone());
41        let (model_tx, model_rx) = watch::channel(model.clone());
42
43        #[cfg(feature = "tool")]
44        let tool_registry = Arc::new(RwLock::new(ToolRegistry::new()));
45        #[cfg(feature = "tool")]
46        let (tool_tx, tool_rx) = watch::channel(JsonValue::Array(Vec::new()));
47
48        #[cfg(feature = "skill")]
49        let skill_registry = Arc::new(RwLock::new(SkillRegistry::new()));
50        #[cfg(feature = "skill")]
51        let (skill_tx, skill_rx) = watch::channel(String::new());
52
53        (
54            Self {
55                #[cfg(feature = "tool")]
56                tool_registry,
57                #[cfg(feature = "skill")]
58                skill_registry,
59                llm_client,
60                model,
61                #[cfg(feature = "tool")]
62                tool_tx,
63                client_tx,
64                model_tx,
65                #[cfg(feature = "skill")]
66                skill_tx,
67                #[cfg(feature = "sandbox")]
68                sandbox_policy: SandboxPolicy::default(),
69            },
70            FuneraEnvWatcher {
71                #[cfg(feature = "tool")]
72                tool_rx,
73                client_rx,
74                model_rx,
75                #[cfg(feature = "skill")]
76                skill_rx,
77            },
78        )
79    }
80
81    /// Access the current sandbox policy.
82    #[cfg(feature = "sandbox")]
83    pub fn sandbox_policy(&self) -> &SandboxPolicy {
84        &self.sandbox_policy
85    }
86
87    /// Set a custom sandbox policy.
88    #[cfg(feature = "sandbox")]
89    pub fn with_sandbox_policy(mut self, policy: SandboxPolicy) -> Self {
90        self.sandbox_policy = policy;
91        self
92    }
93
94    #[cfg(feature = "tool")]
95    pub fn with_tool_registry(
96        self,
97        tool_registry: ToolRegistry,
98    ) -> Self {
99        let snapshot = tool_registry.available_tools_json();
100        let _ = self.tool_tx.send(snapshot);
101        Self {
102            tool_registry: Arc::new(RwLock::new(tool_registry)),
103            ..self
104        }
105    }
106
107    #[cfg(feature = "skill")]
108    pub fn with_skill_registry(
109        self,
110        skill_registry: SkillRegistry,
111    ) -> Self {
112        let prompt = skill_registry.get_active_skills_prompt();
113        let _ = self.skill_tx.send(prompt);
114        Self {
115            skill_registry: Arc::new(RwLock::new(skill_registry)),
116            ..self
117        }
118    }
119
120    #[cfg(feature = "tool")]
121    pub async fn add_tool(&mut self, tool: Box<dyn Tool>) {
122        let mut registry = self.tool_registry.write().await;
123        registry.add_tool(tool);
124        let _ = self.tool_tx.send(registry.available_tools_json());
125    }
126
127    #[cfg(feature = "tool")]
128    pub async fn remove_tool(&mut self, name: &str) {
129        let mut registry = self.tool_registry.write().await;
130        registry.remove_tool(name);
131        let _ = self.tool_tx.send(registry.available_tools_json());
132    }
133
134    #[cfg(feature = "tool")]
135    pub async fn set_tool_availability(&mut self, _name: &str, _available: bool) {
136        let registry = self.tool_registry.read().await;
137        let _ = self.tool_tx.send(registry.available_tools_json());
138    }
139
140    pub fn set_client(&mut self, client: async_openai::Client<OpenAIConfig>) {
141        self.llm_client = client.clone();
142        let _ = self.client_tx.send(client);
143    }
144
145    pub fn set_model(&mut self, model: impl Into<String>) {
146        let model = model.into();
147        self.model = model.clone();
148        let _ = self.model_tx.send(model);
149    }
150
151    #[cfg(feature = "skill")]
152    pub async fn add_skill(&mut self, skill: Skill) {
153        let mut registry = self.skill_registry.write().await;
154        registry.add(skill);
155        let _ = self.skill_tx.send(registry.get_active_skills_prompt());
156    }
157
158    #[cfg(feature = "skill")]
159    pub async fn remove_skill(&mut self, name: &str) {
160        let mut registry = self.skill_registry.write().await;
161        registry.remove(name);
162        let _ = self.skill_tx.send(registry.get_active_skills_prompt());
163    }
164
165    #[cfg(feature = "skill")]
166    pub async fn activate_skill(&mut self, name: &str) -> bool {
167        let mut registry = self.skill_registry.write().await;
168        let ok = registry.activate(name);
169        if ok {
170            let _ = self.skill_tx.send(registry.get_active_skills_prompt());
171        }
172        ok
173    }
174
175    #[cfg(feature = "skill")]
176    pub async fn deactivate_skill(&mut self, name: &str) -> bool {
177        let mut registry = self.skill_registry.write().await;
178        let ok = registry.deactivate(name);
179        if ok {
180            let _ = self.skill_tx.send(registry.get_active_skills_prompt());
181        }
182        ok
183    }
184
185    #[cfg(feature = "skill")]
186    pub fn skill_prompt_now(&self) -> String {
187        self.skill_tx.borrow().clone()
188    }
189
190    #[cfg(feature = "skill")]
191    pub fn set_skill_prompt(&mut self, prompt: String) {
192        let _ = self.skill_tx.send(prompt);
193    }
194}
195
196#[derive(Debug, Clone)]
197pub struct FuneraEnvWatcher {
198    #[cfg(feature = "tool")]
199    tool_rx: watch::Receiver<JsonValue>,
200    client_rx: watch::Receiver<async_openai::Client<OpenAIConfig>>,
201    model_rx: watch::Receiver<String>,
202    #[cfg(feature = "skill")]
203    skill_rx: watch::Receiver<String>,
204}
205
206impl FuneraEnvWatcher {
207    #[cfg(feature = "tool")]
208    pub fn watch_tool(&mut self) -> JsonValue {
209        self.tool_rx.borrow_and_update().clone()
210    }
211
212    pub fn watch_client(&mut self) -> async_openai::Client<OpenAIConfig> {
213        self.client_rx.borrow_and_update().clone()
214    }
215
216    pub fn watch_model(&mut self) -> String {
217        self.model_rx.borrow_and_update().clone()
218    }
219
220    #[cfg(feature = "skill")]
221    pub fn watch_skill(&mut self) -> String {
222        self.skill_rx.borrow_and_update().clone()
223    }
224
225    #[cfg(feature = "tool")]
226    pub fn has_tool_changed(&self) -> bool {
227        self.tool_rx.has_changed().unwrap_or(false)
228    }
229
230    pub fn has_client_changed(&self) -> bool {
231        self.client_rx.has_changed().unwrap_or(false)
232    }
233
234    pub fn has_model_changed(&self) -> bool {
235        self.model_rx.has_changed().unwrap_or(false)
236    }
237
238    #[cfg(feature = "skill")]
239    pub fn has_skill_changed(&self) -> bool {
240        self.skill_rx.has_changed().unwrap_or(false)
241    }
242
243    pub fn use_client(&mut self) -> async_openai::Client<OpenAIConfig> {
244        self.watch_client()
245    }
246
247    #[cfg(feature = "tool")]
248    pub async fn tool_changed(&mut self) -> Result<(), RecvError> {
249        self.tool_rx.changed().await
250    }
251
252    pub async fn client_changed(&mut self) -> Result<(), RecvError> {
253        self.client_rx.changed().await
254    }
255
256    pub async fn model_changed(&mut self) -> Result<(), RecvError> {
257        self.model_rx.changed().await
258    }
259
260    #[cfg(feature = "skill")]
261    pub async fn skill_changed(&mut self) -> Result<(), RecvError> {
262        self.skill_rx.changed().await
263    }
264}