pub mod evidence;
pub mod operator;
pub use evidence::{ContradictionEvidence, EvidenceSnapshot, EvidenceWindow};
pub use operator::{
BinaryToConditional, ContextCompletion, EntityDisambiguation, GlobalToTemporal,
OutcomeToUpstreamLevers, PropositionToSourceComparison, ReasonCode, SocraticOperator,
SocraticSuggestion,
};
use std::sync::Arc;
pub fn rewrite(
operators: &[Arc<dyn SocraticOperator>],
query: &str,
evidence: &EvidenceSnapshot,
) -> Vec<SocraticSuggestion> {
operators
.iter()
.filter_map(|op| {
if op.fires_on(query, evidence) {
Some(op.suggest(query, evidence))
} else {
None
}
})
.collect()
}
pub fn default_operators() -> Vec<Arc<dyn SocraticOperator>> {
vec![
Arc::new(BinaryToConditional),
Arc::new(GlobalToTemporal),
Arc::new(PropositionToSourceComparison),
Arc::new(OutcomeToUpstreamLevers),
Arc::new(EntityDisambiguation),
Arc::new(ContextCompletion),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_operator_set_has_all_six_families() {
let ops = default_operators();
assert_eq!(ops.len(), 6);
}
#[test]
fn rewrite_returns_only_suggestions_from_firing_operators() {
let evidence = EvidenceSnapshot::default();
let ops = default_operators();
let suggestions = rewrite(&ops, "is X true?", &evidence);
assert!(
suggestions.is_empty(),
"expected no fires on null evidence, got {:?}",
suggestions
);
}
#[test]
fn rewrite_returns_suggestions_in_declared_order() {
let mut evidence = EvidenceSnapshot::default();
evidence.context_variance = 0.9; evidence.temporal_spread_days = 365.0;
let ops: Vec<Arc<dyn SocraticOperator>> =
vec![Arc::new(GlobalToTemporal), Arc::new(BinaryToConditional)];
let suggestions = rewrite(&ops, "did Acme grow last year?", &evidence);
assert_eq!(suggestions.len(), 2);
assert_eq!(suggestions[0].reason_code, ReasonCode::GlobalToTemporal);
assert_eq!(suggestions[1].reason_code, ReasonCode::BinaryToConditional);
}
}