fabula_discovery/
session.rs1use crate::corpus::TraceCorpus;
2use crate::score::{PatternScore, ScoredPattern};
3use crate::traits::{CandidateGenerator, PatternEvaluator, PatternFilter};
4
5#[derive(Debug, Clone)]
7pub struct SessionConfig {
8 pub max_rounds: usize,
10 pub candidates_per_round: usize,
12}
13
14impl Default for SessionConfig {
15 fn default() -> Self {
16 Self {
17 max_rounds: 10,
18 candidates_per_round: 50,
19 }
20 }
21}
22
23#[derive(Debug, Clone)]
25pub struct SessionHistory {
26 pub rounds: usize,
28 pub all_scored: Vec<ScoredPattern<String, String>>,
30 pub accepted: Vec<ScoredPattern<String, String>>,
32}
33
34pub struct DiscoverySession {
36 config: SessionConfig,
37}
38
39impl DiscoverySession {
40 pub fn new(config: SessionConfig) -> Self {
42 Self { config }
43 }
44
45 pub fn run(
53 &mut self,
54 corpus: &TraceCorpus,
55 mut generator: impl CandidateGenerator,
56 evaluators: Vec<Box<dyn PatternEvaluator>>,
57 filter: impl PatternFilter,
58 ) -> SessionHistory {
59 let mut all_scored = Vec::new();
60 let mut accepted = Vec::new();
61 let mut rounds_run = 0;
62
63 for round in 0..self.config.max_rounds {
64 let candidates = generator.generate(corpus, self.config.candidates_per_round);
65
66 if candidates.is_empty() {
67 break;
68 }
69
70 rounds_run += 1;
71 let mut round_scored = Vec::new();
72 for pattern in candidates {
73 let mut scores = std::collections::HashMap::new();
74 for evaluator in &evaluators {
75 let value = evaluator.evaluate(&pattern, corpus);
76 scores.insert(evaluator.name().to_string(), value);
77 }
78
79 let scored = ScoredPattern {
80 pattern,
81 score: PatternScore {
82 scores,
83 round,
84 generator: generator.name().to_string(),
85 },
86 };
87
88 if filter.accept(&scored) {
89 accepted.push(scored.clone());
90 }
91
92 round_scored.push(scored);
93 }
94
95 generator.feedback(&round_scored);
96 all_scored.extend(round_scored);
97 }
98
99 SessionHistory {
100 rounds: rounds_run,
101 all_scored,
102 accepted,
103 }
104 }
105}