mockforge_intelligence/ai_studio/
debug_context_integrator.rs1use crate::ai_studio::debug_context::{
8 ChaosContext, ChaosRule, ContractContext, ContractValidationResult, DebugContext,
9 DriftHistoryEntry, FailureInjectionConfig, PersonaContext, RealityContext, ScenarioContext,
10};
11use crate::reality::{RealityConfig, RealityEngine, RealityLevel};
12use async_trait::async_trait;
13use mockforge_foundation::Result;
14use serde_json::Value;
15use std::collections::HashMap;
16
17#[async_trait]
19pub trait RealityAccessor: Send + Sync {
20 async fn get_level(&self) -> Option<RealityLevel>;
22 async fn get_config(&self) -> Option<RealityConfig>;
24}
25
26#[async_trait]
28pub trait ContractAccessor: Send + Sync {
29 async fn get_validation_result(
31 &self,
32 workspace_id: Option<&str>,
33 ) -> Result<Option<ContractValidationResult>>;
34 async fn get_enforcement_mode(&self, workspace_id: Option<&str>) -> Result<String>;
36 async fn get_drift_history(&self, workspace_id: Option<&str>)
38 -> Result<Vec<DriftHistoryEntry>>;
39 async fn get_active_contracts(&self, workspace_id: Option<&str>) -> Result<Vec<String>>;
41}
42
43#[async_trait]
45pub trait ScenarioAccessor: Send + Sync {
46 async fn get_active_scenario(&self, workspace_id: Option<&str>) -> Result<Option<String>>;
48 async fn get_current_state(
50 &self,
51 workspace_id: Option<&str>,
52 scenario_id: Option<&str>,
53 ) -> Result<Option<String>>;
54 async fn get_available_transitions(
56 &self,
57 workspace_id: Option<&str>,
58 scenario_id: Option<&str>,
59 ) -> Result<Vec<String>>;
60 async fn get_scenario_config(
62 &self,
63 workspace_id: Option<&str>,
64 scenario_id: Option<&str>,
65 ) -> Result<Option<Value>>;
66}
67
68#[async_trait]
70pub trait PersonaAccessor: Send + Sync {
71 async fn get_active_persona_id(&self, workspace_id: Option<&str>) -> Result<Option<String>>;
73 async fn get_persona_details(
75 &self,
76 workspace_id: Option<&str>,
77 persona_id: Option<&str>,
78 ) -> Result<Option<PersonaDetails>>;
79}
80
81#[derive(Debug, Clone)]
83pub struct PersonaDetails {
84 pub id: String,
86 pub name: Option<String>,
88 pub traits: HashMap<String, String>,
90 pub domain: Option<String>,
92 pub backstory: Option<String>,
94 pub relationships: HashMap<String, Vec<String>>,
96 pub lifecycle_state: Option<String>,
98}
99
100#[async_trait]
102pub trait ChaosAccessor: Send + Sync {
103 async fn get_chaos_config(&self, workspace_id: Option<&str>) -> Result<Option<ChaosConfig>>;
105}
106
107#[derive(Debug, Clone)]
110pub struct ChaosConfig {
111 pub enabled: bool,
113 pub active_rules: Vec<ChaosRule>,
115 pub failure_injection: Option<FailureInjectionConfig>,
117 pub tags: Vec<String>,
119}
120
121pub struct DebugContextIntegrator {
126 reality_accessor: Option<Box<dyn RealityAccessor>>,
128 contract_accessor: Option<Box<dyn ContractAccessor>>,
130 scenario_accessor: Option<Box<dyn ScenarioAccessor>>,
132 persona_accessor: Option<Box<dyn PersonaAccessor>>,
134 chaos_accessor: Option<Box<dyn ChaosAccessor>>,
136}
137
138impl DebugContextIntegrator {
139 pub fn new() -> Self {
141 Self {
142 reality_accessor: None,
143 contract_accessor: None,
144 scenario_accessor: None,
145 persona_accessor: None,
146 chaos_accessor: None,
147 }
148 }
149
150 pub fn with_reality(mut self, accessor: Box<dyn RealityAccessor>) -> Self {
152 self.reality_accessor = Some(accessor);
153 self
154 }
155
156 pub fn with_contract(mut self, accessor: Box<dyn ContractAccessor>) -> Self {
158 self.contract_accessor = Some(accessor);
159 self
160 }
161
162 pub fn with_scenario(mut self, accessor: Box<dyn ScenarioAccessor>) -> Self {
164 self.scenario_accessor = Some(accessor);
165 self
166 }
167
168 pub fn with_persona(mut self, accessor: Box<dyn PersonaAccessor>) -> Self {
170 self.persona_accessor = Some(accessor);
171 self
172 }
173
174 pub fn with_chaos(mut self, accessor: Box<dyn ChaosAccessor>) -> Self {
176 self.chaos_accessor = Some(accessor);
177 self
178 }
179
180 pub async fn collect_unified_context(
185 &self,
186 workspace_id: Option<&str>,
187 ) -> Result<DebugContext> {
188 let reality = self.collect_reality_context().await?;
189 let contract = self.collect_contract_context(workspace_id).await?;
190 let scenario = self.collect_scenario_context(workspace_id).await?;
191 let persona = self.collect_persona_context(workspace_id).await?;
192 let chaos = self.collect_chaos_context(workspace_id).await?;
193
194 Ok(DebugContext {
195 reality,
196 contract,
197 scenario,
198 persona,
199 chaos,
200 collected_at: chrono::Utc::now(),
201 })
202 }
203
204 async fn collect_reality_context(&self) -> Result<RealityContext> {
206 if let Some(accessor) = &self.reality_accessor {
207 let level = accessor.get_level().await;
208 let config = accessor.get_config().await;
209
210 if let Some(config) = config {
211 Ok(RealityContext::from_config(&config))
212 } else if let Some(level) = level {
213 Ok(RealityContext {
215 level: Some(level),
216 level_name: Some(level.name().to_string()),
217 ..Default::default()
218 })
219 } else {
220 Ok(RealityContext::default())
221 }
222 } else {
223 Ok(RealityContext::default())
224 }
225 }
226
227 async fn collect_contract_context(
229 &self,
230 workspace_id: Option<&str>,
231 ) -> Result<ContractContext> {
232 if let Some(accessor) = &self.contract_accessor {
233 let validation_result = accessor.get_validation_result(workspace_id).await?;
234 let enforcement_mode = accessor.get_enforcement_mode(workspace_id).await?;
235 let drift_history = accessor.get_drift_history(workspace_id).await?;
236 let active_contracts = accessor.get_active_contracts(workspace_id).await?;
237
238 let validation_enabled = validation_result.is_some();
239 let validation_errors =
240 validation_result.as_ref().map(|r| r.errors.clone()).unwrap_or_default();
241
242 Ok(ContractContext {
243 validation_enabled,
244 validation_result,
245 enforcement_mode,
246 drift_history,
247 active_contracts,
248 validation_errors,
249 })
250 } else {
251 Ok(ContractContext::default())
252 }
253 }
254
255 async fn collect_scenario_context(
257 &self,
258 workspace_id: Option<&str>,
259 ) -> Result<ScenarioContext> {
260 if let Some(accessor) = &self.scenario_accessor {
261 let active_scenario = accessor.get_active_scenario(workspace_id).await?;
262 let current_state = if let Some(ref scenario_id) = active_scenario {
263 accessor.get_current_state(workspace_id, Some(scenario_id)).await?
264 } else {
265 None
266 };
267 let available_transitions = if let Some(ref scenario_id) = active_scenario {
268 accessor.get_available_transitions(workspace_id, Some(scenario_id)).await?
269 } else {
270 Vec::new()
271 };
272 let scenario_config = if let Some(ref scenario_id) = active_scenario {
273 accessor.get_scenario_config(workspace_id, Some(scenario_id)).await?
274 } else {
275 None
276 };
277
278 Ok(ScenarioContext {
279 active_scenario,
280 current_state,
281 available_transitions,
282 scenario_config,
283 })
284 } else {
285 Ok(ScenarioContext::default())
286 }
287 }
288
289 async fn collect_persona_context(&self, workspace_id: Option<&str>) -> Result<PersonaContext> {
291 if let Some(accessor) = &self.persona_accessor {
292 let active_persona_id = accessor.get_active_persona_id(workspace_id).await?;
293 let persona_details = if let Some(ref persona_id) = active_persona_id {
294 accessor.get_persona_details(workspace_id, Some(persona_id)).await?
295 } else {
296 None
297 };
298
299 if let Some(details) = persona_details {
300 Ok(PersonaContext {
301 active_persona_id: Some(details.id.clone()),
302 active_persona_name: details.name,
303 traits: details.traits,
304 domain: details.domain,
305 backstory: details.backstory,
306 relationships: details.relationships,
307 lifecycle_state: details.lifecycle_state,
308 })
309 } else if let Some(persona_id) = active_persona_id {
310 Ok(PersonaContext {
312 active_persona_id: Some(persona_id),
313 ..Default::default()
314 })
315 } else {
316 Ok(PersonaContext::default())
317 }
318 } else {
319 Ok(PersonaContext::default())
320 }
321 }
322
323 async fn collect_chaos_context(&self, workspace_id: Option<&str>) -> Result<ChaosContext> {
325 if let Some(accessor) = &self.chaos_accessor {
326 if let Some(config) = accessor.get_chaos_config(workspace_id).await? {
327 Ok(ChaosContext {
328 enabled: config.enabled,
329 active_rules: config.active_rules,
330 failure_injection: config.failure_injection,
331 tags: config.tags,
332 })
333 } else {
334 Ok(ChaosContext::default())
335 }
336 } else {
337 Ok(ChaosContext::default())
338 }
339 }
340}
341
342impl Default for DebugContextIntegrator {
343 fn default() -> Self {
344 Self::new()
345 }
346}
347
348pub struct RealityEngineAccessor {
350 engine: std::sync::Arc<tokio::sync::RwLock<RealityEngine>>,
351}
352
353impl RealityEngineAccessor {
354 pub fn new(engine: std::sync::Arc<tokio::sync::RwLock<RealityEngine>>) -> Self {
356 Self { engine }
357 }
358}
359
360#[async_trait]
361impl RealityAccessor for RealityEngineAccessor {
362 async fn get_level(&self) -> Option<RealityLevel> {
363 Some(self.engine.read().await.get_level().await)
364 }
365
366 async fn get_config(&self) -> Option<RealityConfig> {
367 Some(self.engine.read().await.get_config().await)
368 }
369}