use crate::core::knowledge_relations::{KnowledgeEdge, KnowledgeRelationGraph};
use lean_ctx_protocol::KnowledgeObjectV1;
use serde::{Deserialize, Serialize};
use super::types::{ConsolidatedInsight, KnowledgeFact, ProjectKnowledge, ProjectPattern};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KnowledgeRef {
pub object_id: String,
pub evidence_digest: String,
pub policy_ref: String,
}
#[derive(Debug, Clone)]
pub struct KnowledgeSnapshot {
pub project_root: String,
pub project_hash: String,
pub facts: Vec<KnowledgeFact>,
pub patterns: Vec<ProjectPattern>,
pub insights: Vec<ConsolidatedInsight>,
pub relations: Vec<KnowledgeEdge>,
pub task_id: Option<String>,
pub policy_version: Option<String>,
pub knowledge_refs: Vec<KnowledgeRef>,
pub snapshot_hash: Option<String>,
}
impl KnowledgeSnapshot {
pub fn collect(project_root: &str) -> Self {
let knowledge = ProjectKnowledge::load_or_create(project_root);
let relations = KnowledgeRelationGraph::load(&knowledge.project_hash)
.map(|g| g.edges)
.unwrap_or_default();
Self::from_project(&knowledge, relations)
}
pub fn from_project(knowledge: &ProjectKnowledge, relations: Vec<KnowledgeEdge>) -> Self {
Self {
project_root: knowledge.project_root.clone(),
project_hash: knowledge.project_hash.clone(),
facts: knowledge.facts.clone(),
patterns: knowledge.patterns.clone(),
insights: knowledge.history.clone(),
relations,
task_id: None,
policy_version: None,
knowledge_refs: Vec::new(),
snapshot_hash: None,
}
}
pub fn from_items(task_id: &str, policy_version: &str, items: &[KnowledgeObjectV1]) -> Self {
let knowledge_refs = items
.iter()
.map(|item| KnowledgeRef {
object_id: item.object_id().to_owned(),
evidence_digest: item.evidence_digest.clone(),
policy_ref: item.policy_ref.clone(),
})
.collect::<Vec<_>>();
let mut hasher = blake3::Hasher::new();
for part in std::iter::once(task_id)
.chain(std::iter::once(policy_version))
.chain(knowledge_refs.iter().flat_map(|reference| {
[
reference.object_id.as_str(),
reference.evidence_digest.as_str(),
reference.policy_ref.as_str(),
]
}))
{
hasher.update(&(part.len() as u64).to_le_bytes());
hasher.update(part.as_bytes());
}
let snapshot_hash = hasher.finalize().to_hex().to_string();
Self {
project_root: String::new(),
project_hash: snapshot_hash.clone(),
facts: Vec::new(),
patterns: Vec::new(),
insights: Vec::new(),
relations: Vec::new(),
task_id: Some(task_id.to_owned()),
policy_version: Some(policy_version.to_owned()),
knowledge_refs,
snapshot_hash: Some(snapshot_hash),
}
}
pub fn is_empty(&self) -> bool {
self.facts.is_empty()
&& self.patterns.is_empty()
&& self.insights.is_empty()
&& self.knowledge_refs.is_empty()
}
pub fn current_facts(&self) -> Vec<&KnowledgeFact> {
self.facts.iter().filter(|f| f.is_current()).collect()
}
}