Weksa/crates/weksa/tests/common/mod.rs

274 lines
7.6 KiB
Rust

#![allow(dead_code)]
use std::sync::OnceLock;
use tempfile::TempDir;
use weksa::inventory::{Change, seed};
use weksa::packet::*;
use weksa::{
CorpusItem, CorpusStore, InvId, Inventory, InventoryEntry, InventoryRevision, InventoryStore,
OptionLabel, Slot, Source, SplitScheme, ValueKind,
};
pub const PERSON: &str = "referent_kind:1";
pub const AGENT: &str = "role:1";
pub const PATIENT: &str = "role:2";
pub const WARN: &str = "speech_act:5";
pub const POLARITY: &str = "feature:1";
pub const MODALITY: &str = "feature:4";
pub const HEDGE: &str = "feature:6";
pub const IRONY: &str = "feature:11";
/// Introduced at rev 2.
pub const CONCEPT_A: &str = "concept:1";
pub const CONCEPT_B: &str = "concept:2";
/// Introduced at rev 2, retired at rev 3.
pub const CONCEPT_RETIRED: &str = "concept:3";
/// Introduced at rev 4.
pub const CONCEPT_LATE: &str = "concept:4";
pub const FRAME: &str = "frame:1";
/// An extension entry of value kind Grade, introduced at rev 2.
pub const EXT: &str = "extension:1";
/// A Grade feature introduced at rev 2 that sits in every slot.
pub const FEAT_ALL: &str = "feature:50";
pub const PACKET_ID: &str = "packet:0123456789abcdef";
pub const ENCODER_ID: &str = "encoder:fedcba9876543210";
pub fn iv(s: &str) -> InvId {
s.parse().unwrap()
}
pub fn nd(s: &str) -> NodeId {
NodeId::parse(s).unwrap()
}
pub fn lab(s: &str) -> OptionLabel {
OptionLabel::parse(s).unwrap()
}
pub fn grade(v: u16) -> Grade {
Grade::new(v).unwrap()
}
pub fn group_in_bucket(want: u64) -> String {
(0..100_000)
.map(|i| format!("g{i}"))
.find(|g| SplitScheme::V1.bucket(g) == want)
.unwrap_or_else(|| panic!("no group lands in bucket {want}"))
}
pub fn cast() -> Source {
Source::CastAuthored {
persona: "nibu".into(),
fixture: "f.yaml".into(),
}
}
pub fn model() -> Source {
Source::ModelWritten {
model_family: "m".into(),
prompt_digest: None,
}
}
pub fn item_in(key: &str, text: &str, group: &str, source: Source) -> CorpusItem {
CorpusItem {
id: key.into(),
text: text.into(),
language: "en-US".into(),
speaker: "nibu".into(),
group: group.into(),
prior_turns: vec![],
source,
admitted_on: "2026-10-29".into(),
supersedes: None,
withdrawn: false,
}
}
pub fn entry(id: &str, introduced: u32) -> InventoryEntry {
InventoryEntry {
id: iv(id),
gloss: id.into(),
value_kind: None,
applies_to: vec![],
introduced_rev: introduced,
retired_rev: None,
}
}
pub fn feature(id: &str, kind: ValueKind, slots: &[Slot]) -> InventoryEntry {
InventoryEntry {
value_kind: Some(kind),
applies_to: slots.to_vec(),
..entry(id, 1)
}
}
pub fn rev_with(changes: Vec<Change>, n: u32, parent: Option<u32>) -> InventoryRevision {
InventoryRevision {
n,
parent,
changes,
frozen: false,
}
}
/// Admits `entries` as revision `n` and returns nothing; the stores are the only door.
pub fn admit(store: &mut InventoryStore, n: u32, entries: Vec<InventoryEntry>) {
let changes = entries.iter().map(|e| Change::Add(e.id)).collect();
let parent = n.checked_sub(1).filter(|p| *p > 0);
store
.admit_revision(rev_with(changes, n, parent), entries)
.unwrap();
}
/// Stores a test can admit through: a corpus holding `item:nibu:1` in a Train
/// group, and an inventory of revisions 1 (the seed), 2 (concepts A, B,
/// RETIRED, a frame, a Grade extension), 3 (RETIRED retired) and 4 (LATE).
pub struct World {
pub dir: TempDir,
pub inventory: InventoryStore,
pub corpus: CorpusStore,
}
impl World {
pub fn inv(&self) -> Inventory {
self.inventory.inventory().unwrap()
}
}
pub fn world() -> World {
let dir = tempfile::tempdir().unwrap();
let mut corpus = CorpusStore::open(&dir.path().join("c.cc")).unwrap();
let train = group_in_bucket(10);
corpus
.add(item_in(
"item:nibu:1",
"Nibu says mind the hatch.",
&train,
cast(),
))
.unwrap();
let mut inventory = InventoryStore::open(&dir.path().join("i.cc")).unwrap();
let (rev1, entries) = seed();
inventory.admit_revision(rev1, entries).unwrap();
let mut ext = entry(EXT, 2);
ext.value_kind = Some(ValueKind::Grade);
admit(
&mut inventory,
2,
vec![
entry(CONCEPT_A, 2),
entry(CONCEPT_B, 2),
entry(CONCEPT_RETIRED, 2),
entry(FRAME, 2),
ext,
InventoryEntry {
introduced_rev: 2,
..feature(
FEAT_ALL,
ValueKind::Grade,
&[
Slot::Discourse,
Slot::Referent,
Slot::Predication,
Slot::Relation,
],
)
},
],
);
inventory
.admit_revision(
rev_with(vec![Change::Retire(iv(CONCEPT_RETIRED))], 3, Some(2)),
vec![],
)
.unwrap();
admit(&mut inventory, 4, vec![entry(CONCEPT_LATE, 4)]);
World {
dir,
inventory,
corpus,
}
}
/// The world's inventory, built once.
pub fn inventory() -> Inventory {
static INV: OnceLock<Inventory> = OnceLock::new();
INV.get_or_init(|| world().inv()).clone()
}
pub fn good() -> Packet {
Packet {
id: PacketId::parse(PACKET_ID).unwrap(),
schema_version: SCHEMA_VERSION,
inventory_rev: 2,
kind: PacketKind::DialogueLine,
provenance: Provenance {
source: ProvenanceSource::Authored,
corpus_item: Some("item:nibu:1".into()),
encoder: EncoderId::parse(ENCODER_ID).unwrap(),
confidence: grade(900),
},
context: Context {
setting: Some(iv(CONCEPT_A)),
medium: Some(iv(CONCEPT_B)),
participants: vec![nd("n1")],
audience: vec![nd("n1")],
},
discourse: Discourse {
speech_act: Some(iv(WARN)),
addressee: Some(nd("n1")),
features: vec![FeatureValue {
feature: iv(HEDGE),
value: FeatValue::Grade(grade(300)),
}],
},
referents: vec![Referent {
id: nd("n1"),
kind: iv(PERSON),
concept: iv(CONCEPT_A),
name: Some(ProperName::parse("Nibu").unwrap()),
features: vec![],
}],
predications: vec![Predication {
id: nd("n2"),
concept: iv(CONCEPT_B),
frame: iv(FRAME),
roles: vec![
RoleFill {
role: iv(AGENT),
filler: nd("n1"),
},
RoleFill {
role: iv(PATIENT),
filler: nd("n1"),
},
],
features: vec![FeatureValue {
feature: iv(MODALITY),
value: FeatValue::Option(lab("potential")),
}],
}],
relations: vec![Relation {
relation: iv("relation:8"),
from: nd("n2"),
to: nd("n1"),
features: vec![],
}],
constraints: Constraints {
preserve: vec![nd("n1")],
deterministic: true,
max_latency_ms: Some(50),
},
extensions: vec![Extension {
key: iv(EXT),
value: FeatValue::Grade(grade(3)),
}],
trace: Trace {
notes: vec!["a note".into()],
rejected_readings: vec![],
},
}
}