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 #[cfg(feature = "sandbox")]
83 pub fn sandbox_policy(&self) -> &SandboxPolicy {
84 &self.sandbox_policy
85 }
86
87 #[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}