Switch classifier sessions onto bread-onnx v0.7.2
Replace local ort Session::builder/ROCm wiring with bread_onnx::build_session (MIGraphX + CPU fallback, same pin as breadmill). Drop the breadman chip/init_adw shim now that bread-theme v0.7.4 exports those helpers.
This commit is contained in:
parent
51338af7eb
commit
39e26a1b9f
11 changed files with 254 additions and 137 deletions
|
|
@ -1,17 +1,24 @@
|
|||
use breadpad_shared::classifier::{Classifier, ExecutionProvider};
|
||||
use breadpad_shared::types::NoteType;
|
||||
use chrono::Timelike;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Rule-based path only — a present `~/.local/share/breadpad/model` must not
|
||||
/// change these assertions.
|
||||
fn cl() -> Classifier {
|
||||
Classifier::load("08:00")
|
||||
Classifier::load_with_paths(
|
||||
"08:00",
|
||||
PathBuf::from("/nonexistent/classifier.onnx"),
|
||||
PathBuf::from("/nonexistent/tokenizer.json"),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_provider_is_valid() {
|
||||
// The active provider depends on the host: a machine with the ONNX model present and
|
||||
// a working ROCm iGPU loads `Gpu`, otherwise `Cpu`. Either is valid — but when no
|
||||
// a working MIGraphX iGPU loads `Gpu`, otherwise `Cpu`. Either is valid — but when no
|
||||
// model is available we must be on CPU (no session => no GPU EP in use).
|
||||
let c = cl();
|
||||
let c = Classifier::load("08:00");
|
||||
assert!(matches!(
|
||||
c.active_provider,
|
||||
ExecutionProvider::Cpu | ExecutionProvider::Gpu
|
||||
|
|
@ -49,19 +56,28 @@ fn classify_reminder_via_fallback() {
|
|||
#[test]
|
||||
fn classify_idea_via_fallback() {
|
||||
let mut c = cl();
|
||||
assert_eq!(c.classify("what if we added a calendar view").note_type, NoteType::Idea);
|
||||
assert_eq!(
|
||||
c.classify("what if we added a calendar view").note_type,
|
||||
NoteType::Idea
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify_question_via_fallback() {
|
||||
let mut c = cl();
|
||||
assert_eq!(c.classify("why does this fail?").note_type, NoteType::Question);
|
||||
assert_eq!(
|
||||
c.classify("why does this fail?").note_type,
|
||||
NoteType::Question
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify_note_via_fallback() {
|
||||
let mut c = cl();
|
||||
assert_eq!(c.classify("meeting went well today").note_type, NoteType::Note);
|
||||
assert_eq!(
|
||||
c.classify("meeting went well today").note_type,
|
||||
NoteType::Note
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -74,7 +90,11 @@ fn classify_recurrence_via_fallback() {
|
|||
|
||||
#[test]
|
||||
fn classify_custom_morning_time() {
|
||||
let mut c = Classifier::load("07:15");
|
||||
let mut c = Classifier::load_with_paths(
|
||||
"07:15",
|
||||
PathBuf::from("/nonexistent/classifier.onnx"),
|
||||
PathBuf::from("/nonexistent/tokenizer.json"),
|
||||
);
|
||||
let r = c.classify("sync tomorrow morning");
|
||||
let t = r.time.expect("should have a time for tomorrow morning");
|
||||
let local: chrono::DateTime<chrono::Local> = t.into();
|
||||
|
|
@ -114,12 +134,16 @@ fn classify_returns_cleaned_body() {
|
|||
let mut c = cl();
|
||||
let r = c.classify("call mum at 6pm");
|
||||
assert!(r.body.contains("call mum"), "body: {}", r.body);
|
||||
assert!(!r.body.contains("6pm"), "time phrase should be stripped from body: {}", r.body);
|
||||
assert!(
|
||||
!r.body.contains("6pm"),
|
||||
"time phrase should be stripped from body: {}",
|
||||
r.body
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn model_path_points_to_expected_location() {
|
||||
let c = cl();
|
||||
let c = Classifier::load("08:00");
|
||||
assert!(
|
||||
c.model_path.to_str().unwrap().contains("breadpad"),
|
||||
"model path: {:?}",
|
||||
|
|
|
|||
|
|
@ -9,12 +9,18 @@ use breadpad_shared::classifier::Classifier;
|
|||
use breadpad_shared::store::Store;
|
||||
use breadpad_shared::types::{Note, NoteType};
|
||||
use chrono::Timelike;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
|
||||
// Mirrors commit_note() in breadpad/src/main.rs.
|
||||
// `user_type` is the type the user selected in the chip row (default = NoteType::Note).
|
||||
fn capture(store: &Store, text: &str, user_type: NoteType) -> Note {
|
||||
let mut classifier = Classifier::load("08:00");
|
||||
// WHY: pipeline tests cover classify→save→reload, not a host ONNX model.
|
||||
let mut classifier = Classifier::load_with_paths(
|
||||
"08:00",
|
||||
PathBuf::from("/nonexistent/classifier.onnx"),
|
||||
PathBuf::from("/nonexistent/tokenizer.json"),
|
||||
);
|
||||
let result = classifier.classify(text);
|
||||
|
||||
let mut note = Note::new(text.into(), user_type.clone(), None);
|
||||
|
|
@ -61,7 +67,11 @@ fn todo_note_appears_in_store() {
|
|||
#[test]
|
||||
fn idea_note_appears_in_store() {
|
||||
let (dir, store) = setup();
|
||||
capture(&store, "what if we added dark mode", NoteType::from_str("note"));
|
||||
capture(
|
||||
&store,
|
||||
"what if we added dark mode",
|
||||
NoteType::from_str("note"),
|
||||
);
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert_eq!(notes.len(), 1);
|
||||
|
|
@ -71,7 +81,11 @@ fn idea_note_appears_in_store() {
|
|||
#[test]
|
||||
fn question_note_appears_in_store() {
|
||||
let (dir, store) = setup();
|
||||
capture(&store, "why does the cache miss on cold start?", NoteType::from_str("note"));
|
||||
capture(
|
||||
&store,
|
||||
"why does the cache miss on cold start?",
|
||||
NoteType::from_str("note"),
|
||||
);
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert_eq!(notes.len(), 1);
|
||||
|
|
@ -97,7 +111,10 @@ fn reminder_has_time_set() {
|
|||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert_eq!(notes[0].note_type, NoteType::Reminder);
|
||||
assert!(notes[0].time.is_some(), "reminder should have a scheduled time");
|
||||
assert!(
|
||||
notes[0].time.is_some(),
|
||||
"reminder should have a scheduled time"
|
||||
);
|
||||
let local: chrono::DateTime<chrono::Local> = notes[0].time.unwrap().into();
|
||||
assert_eq!(local.hour(), 18);
|
||||
}
|
||||
|
|
@ -108,14 +125,21 @@ fn reminder_body_has_time_stripped() {
|
|||
capture(&store, "call mum at 6pm", NoteType::from_str("note"));
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert!(!notes[0].body.contains("6pm"), "time phrase should be removed from body");
|
||||
assert!(
|
||||
!notes[0].body.contains("6pm"),
|
||||
"time phrase should be removed from body"
|
||||
);
|
||||
assert!(notes[0].body.contains("call mum"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_duration_reminder_has_time() {
|
||||
let (dir, store) = setup();
|
||||
capture(&store, "check on the build in 30 minutes", NoteType::from_str("note"));
|
||||
capture(
|
||||
&store,
|
||||
"check on the build in 30 minutes",
|
||||
NoteType::from_str("note"),
|
||||
);
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert_eq!(notes[0].note_type, NoteType::Reminder);
|
||||
|
|
@ -127,7 +151,11 @@ fn in_duration_reminder_has_time() {
|
|||
#[test]
|
||||
fn recurring_reminder_has_rrule() {
|
||||
let (dir, store) = setup();
|
||||
capture(&store, "standup every monday at 9am", NoteType::from_str("note"));
|
||||
capture(
|
||||
&store,
|
||||
"standup every monday at 9am",
|
||||
NoteType::from_str("note"),
|
||||
);
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert_eq!(notes[0].note_type, NoteType::Reminder);
|
||||
|
|
@ -139,11 +167,20 @@ fn recurring_reminder_has_rrule() {
|
|||
#[test]
|
||||
fn daily_reminder_has_rrule() {
|
||||
let (dir, store) = setup();
|
||||
capture(&store, "drink water every day at 8am", NoteType::from_str("note"));
|
||||
capture(
|
||||
&store,
|
||||
"drink water every day at 8am",
|
||||
NoteType::from_str("note"),
|
||||
);
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert_eq!(notes[0].note_type, NoteType::Reminder);
|
||||
assert!(notes[0].rrule.as_ref().unwrap().as_str().contains("FREQ=DAILY"));
|
||||
assert!(notes[0]
|
||||
.rrule
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.contains("FREQ=DAILY"));
|
||||
}
|
||||
|
||||
// ---- user-forced type is respected ----
|
||||
|
|
@ -155,7 +192,11 @@ fn user_selected_type_overrides_classifier() {
|
|||
capture(&store, "fix the login bug", NoteType::Idea);
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
assert_eq!(notes[0].note_type, NoteType::Idea, "user chip selection should win over classifier");
|
||||
assert_eq!(
|
||||
notes[0].note_type,
|
||||
NoteType::Idea,
|
||||
"user chip selection should win over classifier"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -173,7 +214,11 @@ fn user_selected_reminder_overrides_classifier() {
|
|||
fn three_notes_all_visible_to_breadman() {
|
||||
let (dir, store) = setup();
|
||||
capture(&store, "buy milk", NoteType::from_str("note"));
|
||||
capture(&store, "what if we rewrote in Zig", NoteType::from_str("note"));
|
||||
capture(
|
||||
&store,
|
||||
"what if we rewrote in Zig",
|
||||
NoteType::from_str("note"),
|
||||
);
|
||||
capture(&store, "team standup went well", NoteType::from_str("note"));
|
||||
|
||||
let notes = breadman_store(&dir).load_all().unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue