use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicU64, Ordering};
use uuid::Uuid;
use super::error::{Result, RuvLlmIntegrationError};
#[derive(Debug)]
pub struct WitnessAdapter {
config: WitnessAdapterConfig,
entries_recorded: AtomicU64,
correlations_created: AtomicU64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WitnessAdapterConfig {
pub storage_path: String,
pub correlation_window_secs: u64,
pub enable_correlation: bool,
pub max_entries: usize,
pub embedding_dim: usize,
}
impl Default for WitnessAdapterConfig {
fn default() -> Self {
Self {
storage_path: ".prime-radiant/witness".to_string(),
correlation_window_secs: super::DEFAULT_CORRELATION_WINDOW_SECS,
enable_correlation: true,
max_entries: 100_000,
embedding_dim: 768,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnifiedWitnessEntry {
pub id: Uuid,
pub correlation_id: Option<CorrelationId>,
pub source: WitnessSource,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub entry_type: WitnessEntryType,
pub session_id: Option<String>,
pub operation: String,
pub latency: LatencyBreakdown,
pub coherence: Option<CoherenceMetrics>,
pub llm: Option<LlmMetrics>,
pub embedding: Option<Vec<f32>>,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WitnessSource {
PrimeRadiant,
RuvLlm,
Unified,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WitnessEntryType {
Inference,
CoherenceCheck,
GateDecision,
PolicyEvaluation,
Escalation,
SystemEvent,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LatencyBreakdown {
pub prefill_ms: f64,
pub decode_ms: f64,
pub coherence_ms: f64,
pub gate_ms: f64,
pub total_ms: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoherenceMetrics {
pub energy: f64,
pub max_residual: f64,
pub affected_nodes: usize,
pub lane: String,
pub allowed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmMetrics {
pub model: String,
pub tokens_generated: usize,
pub tokens_per_second: f64,
pub adapter: Option<String>,
pub quantization: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CorrelationId(pub Uuid);
impl CorrelationId {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
}
impl Default for CorrelationId {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WitnessCorrelation {
pub id: CorrelationId,
pub entries: Vec<Uuid>,
pub start_time: chrono::DateTime<chrono::Utc>,
pub end_time: Option<chrono::DateTime<chrono::Utc>>,
pub session_id: Option<String>,
pub summary: CorrelationSummary,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CorrelationSummary {
pub total_entries: usize,
pub prime_radiant_entries: usize,
pub ruvllm_entries: usize,
pub total_latency_ms: f64,
pub avg_energy: f64,
pub pass_rate: f64,
}
impl WitnessAdapter {
pub fn new(config: WitnessAdapterConfig) -> Result<Self> {
Ok(Self {
config,
entries_recorded: AtomicU64::new(0),
correlations_created: AtomicU64::new(0),
})
}
pub fn record(&self, entry: UnifiedWitnessEntry) -> Result<()> {
self.validate_entry(&entry)?;
self.entries_recorded.fetch_add(1, Ordering::Relaxed);
Ok(())
}
pub fn create_correlation(&self, session_id: Option<String>) -> Result<WitnessCorrelation> {
self.correlations_created.fetch_add(1, Ordering::Relaxed);
Ok(WitnessCorrelation {
id: CorrelationId::new(),
entries: Vec::new(),
start_time: chrono::Utc::now(),
end_time: None,
session_id,
summary: CorrelationSummary::default(),
})
}
pub fn add_to_correlation(
&self,
correlation: &mut WitnessCorrelation,
entry_id: Uuid,
) -> Result<()> {
correlation.entries.push(entry_id);
correlation.summary.total_entries += 1;
Ok(())
}
pub fn stats(&self) -> (u64, u64) {
(
self.entries_recorded.load(Ordering::Relaxed),
self.correlations_created.load(Ordering::Relaxed),
)
}
fn validate_entry(&self, entry: &UnifiedWitnessEntry) -> Result<()> {
if entry.operation.is_empty() {
return Err(RuvLlmIntegrationError::Config(
"Operation cannot be empty".to_string(),
));
}
if let Some(ref embedding) = entry.embedding {
if embedding.len() != self.config.embedding_dim {
return Err(RuvLlmIntegrationError::EmbeddingDimensionMismatch {
expected: self.config.embedding_dim,
actual: embedding.len(),
});
}
}
Ok(())
}
pub fn config(&self) -> &WitnessAdapterConfig {
&self.config
}
}
impl UnifiedWitnessEntry {
pub fn from_prime_radiant(
operation: String,
coherence: CoherenceMetrics,
latency_ms: f64,
) -> Self {
Self {
id: Uuid::new_v4(),
correlation_id: None,
source: WitnessSource::PrimeRadiant,
timestamp: chrono::Utc::now(),
entry_type: WitnessEntryType::CoherenceCheck,
session_id: None,
operation,
latency: LatencyBreakdown {
coherence_ms: latency_ms,
total_ms: latency_ms,
..Default::default()
},
coherence: Some(coherence),
llm: None,
embedding: None,
metadata: serde_json::Value::Null,
}
}
pub fn from_ruvllm(
operation: String,
llm: LlmMetrics,
prefill_ms: f64,
decode_ms: f64,
) -> Self {
Self {
id: Uuid::new_v4(),
correlation_id: None,
source: WitnessSource::RuvLlm,
timestamp: chrono::Utc::now(),
entry_type: WitnessEntryType::Inference,
session_id: None,
operation,
latency: LatencyBreakdown {
prefill_ms,
decode_ms,
total_ms: prefill_ms + decode_ms,
..Default::default()
},
coherence: None,
llm: Some(llm),
embedding: None,
metadata: serde_json::Value::Null,
}
}
pub fn with_correlation(mut self, correlation_id: CorrelationId) -> Self {
self.correlation_id = Some(correlation_id);
self
}
pub fn with_session(mut self, session_id: String) -> Self {
self.session_id = Some(session_id);
self
}
pub fn with_embedding(mut self, embedding: Vec<f32>) -> Self {
self.embedding = Some(embedding);
self
}
}