ainl_graph_extractor/
extractor.rs1use crate::persona_signals::{extract_pass, PersonaSignalExtractorState};
8use crate::recurrence::update_semantic_recurrence;
9use ainl_memory::{GraphStore, SqliteGraphStore};
10use ainl_persona::{EvolutionEngine, PersonaSnapshot, RawSignal};
11use chrono::{DateTime, Utc};
12
13fn store_has_any_persona_for_agent(store: &SqliteGraphStore, agent_id: &str) -> Result<bool, String> {
14 for n in store.find_by_type("persona")? {
15 if n.agent_id == agent_id {
16 return Ok(true);
17 }
18 }
19 Ok(false)
20}
21
22pub struct GraphExtractorTask {
23 pub agent_id: String,
24 pub evolution_engine: EvolutionEngine,
25 pub signal_state: PersonaSignalExtractorState,
26}
27
28#[derive(Debug, Clone)]
29pub struct ExtractionReport {
30 pub agent_id: String,
31 pub semantic_nodes_updated: usize,
32 pub signals_extracted: usize,
34 pub signals_applied: usize,
36 pub merged_signals: Vec<RawSignal>,
38 pub persona_snapshot: PersonaSnapshot,
39 pub timestamp: DateTime<Utc>,
40}
41
42impl GraphExtractorTask {
43 pub fn new(agent_id: &str) -> Self {
44 Self {
45 agent_id: agent_id.to_string(),
46 evolution_engine: EvolutionEngine::new(agent_id),
47 signal_state: PersonaSignalExtractorState::new(),
48 }
49 }
50
51 pub fn run_pass(&mut self, store: &SqliteGraphStore) -> Result<ExtractionReport, String> {
53 let semantic_nodes_updated = update_semantic_recurrence(store, &self.agent_id)?;
54 let mut signals = self.evolution_engine.extract_signals(store)?;
55 signals.extend(extract_pass(store, &self.agent_id, &mut self.signal_state)?);
56 let signals_extracted = signals.len();
57 let merged_signals = signals.clone();
58 let signals_applied = self.evolution_engine.ingest_signals(signals);
59 let persona_snapshot = self.evolution_engine.snapshot();
60 let should_persist_persona = signals_extracted > 0
64 || semantic_nodes_updated > 0
65 || store_has_any_persona_for_agent(store, &self.agent_id)?;
66 if should_persist_persona {
67 self.evolution_engine
68 .write_persona_node(store, &persona_snapshot)?;
69 }
70 Ok(ExtractionReport {
71 agent_id: self.agent_id.clone(),
72 semantic_nodes_updated,
73 signals_extracted,
74 signals_applied,
75 merged_signals,
76 persona_snapshot,
77 timestamp: Utc::now(),
78 })
79 }
80}