use super::{QueryPattern, ResolvedReference, SealProcessingResult};
use anyhow::Result;
use brainwires_knowledge::knowledge::bks_pks::{
BehavioralKnowledgeCache, BehavioralTruth, PersonalKnowledgeCache, TruthCategory, TruthSource,
};
use std::sync::Arc;
use tokio::sync::Mutex;
const DEFAULT_PATTERN_PROMOTION_THRESHOLD: f32 = 0.8;
#[derive(Debug, Clone)]
pub struct IntegrationConfig {
pub enabled: bool,
pub seal_to_knowledge: bool,
pub knowledge_to_seal: bool,
pub min_seal_quality_for_bks_boost: f32,
pub min_seal_quality_for_pks_boost: f32,
pub pattern_promotion_threshold: f32,
pub min_pattern_uses: u32,
pub cache_bks_in_seal: bool,
pub entity_resolution_strategy: EntityResolutionStrategy,
pub seal_weight: f32,
pub bks_weight: f32,
pub pks_weight: f32,
}
impl Default for IntegrationConfig {
fn default() -> Self {
Self {
enabled: true,
seal_to_knowledge: true,
knowledge_to_seal: true,
min_seal_quality_for_bks_boost: 0.7,
min_seal_quality_for_pks_boost: 0.5,
pattern_promotion_threshold: DEFAULT_PATTERN_PROMOTION_THRESHOLD,
min_pattern_uses: 5,
cache_bks_in_seal: true,
entity_resolution_strategy: EntityResolutionStrategy::Hybrid {
seal_weight: 0.6,
pks_weight: 0.4,
},
seal_weight: 0.5,
bks_weight: 0.3,
pks_weight: 0.2,
}
}
}
impl IntegrationConfig {
pub fn full() -> Self {
Self::default()
}
pub fn seal_to_knowledge_only() -> Self {
Self {
knowledge_to_seal: false,
..Self::default()
}
}
pub fn disabled() -> Self {
Self {
enabled: false,
..Self::default()
}
}
pub fn validate(&self) -> Result<()> {
if self.min_seal_quality_for_bks_boost < 0.0 || self.min_seal_quality_for_bks_boost > 1.0 {
anyhow::bail!("min_seal_quality_for_bks_boost must be between 0.0 and 1.0");
}
if self.pattern_promotion_threshold < 0.0 || self.pattern_promotion_threshold > 1.0 {
anyhow::bail!("pattern_promotion_threshold must be between 0.0 and 1.0");
}
let weight_sum = self.seal_weight + self.bks_weight + self.pks_weight;
if (weight_sum - 1.0).abs() > 0.01 {
anyhow::bail!(
"Confidence weights must sum to 1.0 (got: {:.2})",
weight_sum
);
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum EntityResolutionStrategy {
SealFirst,
PksContextFirst,
Hybrid {
seal_weight: f32,
pks_weight: f32,
},
}
pub struct SealKnowledgeCoordinator {
bks_cache: Arc<Mutex<BehavioralKnowledgeCache>>,
pks_cache: Arc<Mutex<PersonalKnowledgeCache>>,
config: IntegrationConfig,
}
impl SealKnowledgeCoordinator {
pub fn new(
bks_cache: Arc<Mutex<BehavioralKnowledgeCache>>,
pks_cache: Arc<Mutex<PersonalKnowledgeCache>>,
config: IntegrationConfig,
) -> Result<Self> {
config.validate()?;
Ok(Self {
bks_cache,
pks_cache,
config,
})
}
pub fn with_defaults(
bks_cache: Arc<Mutex<BehavioralKnowledgeCache>>,
pks_cache: Arc<Mutex<PersonalKnowledgeCache>>,
) -> Result<Self> {
Self::new(bks_cache, pks_cache, IntegrationConfig::default())
}
pub async fn get_pks_context(
&self,
seal_result: &SealProcessingResult,
) -> Result<Option<String>> {
if !self.config.enabled {
return Ok(None);
}
if seal_result.quality_score < self.config.min_seal_quality_for_pks_boost {
return Ok(None);
}
let entities: Vec<&str> = seal_result
.resolutions
.iter()
.map(|r| r.antecedent.as_str())
.collect();
if entities.is_empty() {
return Ok(None);
}
let pks = self.pks_cache.lock().await;
let mut context_parts = Vec::new();
for entity in entities {
let all_facts: Vec<_> = pks
.get_all_facts()
.into_iter()
.filter(|f| !f.deleted && (f.key.contains(entity) || f.value.contains(entity)))
.collect();
if !all_facts.is_empty() {
context_parts.push(format!("\n**{}:**", entity));
for fact in all_facts {
if fact.confidence >= 0.5 {
context_parts.push(format!(
" - {} (confidence: {:.2})",
fact.value, fact.confidence
));
}
}
}
}
if context_parts.is_empty() {
Ok(None)
} else {
Ok(Some(format!(
"# PERSONAL CONTEXT\n\nRelevant facts about entities mentioned:\n{}",
context_parts.join("\n")
)))
}
}
pub async fn get_bks_context(&self, query: &str) -> Result<Option<String>> {
if !self.config.enabled {
return Ok(None);
}
let bks = self.bks_cache.lock().await;
let truths = bks.get_matching_truths_with_scores(query, 0.5, 5)?;
if truths.is_empty() {
return Ok(None);
}
let mut context_parts = vec!["# BEHAVIORAL KNOWLEDGE\n".to_string()];
context_parts.push("Learned patterns that may be relevant:\n".to_string());
for (truth, score) in truths {
context_parts.push(format!(
"\n**{}** (confidence: {:.2}, relevance: {:.2}):",
truth.context_pattern, truth.confidence, score
));
context_parts.push(format!(" Rule: {}", truth.rule));
context_parts.push(format!(" Why: {}", truth.rationale));
}
Ok(Some(context_parts.join("\n")))
}
pub fn harmonize_confidence(
&self,
seal_quality: f32,
bks_confidence: Option<f32>,
pks_confidence: Option<f32>,
) -> f32 {
let mut weighted_sum = seal_quality * self.config.seal_weight;
let mut total_weight = self.config.seal_weight;
if let Some(bks) = bks_confidence {
weighted_sum += bks * self.config.bks_weight;
total_weight += self.config.bks_weight;
}
if let Some(pks) = pks_confidence {
weighted_sum += pks * self.config.pks_weight;
total_weight += self.config.pks_weight;
}
if total_weight > 0.0 {
(weighted_sum / total_weight).min(1.0)
} else {
seal_quality
}
}
pub fn adjust_retrieval_threshold(&self, base_threshold: f32, seal_quality: f32) -> f32 {
base_threshold * (0.7 + 0.3 * seal_quality).max(0.5)
}
pub async fn check_and_promote_pattern(
&mut self,
pattern: &QueryPattern,
execution_context: &str,
) -> Result<Option<BehavioralTruth>> {
if !self.config.enabled || !self.config.seal_to_knowledge {
return Ok(None);
}
if pattern.reliability() < self.config.pattern_promotion_threshold {
tracing::debug!(
"Pattern '{}' reliability ({:.2}) below threshold ({:.2})",
pattern.template,
pattern.reliability(),
self.config.pattern_promotion_threshold
);
return Ok(None);
}
let total_uses = pattern.success_count + pattern.failure_count;
if total_uses < self.config.min_pattern_uses {
tracing::debug!(
"Pattern '{}' uses ({}) below minimum ({})",
pattern.template,
total_uses,
self.config.min_pattern_uses
);
return Ok(None);
}
let category = self.infer_category(&pattern.question_type);
let rule = self.generalize_pattern_to_rule(pattern);
let rationale = format!(
"Learned from {} successful executions with {:.1}% reliability (SEAL pattern)",
pattern.success_count,
pattern.reliability() * 100.0
);
let truth = BehavioralTruth::new(
category,
execution_context.to_string(),
rule,
rationale,
TruthSource::SuccessPattern, None, );
let mut bks = self.bks_cache.lock().await;
match bks.queue_submission(truth.clone()) {
Ok(_) => {
tracing::info!(
"✓ Promoted SEAL pattern to BKS: '{}' (reliability: {:.2}, uses: {})",
pattern.template,
pattern.reliability(),
total_uses
);
Ok(Some(truth))
}
Err(e) => {
tracing::warn!("Failed to promote SEAL pattern to BKS: {}", e);
Err(e)
}
}
}
pub async fn sync_bks_to_seal(
&mut self,
seal_learning: &mut super::learning::LearningCoordinator,
) -> Result<u32> {
if !self.config.enabled || !self.config.knowledge_to_seal {
return Ok(0);
}
if !self.config.cache_bks_in_seal {
return Ok(0);
}
let bks = self.bks_cache.lock().await;
let truths = bks.get_reliable_truths(0.7, 30);
let mut loaded = 0;
for truth in truths {
if let Some(hint) = self.truth_to_pattern_hint(truth) {
seal_learning.global.add_pattern_hint(hint);
tracing::debug!(
"Loaded BKS truth into SEAL: {} -> {}",
truth.context_pattern,
truth.rule
);
loaded += 1;
}
}
tracing::info!("Loaded {} BKS truths into SEAL global memory", loaded);
Ok(loaded)
}
pub async fn observe_seal_resolutions(
&mut self,
resolutions: &[ResolvedReference],
) -> Result<()> {
if !self.config.enabled {
return Ok(());
}
let mut pks = self.pks_cache.lock().await;
for resolution in resolutions {
let key = format!("recent_entity:{}", resolution.antecedent);
pks.upsert_fact_simple(
&key,
&resolution.antecedent,
resolution.confidence,
true, )?;
}
Ok(())
}
pub async fn record_tool_failure(
&mut self,
tool_name: &str,
error_message: &str,
context: &str,
) -> Result<()> {
if !self.config.enabled || !self.config.seal_to_knowledge {
return Ok(());
}
let truth = BehavioralTruth::new(
TruthCategory::ErrorRecovery,
context.to_string(),
format!(
"Tool '{}' commonly fails with: {}",
tool_name, error_message
),
"Observed from validation failures".to_string(),
TruthSource::FailurePattern,
None,
);
let mut bks = self.bks_cache.lock().await;
bks.queue_submission(truth)?;
tracing::debug!(
"Recorded tool failure pattern: {} in context: {}",
tool_name,
context
);
Ok(())
}
pub fn config(&self) -> &IntegrationConfig {
&self.config
}
pub fn get_pks_cache(&self) -> Arc<Mutex<PersonalKnowledgeCache>> {
Arc::clone(&self.pks_cache)
}
pub fn get_bks_cache(&self) -> Arc<Mutex<BehavioralKnowledgeCache>> {
Arc::clone(&self.bks_cache)
}
fn infer_category(&self, question_type: &super::query_core::QuestionType) -> TruthCategory {
use super::query_core::QuestionType;
match question_type {
QuestionType::Definition => TruthCategory::CommandUsage,
QuestionType::Dependency => TruthCategory::TaskStrategy,
QuestionType::Location => TruthCategory::TaskStrategy,
QuestionType::Count | QuestionType::Superlative => TruthCategory::TaskStrategy,
_ => TruthCategory::TaskStrategy,
}
}
fn generalize_pattern_to_rule(&self, pattern: &QueryPattern) -> String {
let types_str = pattern
.required_types
.iter()
.map(|t| format!("{:?}", t))
.collect::<Vec<_>>()
.join(", ");
format!(
"For '{:?}' queries about {}, use pattern: {}",
pattern.question_type, types_str, pattern.template
)
}
fn truth_to_pattern_hint(
&self,
truth: &BehavioralTruth,
) -> Option<super::learning::PatternHint> {
Some(super::learning::PatternHint {
context_pattern: truth.context_pattern.clone(),
rule: truth.rule.clone(),
confidence: truth.confidence as f64,
source: "bks".to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_integration_config_validation() {
let mut config = IntegrationConfig::default();
assert!(config.validate().is_ok());
config.min_seal_quality_for_bks_boost = 1.5;
assert!(config.validate().is_err());
config = IntegrationConfig::default();
config.seal_weight = 0.5;
config.bks_weight = 0.5;
config.pks_weight = 0.5; assert!(config.validate().is_err());
}
#[test]
fn test_confidence_harmonization() {
let coordinator = create_test_coordinator();
let conf = coordinator.harmonize_confidence(0.8, None, None);
assert!((conf - 0.8).abs() < 0.01);
let conf = coordinator.harmonize_confidence(0.6, Some(0.9), Some(0.8));
assert!((conf - 0.73).abs() < 0.01);
}
#[test]
fn test_retrieval_threshold_adjustment() {
let coordinator = create_test_coordinator();
let adjusted = coordinator.adjust_retrieval_threshold(0.75, 0.0);
assert!((adjusted - 0.525).abs() < 0.01);
let adjusted = coordinator.adjust_retrieval_threshold(0.75, 1.0);
assert!((adjusted - 0.75).abs() < 0.01);
let adjusted = coordinator.adjust_retrieval_threshold(0.75, 0.5);
assert!((adjusted - 0.6375).abs() < 0.01); }
fn create_test_coordinator() -> SealKnowledgeCoordinator {
let bks_cache = Arc::new(Mutex::new(
BehavioralKnowledgeCache::in_memory(100).unwrap(),
));
let pks_cache = Arc::new(Mutex::new(PersonalKnowledgeCache::in_memory(100).unwrap()));
SealKnowledgeCoordinator::new(bks_cache, pks_cache, IntegrationConfig::default()).unwrap()
}
}