use crate::ai_studio::debug_context::{
ChaosContext, ChaosRule, ContractContext, ContractValidationResult, DebugContext,
DriftHistoryEntry, FailureInjectionConfig, PersonaContext, RealityContext, ScenarioContext,
};
use crate::reality::{RealityConfig, RealityEngine, RealityLevel};
use async_trait::async_trait;
use mockforge_foundation::Result;
use serde_json::Value;
use std::collections::HashMap;
#[async_trait]
pub trait RealityAccessor: Send + Sync {
async fn get_level(&self) -> Option<RealityLevel>;
async fn get_config(&self) -> Option<RealityConfig>;
}
#[async_trait]
pub trait ContractAccessor: Send + Sync {
async fn get_validation_result(
&self,
workspace_id: Option<&str>,
) -> Result<Option<ContractValidationResult>>;
async fn get_enforcement_mode(&self, workspace_id: Option<&str>) -> Result<String>;
async fn get_drift_history(&self, workspace_id: Option<&str>)
-> Result<Vec<DriftHistoryEntry>>;
async fn get_active_contracts(&self, workspace_id: Option<&str>) -> Result<Vec<String>>;
}
#[async_trait]
pub trait ScenarioAccessor: Send + Sync {
async fn get_active_scenario(&self, workspace_id: Option<&str>) -> Result<Option<String>>;
async fn get_current_state(
&self,
workspace_id: Option<&str>,
scenario_id: Option<&str>,
) -> Result<Option<String>>;
async fn get_available_transitions(
&self,
workspace_id: Option<&str>,
scenario_id: Option<&str>,
) -> Result<Vec<String>>;
async fn get_scenario_config(
&self,
workspace_id: Option<&str>,
scenario_id: Option<&str>,
) -> Result<Option<Value>>;
}
#[async_trait]
pub trait PersonaAccessor: Send + Sync {
async fn get_active_persona_id(&self, workspace_id: Option<&str>) -> Result<Option<String>>;
async fn get_persona_details(
&self,
workspace_id: Option<&str>,
persona_id: Option<&str>,
) -> Result<Option<PersonaDetails>>;
}
#[derive(Debug, Clone)]
pub struct PersonaDetails {
pub id: String,
pub name: Option<String>,
pub traits: HashMap<String, String>,
pub domain: Option<String>,
pub backstory: Option<String>,
pub relationships: HashMap<String, Vec<String>>,
pub lifecycle_state: Option<String>,
}
#[async_trait]
pub trait ChaosAccessor: Send + Sync {
async fn get_chaos_config(&self, workspace_id: Option<&str>) -> Result<Option<ChaosConfig>>;
}
#[derive(Debug, Clone)]
pub struct ChaosConfig {
pub enabled: bool,
pub active_rules: Vec<ChaosRule>,
pub failure_injection: Option<FailureInjectionConfig>,
pub tags: Vec<String>,
}
pub struct DebugContextIntegrator {
reality_accessor: Option<Box<dyn RealityAccessor>>,
contract_accessor: Option<Box<dyn ContractAccessor>>,
scenario_accessor: Option<Box<dyn ScenarioAccessor>>,
persona_accessor: Option<Box<dyn PersonaAccessor>>,
chaos_accessor: Option<Box<dyn ChaosAccessor>>,
}
impl DebugContextIntegrator {
pub fn new() -> Self {
Self {
reality_accessor: None,
contract_accessor: None,
scenario_accessor: None,
persona_accessor: None,
chaos_accessor: None,
}
}
pub fn with_reality(mut self, accessor: Box<dyn RealityAccessor>) -> Self {
self.reality_accessor = Some(accessor);
self
}
pub fn with_contract(mut self, accessor: Box<dyn ContractAccessor>) -> Self {
self.contract_accessor = Some(accessor);
self
}
pub fn with_scenario(mut self, accessor: Box<dyn ScenarioAccessor>) -> Self {
self.scenario_accessor = Some(accessor);
self
}
pub fn with_persona(mut self, accessor: Box<dyn PersonaAccessor>) -> Self {
self.persona_accessor = Some(accessor);
self
}
pub fn with_chaos(mut self, accessor: Box<dyn ChaosAccessor>) -> Self {
self.chaos_accessor = Some(accessor);
self
}
pub async fn collect_unified_context(
&self,
workspace_id: Option<&str>,
) -> Result<DebugContext> {
let reality = self.collect_reality_context().await?;
let contract = self.collect_contract_context(workspace_id).await?;
let scenario = self.collect_scenario_context(workspace_id).await?;
let persona = self.collect_persona_context(workspace_id).await?;
let chaos = self.collect_chaos_context(workspace_id).await?;
Ok(DebugContext {
reality,
contract,
scenario,
persona,
chaos,
collected_at: chrono::Utc::now(),
})
}
async fn collect_reality_context(&self) -> Result<RealityContext> {
if let Some(accessor) = &self.reality_accessor {
let level = accessor.get_level().await;
let config = accessor.get_config().await;
if let Some(config) = config {
Ok(RealityContext::from_config(&config))
} else if let Some(level) = level {
Ok(RealityContext {
level: Some(level),
level_name: Some(level.name().to_string()),
..Default::default()
})
} else {
Ok(RealityContext::default())
}
} else {
Ok(RealityContext::default())
}
}
async fn collect_contract_context(
&self,
workspace_id: Option<&str>,
) -> Result<ContractContext> {
if let Some(accessor) = &self.contract_accessor {
let validation_result = accessor.get_validation_result(workspace_id).await?;
let enforcement_mode = accessor.get_enforcement_mode(workspace_id).await?;
let drift_history = accessor.get_drift_history(workspace_id).await?;
let active_contracts = accessor.get_active_contracts(workspace_id).await?;
let validation_enabled = validation_result.is_some();
let validation_errors =
validation_result.as_ref().map(|r| r.errors.clone()).unwrap_or_default();
Ok(ContractContext {
validation_enabled,
validation_result,
enforcement_mode,
drift_history,
active_contracts,
validation_errors,
})
} else {
Ok(ContractContext::default())
}
}
async fn collect_scenario_context(
&self,
workspace_id: Option<&str>,
) -> Result<ScenarioContext> {
if let Some(accessor) = &self.scenario_accessor {
let active_scenario = accessor.get_active_scenario(workspace_id).await?;
let current_state = if let Some(ref scenario_id) = active_scenario {
accessor.get_current_state(workspace_id, Some(scenario_id)).await?
} else {
None
};
let available_transitions = if let Some(ref scenario_id) = active_scenario {
accessor.get_available_transitions(workspace_id, Some(scenario_id)).await?
} else {
Vec::new()
};
let scenario_config = if let Some(ref scenario_id) = active_scenario {
accessor.get_scenario_config(workspace_id, Some(scenario_id)).await?
} else {
None
};
Ok(ScenarioContext {
active_scenario,
current_state,
available_transitions,
scenario_config,
})
} else {
Ok(ScenarioContext::default())
}
}
async fn collect_persona_context(&self, workspace_id: Option<&str>) -> Result<PersonaContext> {
if let Some(accessor) = &self.persona_accessor {
let active_persona_id = accessor.get_active_persona_id(workspace_id).await?;
let persona_details = if let Some(ref persona_id) = active_persona_id {
accessor.get_persona_details(workspace_id, Some(persona_id)).await?
} else {
None
};
if let Some(details) = persona_details {
Ok(PersonaContext {
active_persona_id: Some(details.id.clone()),
active_persona_name: details.name,
traits: details.traits,
domain: details.domain,
backstory: details.backstory,
relationships: details.relationships,
lifecycle_state: details.lifecycle_state,
})
} else if let Some(persona_id) = active_persona_id {
Ok(PersonaContext {
active_persona_id: Some(persona_id),
..Default::default()
})
} else {
Ok(PersonaContext::default())
}
} else {
Ok(PersonaContext::default())
}
}
async fn collect_chaos_context(&self, workspace_id: Option<&str>) -> Result<ChaosContext> {
if let Some(accessor) = &self.chaos_accessor {
if let Some(config) = accessor.get_chaos_config(workspace_id).await? {
Ok(ChaosContext {
enabled: config.enabled,
active_rules: config.active_rules,
failure_injection: config.failure_injection,
tags: config.tags,
})
} else {
Ok(ChaosContext::default())
}
} else {
Ok(ChaosContext::default())
}
}
}
impl Default for DebugContextIntegrator {
fn default() -> Self {
Self::new()
}
}
pub struct RealityEngineAccessor {
engine: std::sync::Arc<tokio::sync::RwLock<RealityEngine>>,
}
impl RealityEngineAccessor {
pub fn new(engine: std::sync::Arc<tokio::sync::RwLock<RealityEngine>>) -> Self {
Self { engine }
}
}
#[async_trait]
impl RealityAccessor for RealityEngineAccessor {
async fn get_level(&self) -> Option<RealityLevel> {
Some(self.engine.read().await.get_level().await)
}
async fn get_config(&self) -> Option<RealityConfig> {
Some(self.engine.read().await.get_config().await)
}
}