tj_core/classifier/
mod.rs1use crate::event::{EventType, EvidenceStrength};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize)]
8pub struct ClassifyInput {
9 pub text: String,
10 pub author_hint: String,
11 pub recent_tasks: Vec<TaskContext>,
12}
13
14#[derive(Debug, Clone, Serialize)]
15pub struct TaskContext {
16 pub task_id: String,
17 pub title: String,
18 pub last_events: Vec<String>,
19}
20
21#[derive(Debug, Clone, Deserialize, Serialize)]
22pub struct ClassifyOutput {
23 pub event_type: EventType,
24 pub task_id_guess: Option<String>,
25 pub confidence: f64,
26 pub evidence_strength: Option<EvidenceStrength>,
27 pub suggested_text: String,
28}
29
30pub trait Classifier: Send + Sync {
31 fn classify(&self, input: &ClassifyInput) -> anyhow::Result<ClassifyOutput>;
32}
33
34use crate::event::EventStatus;
35
36pub const CONFIDENCE_THRESHOLD: f64 = 0.85;
37
38pub fn decide_status(confidence: f64) -> EventStatus {
39 if confidence >= CONFIDENCE_THRESHOLD {
40 EventStatus::Confirmed
41 } else {
42 EventStatus::Suggested
43 }
44}
45
46pub mod cli;
47pub mod http;
48pub mod mock;
49pub mod prompt;
50pub mod telemetry;
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 #[test]
56 fn classify_input_serializes() {
57 let i = ClassifyInput {
58 text: "Adopted Rust for the journal".into(),
59 author_hint: "assistant".into(),
60 recent_tasks: vec![],
61 };
62 let s = serde_json::to_string(&i).unwrap();
63 assert!(s.contains("Adopted Rust"));
64 }
65
66 #[test]
67 fn decide_status_high_confidence_is_confirmed() {
68 assert_eq!(decide_status(0.95), EventStatus::Confirmed);
69 assert_eq!(decide_status(0.85), EventStatus::Confirmed);
70 }
71
72 #[test]
73 fn decide_status_low_confidence_is_suggested() {
74 assert_eq!(decide_status(0.84), EventStatus::Suggested);
75 assert_eq!(decide_status(0.0), EventStatus::Suggested);
76 }
77}