language-ontology-scene s1-2: the run pins the speaker-state store, assembled is the one scene door, select reads slot Scene, Prompt::lower refuses an unread scene, weksa scene put|dump, speaker_scene_revision(n)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3cc06693fb
commit
f7f4e58d78
17 changed files with 388 additions and 45 deletions
|
|
@ -259,6 +259,7 @@ fn lower_error(e: &LowerError) -> Value {
|
|||
LowerError::ConsentMissing { predication } => ("ConsentMissing", format!("{predication} is a request the packet does not carry the addressee's consent to")),
|
||||
LowerError::AskerNotAddressee { predication } => ("AskerNotAddressee", format!("the agent of {predication} is not the addressee")),
|
||||
LowerError::EntityRead { entity, .. } => ("EntityRead", format!("the entity store failed reading {entity}")),
|
||||
LowerError::SceneUnread => ("SceneUnread", "the view's scene was never read".to_string()),
|
||||
};
|
||||
json!({ "variant": variant, "reason": reason })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ use crate::packet::{
|
|||
use crate::run::LADDER_STEP;
|
||||
use crate::split::{Checkpoint, Split, SplitScheme, fnv1a64, hex16};
|
||||
use crate::store::{CorpusStore, normalized};
|
||||
use crate::validate::LoweringInput;
|
||||
use crate::validate::{LoweringInput, SceneRead};
|
||||
|
||||
/// The instructions a mint prompt starts with: the editor's whole brief. Fixed
|
||||
/// per run by `PROMPT_SOURCE_DIGEST`.
|
||||
|
|
@ -71,7 +71,7 @@ A feature is {feature, value}; value is {\"Grade\": 0 to 1000}, {\"Option\": lab
|
|||
/// Fixed per run by `PROMPT_SOURCE_DIGEST`.
|
||||
pub const LOWER_INSTRUCTIONS: &str = "\
|
||||
Say the packet below in the language on the language line, as one utterance of its speaker: all of its meaning and nothing it does not carry.
|
||||
Every id is the inventory's listed below and every feature value is as listed; a grade is permille, 0 to 1000. The stance is the speaker's felt affect and restraint: it shapes how the utterance is said, not what it says. The ontology lines are choices this language makes for this speaker and stance: realize each where the packet allows; they add no meaning.
|
||||
Every id is the inventory's listed below and every feature value is as listed; a grade is permille, 0 to 1000. The stance is the speaker's felt affect and restraint: it shapes how the utterance is said, not what it says. The ontology lines are choices this language makes for this speaker, stance and scene: realize each where the packet allows; they add no meaning.
|
||||
A referent with an entity is named by one of that entity's forms on the entities line.
|
||||
Answer with one JSON object and nothing else: {\"text\": \"...\"}. No code fence, no commentary.";
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ Answer with one JSON object and nothing else: {\"text\": \"...\"}, the continuat
|
|||
/// ```no_run
|
||||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc") };
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc"), speaker_state: &p("s.cc") };
|
||||
/// # let panel = ["en-US".to_string()];
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded, &panel).unwrap();
|
||||
/// let t = run.train().unwrap();
|
||||
|
|
@ -104,7 +104,7 @@ Answer with one JSON object and nothing else: {\"text\": \"...\"}, the continuat
|
|||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.cc", "o.cc"] { std::fs::write(p(n), [0x90]).unwrap(); }
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc") };
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc"), speaker_state: &p("s.cc") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded, &["en-US".to_string()]).unwrap();
|
||||
/// let t = run.train().unwrap();
|
||||
/// let _u = t.clone();
|
||||
|
|
@ -115,7 +115,7 @@ Answer with one JSON object and nothing else: {\"text\": \"...\"}, the continuat
|
|||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.cc", "o.cc"] { std::fs::write(p(n), [0x90]).unwrap(); }
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc") };
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc"), speaker_state: &p("s.cc") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded, &["en-US".to_string()]).unwrap();
|
||||
/// let t = run.train().unwrap();
|
||||
/// s(&t);
|
||||
|
|
@ -337,7 +337,7 @@ impl EditFeedback {
|
|||
/// ```no_run
|
||||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc") };
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc"), speaker_state: &p("s.cc") };
|
||||
/// # let panel = ["en-US".to_string()];
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded, &panel).unwrap();
|
||||
/// let t = run.train().unwrap();
|
||||
|
|
@ -356,7 +356,7 @@ impl EditFeedback {
|
|||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.cc", "o.cc"] { std::fs::write(p(n), [0x90]).unwrap(); }
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc") };
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc"), speaker_state: &p("s.cc") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded, &["en-US".to_string()]).unwrap();
|
||||
/// let t = run.train().unwrap();
|
||||
/// let inv = run.inventory().inventory().unwrap();
|
||||
|
|
@ -369,7 +369,7 @@ impl EditFeedback {
|
|||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.cc", "o.cc"] { std::fs::write(p(n), [0x90]).unwrap(); }
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc") };
|
||||
/// # let paths = weksa::run::RunPaths { corpus: &p("c.cc"), inventory: &p("i.cc"), entities: &p("e.cc"), ontology: &p("o.cc"), speaker_state: &p("s.cc") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded, &["en-US".to_string()]).unwrap();
|
||||
/// let t = run.train().unwrap();
|
||||
/// let inv = run.inventory().inventory().unwrap();
|
||||
|
|
@ -528,9 +528,11 @@ impl Prompt {
|
|||
/// `input` (no kind, constraints, scene, speaker or group), the forms in
|
||||
/// `language` of every entity its referents cite, the `ontology:` section
|
||||
/// and the entries of `inventory` live at the view's revision. The section
|
||||
/// holds the entries `ontology::select` chooses from the view's stance and
|
||||
/// discourse features and `language`, never the caller; the view's speaker
|
||||
/// and group are still never read. `item`
|
||||
/// holds the entries `ontology::select` chooses from the view's stance,
|
||||
/// discourse and the scene relation `with_scene` read, and `language`,
|
||||
/// never the caller; the scene's values are never printed, only the entries
|
||||
/// they select. A view whose scene was never read is refused
|
||||
/// ([`LowerError::SceneUnread`]). `item`
|
||||
/// goes only to [`Prompt::item_keys`], never into the text. A speaker's
|
||||
/// entity id appears
|
||||
/// only when the encoder cites that entity on a referent, which is naming a
|
||||
|
|
@ -545,6 +547,9 @@ impl Prompt {
|
|||
entities: &dyn EntityForms,
|
||||
ontology: &Ontology,
|
||||
) -> Result<Prompt, LowerError> {
|
||||
if input.scene_read() == &SceneRead::Unread {
|
||||
return Err(LowerError::SceneUnread);
|
||||
}
|
||||
let mut forms: BTreeMap<&str, Vec<String>> = BTreeMap::new();
|
||||
for id in input.referents().iter().filter_map(|r| r.entity.as_ref()) {
|
||||
let found = entity(entities, id)?;
|
||||
|
|
|
|||
|
|
@ -551,7 +551,7 @@ pub fn proof_revision() -> (InventoryRevision, Vec<InventoryEntry>) {
|
|||
/// arrive as community ids with cut `language-ontology`. Hand-seeded like
|
||||
/// [`proof_revision`] until the loop's minting round owns the vocabulary; the
|
||||
/// features take the ids after the seed's.
|
||||
pub fn speaker_scene_revision() -> (InventoryRevision, Vec<InventoryEntry>) {
|
||||
pub fn speaker_scene_revision(n: u32) -> (InventoryRevision, Vec<InventoryEntry>) {
|
||||
let taken = seed()
|
||||
.1
|
||||
.iter()
|
||||
|
|
@ -593,15 +593,15 @@ pub fn speaker_scene_revision() -> (InventoryRevision, Vec<InventoryEntry>) {
|
|||
gloss: gloss.to_string(),
|
||||
value_kind: Some(kind),
|
||||
applies_to: vec![slot],
|
||||
introduced_rev: 3,
|
||||
introduced_rev: n,
|
||||
retired_rev: None,
|
||||
});
|
||||
}
|
||||
let changes = entries.iter().map(|e| Change::Add(e.id)).collect();
|
||||
(
|
||||
InventoryRevision {
|
||||
n: 3,
|
||||
parent: Some(2),
|
||||
n,
|
||||
parent: Some(n - 1),
|
||||
changes,
|
||||
frozen: false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ pub enum LowerError {
|
|||
AskerNotAddressee { predication: NodeId },
|
||||
/// The entity port failed to read.
|
||||
EntityRead { entity: EntityId, why: String },
|
||||
/// A lowering prompt was asked of a view whose scene was never read.
|
||||
SceneUnread,
|
||||
}
|
||||
|
||||
impl fmt::Display for LowerError {
|
||||
|
|
@ -203,6 +205,7 @@ impl fmt::Display for LowerError {
|
|||
LowerError::ConsentMissing { .. } => "request lacks the addressee's consent",
|
||||
LowerError::AskerNotAddressee { .. } => "request agent is not the addressee",
|
||||
LowerError::EntityRead { .. } => "entity port failed to read",
|
||||
LowerError::SceneUnread => "the view's scene was never read",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ use unicode_normalization::UnicodeNormalization;
|
|||
use weksa::corpus::{CorpusItem, ModelFamily, NameSpan, Source, Turn};
|
||||
use weksa::encode::OntologyDraft;
|
||||
use weksa::entity::{Entity, EntityOrigin, LanguageForms, Pronunciation};
|
||||
use weksa::inventory::{proof_revision, seed};
|
||||
use weksa::inventory::{proof_revision, seed, speaker_scene_revision};
|
||||
use weksa::packet::{FeatValue, FeatureValue, Grade};
|
||||
use weksa::packet::proof_packet;
|
||||
use weksa::model::RecordedAnswers;
|
||||
use weksa::run::{
|
||||
|
|
@ -18,8 +19,8 @@ use weksa::run::{
|
|||
};
|
||||
use weksa::score::ScoreInput;
|
||||
use weksa::{
|
||||
Annotator, AnswerFiles, CorpusStore, EntityId, EntityStore, InventoryStore, OllamaEmbedder, OntologyStore, PacketStore,
|
||||
Scorer, Split, SplitScheme, StoreError, lower, pronounce, validate,
|
||||
Annotator, AnswerFiles, CorpusStore, EntityId, EntityStore, Family, InventoryStore, OllamaEmbedder, OntologyStore, OptionLabel,
|
||||
PacketStore, PairRelation, Party, Scene, Slot, SpeakerStateStore, ValueKind, Scorer, Split, SplitScheme, StoreError, lower, pronounce, validate,
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
|
|
@ -40,6 +41,9 @@ struct Cli {
|
|||
/// The language ontology store.
|
||||
#[arg(long, global = true, default_value = "data/ontology/ontology.cc")]
|
||||
ontology: PathBuf,
|
||||
/// The speaker-state store (scenes).
|
||||
#[arg(long, global = true, default_value = "data/corpus/speaker-state.cc")]
|
||||
speaker_state: PathBuf,
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
|
@ -57,6 +61,8 @@ enum Command {
|
|||
#[command(subcommand)]
|
||||
Ontology(OntologyCommand),
|
||||
#[command(subcommand)]
|
||||
Scene(SceneCommand),
|
||||
#[command(subcommand)]
|
||||
Packet(PacketCommand),
|
||||
/// Lower a stored packet to English text.
|
||||
Lower {
|
||||
|
|
@ -87,7 +93,7 @@ enum Command {
|
|||
|
||||
#[derive(Subcommand)]
|
||||
enum LoopCommand {
|
||||
/// Pin the global --corpus, --inventory, --entities and --ontology as a run at ROOT.
|
||||
/// Pin the global --corpus, --inventory, --entities, --ontology and --speaker-state as a run at ROOT.
|
||||
Open {
|
||||
root: PathBuf,
|
||||
#[arg(long)]
|
||||
|
|
@ -191,6 +197,38 @@ enum InventoryCommand {
|
|||
Proof,
|
||||
/// Admit revision 3 of the Dao De Jing test bed stores (after seed and proof).
|
||||
Dao,
|
||||
/// Admit the speaker-state revision (Scene and Speaker features) as the
|
||||
/// inventory's latest plus one.
|
||||
SpeakerScene,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum SceneCommand {
|
||||
/// Append a scene revision for one corpus group: its one live speaker's
|
||||
/// relation to the addressee, as Scene-slot feature values.
|
||||
Put {
|
||||
#[arg(long)]
|
||||
group: String,
|
||||
/// `entity:<slug>` of a named addressee, or `unnamed`.
|
||||
#[arg(long)]
|
||||
to: String,
|
||||
/// The inventory revision the glosses resolve at.
|
||||
#[arg(long)]
|
||||
inventory_rev: u32,
|
||||
/// `gloss=value`, repeated: a grade 0-1000, an option label or true/false.
|
||||
#[arg(long = "set", value_name = "GLOSS=VALUE")]
|
||||
set: Vec<String>,
|
||||
#[arg(long, requires = "generator", conflicts_with = "operator")]
|
||||
model_family: Option<ModelFamilyArg>,
|
||||
/// The attestation string naming the annotating model.
|
||||
#[arg(long, requires = "model_family")]
|
||||
generator: Option<String>,
|
||||
/// Annotated by the operator, not a model.
|
||||
#[arg(long)]
|
||||
operator: bool,
|
||||
},
|
||||
/// Text projection of the scenes for review; not a store.
|
||||
Dump,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
|
|
@ -734,6 +772,143 @@ fn main() -> Result<()> {
|
|||
.map_err(refused)?;
|
||||
println!("admitted rev:2 with {count} entries");
|
||||
}
|
||||
Command::Inventory(InventoryCommand::SpeakerScene) => {
|
||||
let store = InventoryStore::open(&cli.inventory)?;
|
||||
let latest = store
|
||||
.inventory()?
|
||||
.latest()
|
||||
.map(|r| r.n)
|
||||
.context("the inventory holds no revision")?;
|
||||
let (rev, entries) = speaker_scene_revision(latest + 1);
|
||||
let (n, count) = (rev.n, entries.len());
|
||||
let mut store = store;
|
||||
store.admit_revision(rev, entries).map_err(refused)?;
|
||||
println!("admitted rev:{n} with {count} entries");
|
||||
}
|
||||
Command::Scene(SceneCommand::Put {
|
||||
group,
|
||||
to,
|
||||
inventory_rev,
|
||||
set,
|
||||
model_family,
|
||||
generator,
|
||||
operator,
|
||||
}) => {
|
||||
let annotator = match (model_family, generator, operator) {
|
||||
(Some(family), Some(generator), false) => Annotator::Model {
|
||||
family: family.into(),
|
||||
generator,
|
||||
},
|
||||
(None, None, true) => Annotator::Operator,
|
||||
_ => bail!("name the annotator: --model-family with --generator, or --operator"),
|
||||
};
|
||||
let inventory_store = InventoryStore::open(&cli.inventory)?;
|
||||
let inventory = inventory_store.inventory()?;
|
||||
let corpus = CorpusStore::open(&cli.corpus)?;
|
||||
let live: Vec<CorpusItem> = corpus
|
||||
.all()?
|
||||
.into_iter()
|
||||
.filter(|i| i.group == group && !i.withdrawn)
|
||||
.collect();
|
||||
let mut speakers: Vec<EntityId> = live.iter().map(|i| i.speaker.clone()).collect();
|
||||
speakers.sort();
|
||||
speakers.dedup();
|
||||
let [from] = speakers.as_slice() else {
|
||||
bail!("a scene group holds one live speaker; multi-party scenes wait");
|
||||
};
|
||||
let to = match to.as_str() {
|
||||
"unnamed" => Party::Unnamed,
|
||||
named => Party::Named(
|
||||
EntityId::parse(named).map_err(|_| anyhow::anyhow!("the addressee is unnamed or an entity id"))?,
|
||||
),
|
||||
};
|
||||
let mut features = Vec::new();
|
||||
for pair in &set {
|
||||
let bad = || anyhow::anyhow!("a --set is gloss=value of a live Scene feature");
|
||||
let (gloss, value) = pair.split_once('=').ok_or_else(bad)?;
|
||||
let entry = inventory
|
||||
.entries()
|
||||
.find(|e| {
|
||||
e.family() == Family::Feature
|
||||
&& e.gloss == gloss
|
||||
&& e.live_at(inventory_rev)
|
||||
&& e.applies_to.contains(&Slot::Scene)
|
||||
})
|
||||
.ok_or_else(bad)?;
|
||||
let value = match entry.value_kind.as_ref().ok_or_else(bad)? {
|
||||
ValueKind::Grade => FeatValue::Grade(
|
||||
Grade::new(value.parse().map_err(|_| bad())?).map_err(|_| bad())?,
|
||||
),
|
||||
ValueKind::Options(_) => {
|
||||
FeatValue::Option(OptionLabel::parse(value).map_err(|_| bad())?)
|
||||
}
|
||||
ValueKind::Flag => FeatValue::Flag(value.parse().map_err(|_| bad())?),
|
||||
};
|
||||
features.push(FeatureValue { feature: entry.id.clone(), value });
|
||||
}
|
||||
let mut participants = vec![from.clone()];
|
||||
if let Party::Named(a) = &to
|
||||
&& a != from
|
||||
{
|
||||
participants.push(a.clone());
|
||||
}
|
||||
let mut state = SpeakerStateStore::open(&cli.speaker_state)?;
|
||||
let latest = state.scene(&group)?;
|
||||
let mut scene = Scene {
|
||||
group,
|
||||
n: latest.as_ref().map_or(1, |l| l.n + 1),
|
||||
inventory_rev,
|
||||
participants,
|
||||
relations: vec![PairRelation { from: from.clone(), to, features }],
|
||||
annotator,
|
||||
};
|
||||
if let Some(l) = &latest {
|
||||
let same = Scene { n: l.n, ..scene.clone() };
|
||||
if &same == l {
|
||||
println!("unchanged");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
scene.n = latest.map_or(1, |l| l.n + 1);
|
||||
let key = weksa::speaker::scene_key(&scene.group, scene.n);
|
||||
let outcome = state
|
||||
.put_scene(scene, &inventory_store, &corpus, &EntityStore::open(&cli.entities)?)
|
||||
.map_err(refused)?;
|
||||
println!("{outcome:?} {key}");
|
||||
}
|
||||
Command::Scene(SceneCommand::Dump) => {
|
||||
let inventory = InventoryStore::open(&cli.inventory)?.inventory()?;
|
||||
let gloss = |f: &FeatureValue| {
|
||||
let name = inventory.entry(&f.feature).map_or("?", |e| e.gloss.as_str());
|
||||
match &f.value {
|
||||
FeatValue::Grade(g) => format!("{name}={}", g.get()),
|
||||
FeatValue::Option(l) => format!("{name}={l}"),
|
||||
FeatValue::Flag(b) => format!("{name}={b}"),
|
||||
}
|
||||
};
|
||||
for s in SpeakerStateStore::open(&cli.speaker_state)?.all_scenes()? {
|
||||
let who = match &s.annotator {
|
||||
Annotator::Operator => "Operator".to_string(),
|
||||
Annotator::Model { family, generator } => format!("Model {family:?} {generator}"),
|
||||
};
|
||||
let people: Vec<&str> = s.participants.iter().map(|p| p.as_str()).collect();
|
||||
println!(
|
||||
"scene:{}@{} rev:{} {who} [{}]",
|
||||
s.group,
|
||||
s.n,
|
||||
s.inventory_rev,
|
||||
people.join(",")
|
||||
);
|
||||
for r in &s.relations {
|
||||
let to = match &r.to {
|
||||
Party::Named(e) => e.as_str().to_string(),
|
||||
Party::Unnamed => "unnamed".to_string(),
|
||||
};
|
||||
let feats: Vec<String> = r.features.iter().map(&gloss).collect();
|
||||
println!(" {}->{to} {}", r.from, feats.join(" "));
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::Inventory(InventoryCommand::Dao) => {
|
||||
let (rev, entries) = weksa::daodejing::revision();
|
||||
let count = entries.len();
|
||||
|
|
@ -850,6 +1025,7 @@ fn main() -> Result<()> {
|
|||
inventory: &cli.inventory,
|
||||
entities: &cli.entities,
|
||||
ontology: &cli.ontology,
|
||||
speaker_state: &cli.speaker_state,
|
||||
},
|
||||
&answerer,
|
||||
&embed_model,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! The language ontology: per target language, the choices its culture makes
|
||||
//! about word, grammar, discourse, sound and script for a speaker in a stance
|
||||
//! (ruling `persona-drives-ontology`). An entry says when it applies (feature
|
||||
//! conditions over the lowering view's stance and discourse) and what it
|
||||
//! conditions over the lowering view's stance, discourse and scene relation) and what it
|
||||
//! chooses. `select` is the only reader that decides which entries a lowering
|
||||
//! prompt carries, and it runs only inside `Prompt::lower`;
|
||||
//! `OntologyStore::admit` is the only writer. Entries are immutable.
|
||||
|
|
@ -51,8 +51,8 @@ pub enum Test {
|
|||
Is(FeatValue),
|
||||
}
|
||||
|
||||
/// One feature of one slot of the view, tested. Only `Stance` and `Discourse`
|
||||
/// reach a lowering view today.
|
||||
/// One feature of one slot of the view, tested. `Stance`, `Discourse` and
|
||||
/// `Scene` reach a lowering view; `Speaker` does not yet.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct Condition {
|
||||
pub slot: Slot,
|
||||
|
|
@ -149,8 +149,9 @@ impl Ontology {
|
|||
}
|
||||
|
||||
/// The entries of exactly `language` whose every condition holds against the
|
||||
/// view's stance and discourse features, in `n` order. An absent feature meets
|
||||
/// no condition. Nothing else of the view is read.
|
||||
/// view's stance, discourse and scene-relation features (the scene's only when
|
||||
/// `with_scene` selected a relation), in `n` order. An absent feature meets no
|
||||
/// condition. Nothing else of the view is read.
|
||||
pub fn select<'a>(
|
||||
o: &'a Ontology,
|
||||
language: &str,
|
||||
|
|
@ -163,6 +164,7 @@ pub fn select<'a>(
|
|||
e.when.iter().all(|c| match c.slot {
|
||||
Slot::Stance => c.holds(&input.stance().features),
|
||||
Slot::Discourse => c.holds(&input.discourse().features),
|
||||
Slot::Scene => c.holds(input.scene()),
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -48,10 +48,12 @@ use crate::packet::{Packet, Stance};
|
|||
use crate::score::{CHRF_VERSION, chrf, Embedder, ItemScore, ScoreInput, Scorer, SplitMean, aggregate};
|
||||
use crate::split::{Split, SplitScheme, fnv1a64, fnv1a64_of, hex16};
|
||||
use crate::store::{
|
||||
CorpusStore, EntityStore, InventoryStore, OntologyStore, PacketStore, StoreError,
|
||||
CorpusStore, EntityStore, InventoryStore, OntologyStore, PacketStore, SpeakerStateStore,
|
||||
StoreError,
|
||||
check_ontology_entry, open_cache_over,
|
||||
};
|
||||
use crate::validate::LoweringInput;
|
||||
use crate::inventory::Slot;
|
||||
use crate::validate::{LoweringInput, feature_refusals};
|
||||
|
||||
/// The digest of the prompt builder's source: `context.rs`, then `split.rs`,
|
||||
/// whose scheme and digest function decide which lines the prompt holds as
|
||||
|
|
@ -137,6 +139,13 @@ pub struct RunRecord {
|
|||
/// 16 hex of the ontology file's bytes.
|
||||
#[cultcache(key = 16)]
|
||||
pub ontology_digest: String,
|
||||
/// Canonical absolute path of the speaker-state store (scenes): read-only
|
||||
/// for the run, pinned by `speaker_state_digest`.
|
||||
#[cultcache(key = 17)]
|
||||
pub speaker_state: String,
|
||||
/// 16 hex of the speaker-state file's bytes.
|
||||
#[cultcache(key = 18)]
|
||||
pub speaker_state_digest: String,
|
||||
}
|
||||
|
||||
/// How a run's lowering prompts reach the model that answers them (rulings
|
||||
|
|
@ -442,6 +451,7 @@ pub enum DriftOf {
|
|||
Corpus,
|
||||
Entities,
|
||||
Ontology,
|
||||
SpeakerState,
|
||||
Prompts,
|
||||
Model,
|
||||
Embedder,
|
||||
|
|
@ -480,6 +490,8 @@ pub enum RunError {
|
|||
/// An ontology entry of a panel language does not read against the run's
|
||||
/// head inventory at its revision.
|
||||
Ontology,
|
||||
/// A scene does not read against the run's inventory.
|
||||
SpeakerState,
|
||||
}
|
||||
|
||||
impl fmt::Display for RunError {
|
||||
|
|
@ -504,6 +516,9 @@ impl fmt::Display for RunError {
|
|||
RunError::Ontology => f.write_str(
|
||||
"an ontology entry of a panel language does not read against the run's inventory",
|
||||
),
|
||||
RunError::SpeakerState => {
|
||||
f.write_str("a scene does not read against the run's inventory")
|
||||
}
|
||||
RunError::Panel => f.write_str(
|
||||
"the panel is empty, repeats or misspells a language, or names one with no live Train or Validation item",
|
||||
),
|
||||
|
|
@ -519,13 +534,14 @@ impl fmt::Debug for RunError {
|
|||
|
||||
impl std::error::Error for RunError {}
|
||||
|
||||
/// The four stores a run pins.
|
||||
/// The five stores a run pins.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RunPaths<'a> {
|
||||
pub corpus: &'a Path,
|
||||
pub inventory: &'a Path,
|
||||
pub entities: &'a Path,
|
||||
pub ontology: &'a Path,
|
||||
pub speaker_state: &'a Path,
|
||||
}
|
||||
|
||||
/// A run's open stores, reopened only from its record. The one public producer
|
||||
|
|
@ -538,6 +554,7 @@ pub struct RunStores {
|
|||
inventory: InventoryStore,
|
||||
entities: EntityStore,
|
||||
ontology: OntologyStore,
|
||||
speaker_state: SpeakerStateStore,
|
||||
}
|
||||
|
||||
/// The run's own copy of the inventory, made at `create`.
|
||||
|
|
@ -554,6 +571,9 @@ fn check_pins(cache: &CultCache, root: &Path, record: &RunRecord) -> Result<(),
|
|||
if file_digest(&record.ontology).as_ref() != Some(&record.ontology_digest) {
|
||||
return Err(RunError::Drift(DriftOf::Ontology));
|
||||
}
|
||||
if file_digest(&record.speaker_state).as_ref() != Some(&record.speaker_state_digest) {
|
||||
return Err(RunError::Drift(DriftOf::SpeakerState));
|
||||
}
|
||||
let (path, digest, _) = head_of(cache, root, record)?;
|
||||
if file_digest_at(&path).as_ref() != Some(&digest) {
|
||||
return Err(RunError::Drift(DriftOf::Inventory));
|
||||
|
|
@ -692,6 +712,8 @@ impl RunStores {
|
|||
entities: canonical(paths.entities)?,
|
||||
ontology: canonical(paths.ontology)?,
|
||||
ontology_digest: String::new(),
|
||||
speaker_state: canonical(paths.speaker_state)?,
|
||||
speaker_state_digest: String::new(),
|
||||
corpus_digest: String::new(),
|
||||
entity_digest: String::new(),
|
||||
prompt_digest: hex16(PROMPT_SOURCE_DIGEST),
|
||||
|
|
@ -714,12 +736,14 @@ impl RunStores {
|
|||
entity_digest: file_digest(&record.entities).ok_or(RunError::Store)?,
|
||||
inventory_digest: file_digest_at(&head).ok_or(RunError::Store)?,
|
||||
ontology_digest: file_digest(&record.ontology).ok_or(RunError::Store)?,
|
||||
speaker_state_digest: file_digest(&record.speaker_state).ok_or(RunError::Store)?,
|
||||
..record
|
||||
};
|
||||
between();
|
||||
let mut run = Self::with(root, cache, record)?;
|
||||
run.check_panel()?;
|
||||
run.check_ontology()?;
|
||||
run.check_scenes()?;
|
||||
// The stores were read between the digests: unless they still match
|
||||
// the pins, what was opened is not what was pinned. Nothing is
|
||||
// recorded for a run that did not pin.
|
||||
|
|
@ -771,6 +795,8 @@ impl RunStores {
|
|||
inventory: InventoryStore::open(&head).map_err(store)?,
|
||||
entities: EntityStore::open(Path::new(&record.entities)).map_err(store)?,
|
||||
ontology: OntologyStore::open(Path::new(&record.ontology)).map_err(store)?,
|
||||
speaker_state: SpeakerStateStore::open(Path::new(&record.speaker_state))
|
||||
.map_err(store)?,
|
||||
cache,
|
||||
record,
|
||||
})
|
||||
|
|
@ -803,6 +829,40 @@ impl RunStores {
|
|||
})
|
||||
}
|
||||
|
||||
/// Every scene's relation features read against the run's inventory at the
|
||||
/// scene's own revision, and again at the head: the checks `put_scene`
|
||||
/// applied, applied again to this inventory.
|
||||
fn check_scenes(&self) -> Result<(), RunError> {
|
||||
let inventory = self.inventory.inventory().map_err(|_| RunError::Store)?;
|
||||
let scenes = self.speaker_state.all_scenes().map_err(|_| RunError::Store)?;
|
||||
for scene in &scenes {
|
||||
let head = inventory.latest().map(|r| r.n).ok_or(RunError::SpeakerState)?;
|
||||
for r in &scene.relations {
|
||||
for rev in [scene.inventory_rev, head] {
|
||||
if !feature_refusals(&inventory, rev, &r.features, Slot::Scene).is_empty() {
|
||||
return Err(RunError::SpeakerState);
|
||||
}
|
||||
}
|
||||
}
|
||||
if scene.relations.is_empty()
|
||||
&& !(inventory.has_revision(scene.inventory_rev) && inventory.has_revision(head))
|
||||
{
|
||||
return Err(RunError::SpeakerState);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The one door by which a loop view gets its scene: `with_scene` over the
|
||||
/// run's pinned speaker-state and corpus stores.
|
||||
fn assembled(
|
||||
&self,
|
||||
view: LoweringInput,
|
||||
inventory: &InventoryStore,
|
||||
) -> Result<LoweringInput, StoreError> {
|
||||
view.with_scene(&inventory.inventory()?, &self.speaker_state, &self.corpus)
|
||||
}
|
||||
|
||||
/// Every panel language holds a live Train and a live Validation item.
|
||||
fn check_panel(&self) -> Result<(), RunError> {
|
||||
let items = self.corpus.all().map_err(|_| RunError::Store)?;
|
||||
|
|
@ -1300,6 +1360,9 @@ impl RunStores {
|
|||
let view = stores[slot]
|
||||
.admit(packet, &self.inventory, &self.corpus, &self.entities)
|
||||
.map_err(|_| RunError::Store)?;
|
||||
let view = self
|
||||
.assembled(view, &self.inventory)
|
||||
.map_err(|_| RunError::Store)?;
|
||||
Prompt::lower(
|
||||
&view,
|
||||
&packet_item,
|
||||
|
|
@ -1586,19 +1649,23 @@ fn score_round<M: ModelPort, E: Embedder>(
|
|||
// Phase 2: one lowering prompt per admitted view, answered through the
|
||||
// port. Nothing is kept unless every one is answered.
|
||||
let mut unanswered = Vec::new();
|
||||
for (at, id, packet_key, view) in &admitted {
|
||||
for (at, id, packet_key, view) in admitted {
|
||||
// Each item is lowered in its own language: the only reference the
|
||||
// scorer has is the item's own text.
|
||||
let language = &items[*at].language;
|
||||
let Ok(prompt) = Prompt::lower(view, id, language, &inventory, &run.entities, &ontology) else {
|
||||
fates[*at] = Fate::Refused(Stage::Lower, Some(packet_key.clone()));
|
||||
let language = &items[at].language;
|
||||
let Ok(view) = run.assembled(view, inventory_store) else {
|
||||
fates[at] = Fate::Refused(Stage::Lower, Some(packet_key.clone()));
|
||||
continue;
|
||||
};
|
||||
let Ok(prompt) = Prompt::lower(&view, &id, language, &inventory, &run.entities, &ontology) else {
|
||||
fates[at] = Fate::Refused(Stage::Lower, Some(packet_key.clone()));
|
||||
continue;
|
||||
};
|
||||
calls.push(Call {
|
||||
prompt: prompt.digest(),
|
||||
items: prompt.item_keys().to_vec(),
|
||||
});
|
||||
fates[*at] = match model.answer(&prompt) {
|
||||
fates[at] = match model.answer(&prompt) {
|
||||
Ok(answer) => match LoweringDraft::parse(&answer) {
|
||||
Ok(draft) if draft.usable() => {
|
||||
Fate::Lowered(draft.text, packet_key.clone())
|
||||
|
|
@ -2793,6 +2860,8 @@ mod tests {
|
|||
panel: vec!["en-US".into()],
|
||||
ontology: String::new(),
|
||||
ontology_digest: String::new(),
|
||||
speaker_state: String::new(),
|
||||
speaker_state_digest: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2985,6 +3054,8 @@ mod tests {
|
|||
);
|
||||
let ontology = dir.path().join("o.cc");
|
||||
fs::write(&ontology, [0x90]).unwrap();
|
||||
let speaker_state = dir.path().join("s.cc");
|
||||
fs::write(&speaker_state, [0x90]).unwrap();
|
||||
let root = dir.path().join("run");
|
||||
let mut run = RunStores::create(
|
||||
&root,
|
||||
|
|
@ -2994,6 +3065,7 @@ mod tests {
|
|||
inventory: &inventory,
|
||||
entities: &entities,
|
||||
ontology: &ontology,
|
||||
speaker_state: &speaker_state,
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
|
|||
|
|
@ -1320,8 +1320,11 @@ pub(crate) fn check_ontology_entry(
|
|||
}
|
||||
let mut out = Vec::new();
|
||||
for c in &e.when {
|
||||
if !matches!(c.slot, Slot::Stance | Slot::Discourse) {
|
||||
return Err(malformed(&key, "a condition reads slot Stance or Discourse"));
|
||||
if !matches!(c.slot, Slot::Stance | Slot::Discourse | Slot::Scene) {
|
||||
return Err(malformed(
|
||||
&key,
|
||||
"a condition reads slot Stance, Discourse or Scene",
|
||||
));
|
||||
}
|
||||
if matches!(c.test, Test::Is(FeatValue::Grade(_))) {
|
||||
return Err(malformed(&key, "Is names an option or a flag"));
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ fn weksa(dir: &std::path::Path, args: &[&str]) -> Output {
|
|||
"./e.cc",
|
||||
"--ontology",
|
||||
"./o.cc",
|
||||
"--speaker-state",
|
||||
"./s.cc",
|
||||
])
|
||||
.args(args)
|
||||
.output()
|
||||
|
|
@ -1341,6 +1343,7 @@ fn dao_stores(dir: &std::path::Path) {
|
|||
std::fs::copy(src.join(from), dir.join(to)).unwrap();
|
||||
}
|
||||
std::fs::write(dir.join("o.cc"), [0x90]).unwrap();
|
||||
std::fs::write(dir.join("s.cc"), [0x90]).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -123,6 +123,20 @@ pub fn ontology_in(dir: &std::path::Path) -> std::path::PathBuf {
|
|||
path
|
||||
}
|
||||
|
||||
/// An empty speaker-state store beside `ontology` (same directory), made if absent.
|
||||
pub fn speaker_state_beside(ontology: &std::path::Path) -> std::path::PathBuf {
|
||||
let path = ontology.with_file_name("speaker-state.cc");
|
||||
if !path.exists() {
|
||||
std::fs::write(&path, [0x90]).unwrap();
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
/// An empty speaker-state store at `<dir>/speaker-state.cc`, made if absent.
|
||||
pub fn speaker_state_in(dir: &std::path::Path) -> std::path::PathBuf {
|
||||
speaker_state_beside(&dir.join("ontology.cc"))
|
||||
}
|
||||
|
||||
/// A run over the stores `world()` and `corpus()` keep in `dir`: `c.cc`,
|
||||
/// `i.cc` and `entities.cc`. Read at the call, so create it after the items.
|
||||
/// The run's en-US panel needs a live Train and a live Validation item: a
|
||||
|
|
@ -158,6 +172,7 @@ pub fn run_over(dir: &std::path::Path) -> weksa::run::RunStores {
|
|||
inventory: &dir.join("i.cc"),
|
||||
entities: &dir.join("entities.cc"),
|
||||
ontology: &dir.join("ontology.cc"),
|
||||
speaker_state: &speaker_state_beside(&dir.join("ontology.cc")),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
|
|||
|
|
@ -547,7 +547,8 @@ mod lowering {
|
|||
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../data").join(sub)
|
||||
}
|
||||
|
||||
fn admitted(
|
||||
/// The admitted view, its scene never read.
|
||||
fn admitted_unread(
|
||||
packet: Packet,
|
||||
inv: &InventoryStore,
|
||||
corpus: &str,
|
||||
|
|
@ -561,6 +562,37 @@ mod lowering {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
/// The admitted view with its scene read over an empty speaker-state store.
|
||||
fn admitted(
|
||||
packet: Packet,
|
||||
inv: &InventoryStore,
|
||||
corpus: &str,
|
||||
entities: &EntityStore,
|
||||
) -> LoweringInput {
|
||||
let view = admitted_unread(packet, inv, corpus, entities);
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let state = weksa::SpeakerStateStore::open(&dir.path().join("s.cc")).unwrap();
|
||||
let corpus = CorpusStore::open(&data(corpus)).unwrap();
|
||||
view.with_scene(&inv.inventory().unwrap(), &state, &corpus).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lowering_prompt_refuses_an_unread_scene() {
|
||||
let cast = InventoryStore::open(&data("interlingua/inventory.cc")).unwrap();
|
||||
let entities = EntityStore::open(&data("entity/entity.cc")).unwrap();
|
||||
let inv = cast.inventory().unwrap();
|
||||
let item = CorpusItemId::parse("item:nibu:1").unwrap();
|
||||
let held = Held(vec![nibu(&[("en-US", &["Nibu"])])]);
|
||||
let unread = admitted_unread(proof_packet(), &cast, "corpus/corpus.cc", &entities);
|
||||
let empty = weksa::Ontology::empty();
|
||||
assert_eq!(
|
||||
Prompt::lower(&unread, &item, "en-US", &inv, &held, &empty).err(),
|
||||
Some(LowerError::SceneUnread)
|
||||
);
|
||||
let read = admitted(proof_packet(), &cast, "corpus/corpus.cc", &entities);
|
||||
assert!(Prompt::lower(&read, &item, "en-US", &inv, &held, &empty).is_ok());
|
||||
}
|
||||
|
||||
fn dao(index: usize) -> (LoweringInput, Inventory, EntityStore, CorpusItemId) {
|
||||
let inv = InventoryStore::open(&data("daodejing/inventory.cc")).unwrap();
|
||||
let entities = EntityStore::open(&data("daodejing/entity.cc")).unwrap();
|
||||
|
|
|
|||
|
|
@ -24,12 +24,13 @@ fn rewrite_run(root: &Path, change: impl FnOnce(&mut RunRecord)) {
|
|||
cache.put(format!("run:{}", record.label), &record).unwrap();
|
||||
}
|
||||
|
||||
fn paths(dir: &Path) -> (PathBuf, PathBuf, PathBuf, PathBuf) {
|
||||
fn paths(dir: &Path) -> (PathBuf, PathBuf, PathBuf, PathBuf, PathBuf) {
|
||||
(
|
||||
dir.join("c.cc"),
|
||||
dir.join("i.cc"),
|
||||
dir.join("entities.cc"),
|
||||
common::ontology_in(dir),
|
||||
common::speaker_state_in(dir),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ fn a_run_pins_its_stores_and_refuses_drift() {
|
|||
let mut w = world();
|
||||
let dir = w.dir.path().to_path_buf();
|
||||
let root = dir.join("run");
|
||||
let (c, i, e, o) = paths(&dir);
|
||||
let (c, i, e, o, s) = paths(&dir);
|
||||
let at = |label: &str, model: &str| {
|
||||
RunStores::create(
|
||||
&root,
|
||||
|
|
@ -52,6 +53,7 @@ fn a_run_pins_its_stores_and_refuses_drift() {
|
|||
inventory: &i,
|
||||
entities: &e,
|
||||
ontology: &o,
|
||||
speaker_state: &s,
|
||||
},
|
||||
model,
|
||||
"bag",
|
||||
|
|
@ -147,7 +149,7 @@ fn a_run_works_on_its_own_copy_of_the_inventory() {
|
|||
let w = world();
|
||||
let dir = w.dir.path().to_path_buf();
|
||||
let root = dir.join("run");
|
||||
let (c, i, e, o) = paths(&dir);
|
||||
let (c, i, e, o, s) = paths(&dir);
|
||||
let source_bytes = std::fs::read(&i).unwrap();
|
||||
let mut run = RunStores::create(
|
||||
&root,
|
||||
|
|
@ -157,6 +159,7 @@ fn a_run_works_on_its_own_copy_of_the_inventory() {
|
|||
inventory: &i,
|
||||
entities: &e,
|
||||
ontology: &o,
|
||||
speaker_state: &s,
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
@ -236,6 +239,7 @@ fn create_refuses_a_root_that_holds_an_inventory_and_never_truncates_it() {
|
|||
inventory,
|
||||
entities: &dao.entities,
|
||||
ontology: &dao.ontology,
|
||||
speaker_state: &common::speaker_state_beside(&dao.ontology),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
@ -285,6 +289,7 @@ fn a_create_that_fails_leaves_no_inventory_and_the_retry_succeeds() {
|
|||
inventory,
|
||||
entities,
|
||||
ontology: &dao.ontology,
|
||||
speaker_state: &common::speaker_state_beside(&dao.ontology),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
@ -398,11 +403,13 @@ fn a_store_changed_between_its_digest_and_its_open_is_drift() {
|
|||
}
|
||||
let (c, i, e) = (dir.join("c.cc"), dir.join("i.cc"), dir.join("entities.cc"));
|
||||
let o = dir.join("ontology.cc");
|
||||
let s = common::speaker_state_beside(&o);
|
||||
let paths = || weksa::run::RunPaths {
|
||||
corpus: &c,
|
||||
inventory: &i,
|
||||
entities: &e,
|
||||
ontology: &o,
|
||||
speaker_state: &s,
|
||||
};
|
||||
assert_eq!(
|
||||
RunStores::create_between(
|
||||
|
|
@ -457,6 +464,7 @@ fn window(
|
|||
inventory: &i,
|
||||
entities: &e,
|
||||
ontology: &o,
|
||||
speaker_state: &common::speaker_state_beside(&o),
|
||||
};
|
||||
let created = RunStores::create_between(
|
||||
&root,
|
||||
|
|
@ -905,6 +913,7 @@ fn dao_build(
|
|||
inventory: &inventory,
|
||||
entities: &entities,
|
||||
ontology: &ontology,
|
||||
speaker_state: &common::speaker_state_beside(&ontology),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
@ -1087,6 +1096,8 @@ fn lowering_prompts(dao: &Dao, inventory: &InventoryStore) -> Vec<(String, Promp
|
|||
let view = store
|
||||
.admit(packet, inventory, &corpus, dao.run.entities())
|
||||
.unwrap();
|
||||
let state = weksa::SpeakerStateStore::open(&dir.path().join("s.cc")).unwrap();
|
||||
let view = view.with_scene(&inv, &state, &corpus).unwrap();
|
||||
let prompt = Prompt::lower(&view, &key, "en-US", &inv, dao.run.entities(), &weksa::Ontology::empty()).unwrap();
|
||||
(key.to_string(), prompt)
|
||||
})
|
||||
|
|
@ -1560,6 +1571,7 @@ fn errors_echo_no_input() {
|
|||
inventory: &missing,
|
||||
entities: &missing,
|
||||
ontology: &missing,
|
||||
speaker_state: &common::speaker_state_beside(&missing),
|
||||
};
|
||||
let e = RunStores::create(
|
||||
&root,
|
||||
|
|
@ -1629,6 +1641,7 @@ fn errors_echo_no_input() {
|
|||
inventory: &dao.inventory,
|
||||
entities: &dao.entities,
|
||||
ontology: &dao.ontology,
|
||||
speaker_state: &common::speaker_state_beside(&dao.ontology),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
@ -2551,6 +2564,7 @@ fn a_changed_head_is_drift() {
|
|||
inventory: &dao.inventory,
|
||||
entities: &dao.entities,
|
||||
ontology: &dao.ontology,
|
||||
speaker_state: &common::speaker_state_beside(&dao.ontology),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
@ -2891,6 +2905,7 @@ fn a_summary_line_says_how_lowerings_were_answered() {
|
|||
inventory: &dao.inventory,
|
||||
entities: &dao.entities,
|
||||
ontology: &dao.ontology,
|
||||
speaker_state: &common::speaker_state_beside(&dao.ontology),
|
||||
};
|
||||
RunStores::create(
|
||||
&root,
|
||||
|
|
@ -3615,6 +3630,7 @@ fn mixed_family_round(family: ModelFamily) -> u32 {
|
|||
inventory: &dir.join("i.cc"),
|
||||
entities: &dir.join("entities.cc"),
|
||||
ontology: &dir.join("ontology.cc"),
|
||||
speaker_state: &common::speaker_state_beside(&dir.join("ontology.cc")),
|
||||
},
|
||||
"not-a-draft",
|
||||
"bag",
|
||||
|
|
@ -4402,7 +4418,7 @@ fn open_refuses_a_bad_panel() {
|
|||
let created = RunStores::create(
|
||||
&root,
|
||||
"p",
|
||||
RunPaths { corpus, inventory, entities, ontology: &dao.ontology },
|
||||
RunPaths { corpus, inventory, entities, ontology: &dao.ontology, speaker_state: &common::speaker_state_beside(&dao.ontology) },
|
||||
"fake-model",
|
||||
"bag",
|
||||
weksa::run::LowererReach::ToolLessRelay,
|
||||
|
|
@ -4762,6 +4778,7 @@ fn create_refuses_an_entry_its_inventory_cannot_read() {
|
|||
inventory: &dir.join("i.cc"),
|
||||
entities: &dir.join("e.cc"),
|
||||
ontology,
|
||||
speaker_state: &common::speaker_state_beside(&ontology),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
|
|
|
|||
|
|
@ -307,6 +307,7 @@ fn committed_ontology_is_model_drafted_and_reads_every_inventory() {
|
|||
inventory: &i,
|
||||
entities: &e,
|
||||
ontology: &onto,
|
||||
speaker_state: &common::speaker_state_beside(&onto),
|
||||
},
|
||||
"m",
|
||||
"bag",
|
||||
|
|
@ -351,6 +352,7 @@ fn the_recall_probe_covers_every_live_original_item_and_names_no_work() {
|
|||
inventory: &i,
|
||||
entities: &e,
|
||||
ontology: &o,
|
||||
speaker_state: &common::speaker_state_beside(&o),
|
||||
},
|
||||
"m",
|
||||
"bag",
|
||||
|
|
@ -429,6 +431,7 @@ fn original_round(family: ModelFamily) -> (weksa::run::RunStores, weksa::run::Ro
|
|||
inventory: &tmp.join("inventory.cc"),
|
||||
entities: &tmp.join("entity.cc"),
|
||||
ontology: &tmp.join("ontology.cc"),
|
||||
speaker_state: &common::speaker_state_beside(&tmp.join("ontology.cc")),
|
||||
},
|
||||
"nope",
|
||||
"bag",
|
||||
|
|
@ -588,6 +591,7 @@ fn no_test_item_is_offered_to_a_round_or_recall_or_recorded() {
|
|||
inventory: &p("inventory.cc"),
|
||||
entities: &p("entity.cc"),
|
||||
ontology: &p("ontology.cc"),
|
||||
speaker_state: &common::speaker_state_beside(&p("ontology.cc")),
|
||||
},
|
||||
"nope",
|
||||
"bag",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ struct W {
|
|||
fn world() -> W {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut inventory = InventoryStore::open(&dir.path().join("i.cc")).unwrap();
|
||||
for (rev, entries) in [seed(), proof_revision(), speaker_scene_revision()] {
|
||||
for (rev, entries) in [seed(), proof_revision(), speaker_scene_revision(3)] {
|
||||
inventory.admit_revision(rev, entries).unwrap();
|
||||
}
|
||||
let entities = entities_in(dir.path());
|
||||
|
|
@ -60,7 +60,7 @@ fn feat(gloss: &str, value: FeatValue) -> FeatureValue {
|
|||
let feature = seed()
|
||||
.1
|
||||
.into_iter()
|
||||
.chain(speaker_scene_revision().1)
|
||||
.chain(speaker_scene_revision(3).1)
|
||||
.find(|e| e.gloss == gloss)
|
||||
.unwrap_or_else(|| panic!("no feature {gloss}"))
|
||||
.id;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ struct W {
|
|||
fn world() -> W {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut inventory = InventoryStore::open(&dir.path().join("i.cc")).unwrap();
|
||||
for (rev, entries) in [seed(), proof_revision(), speaker_scene_revision()] {
|
||||
for (rev, entries) in [seed(), proof_revision(), speaker_scene_revision(3)] {
|
||||
inventory.admit_revision(rev, entries).unwrap();
|
||||
}
|
||||
let entities = entities_in(dir.path());
|
||||
|
|
@ -60,7 +60,7 @@ fn feat(gloss: &str, value: FeatValue) -> FeatureValue {
|
|||
let feature = seed()
|
||||
.1
|
||||
.into_iter()
|
||||
.chain(speaker_scene_revision().1)
|
||||
.chain(speaker_scene_revision(3).1)
|
||||
.find(|e| e.gloss == gloss)
|
||||
.unwrap_or_else(|| panic!("no feature {gloss}"))
|
||||
.id;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,14 @@
|
|||
},
|
||||
"prompt_digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"speaker_state": {
|
||||
"description": "Canonical absolute path of the speaker-state store (scenes): read-only\nfor the run, pinned by `speaker_state_digest`.",
|
||||
"type": "string"
|
||||
},
|
||||
"speaker_state_digest": {
|
||||
"description": "16 hex of the speaker-state file's bytes.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
@ -83,7 +91,9 @@
|
|||
"lowerer_reach",
|
||||
"panel",
|
||||
"ontology",
|
||||
"ontology_digest"
|
||||
"ontology_digest",
|
||||
"speaker_state",
|
||||
"speaker_state_digest"
|
||||
],
|
||||
"$defs": {
|
||||
"LowererReach": {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@
|
|||
]
|
||||
},
|
||||
"Condition": {
|
||||
"description": "One feature of one slot of the view, tested. Only `Stance` and `Discourse`\nreach a lowering view today.",
|
||||
"description": "One feature of one slot of the view, tested. `Stance`, `Discourse` and\n`Scene` reach a lowering view; `Speaker` does not yet.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"feature": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue