Loop: lower each item in its own language, read and decide Validation per panel language
Prompt::lower takes the language; RunRecord carries the panel; RoundRecord and Decision hold per-language readings (pooled keys retired); decide_panel is Pareto non-regression over decide(); recall splits at UAX #29 word bounds. Integration tests follow in the next commit. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b83929d4d2
commit
167ce83cfc
8 changed files with 634 additions and 309 deletions
|
|
@ -8,7 +8,9 @@
|
|||
//! consts in this module; every prompt also lists the inventory at the run's
|
||||
//! revision. A mint prompt is the editor's: it reads [`TrainItems`], an
|
||||
//! [`Inventory`] and an [`EditFeedback`] and nothing else, so no held-out text,
|
||||
//! key or per-item score can reach it.
|
||||
//! key or per-item score can reach it. A lowering prompt names the language it
|
||||
//! is said in on a language line; every language is named by its tag, none by a
|
||||
//! default.
|
||||
//!
|
||||
//! Item text may hold line breaks; the renderer indents every continuation line
|
||||
//! (`indented`), so item text cannot forge a `[key]` line or a paragraph.
|
||||
|
|
@ -25,7 +27,7 @@ use serde::Serialize;
|
|||
|
||||
use crate::corpus::{CorpusItem, CorpusItemId};
|
||||
use crate::inventory::{Inventory, InventoryEntry, OptionLabel, ValueKind};
|
||||
use crate::lower::{EntityForms, LowerError, TARGET_LANGUAGE, entity};
|
||||
use crate::lower::{EntityForms, LowerError, entity};
|
||||
use crate::packet::{
|
||||
Context, Discourse, EntityId, Extension, Predication, Referent, Relation, Stance,
|
||||
};
|
||||
|
|
@ -37,7 +39,7 @@ use crate::validate::LoweringInput;
|
|||
/// The instructions a mint prompt starts with: the editor's whole brief. Fixed
|
||||
/// per run by `PROMPT_SOURCE_DIGEST`.
|
||||
pub const MINT_INSTRUCTIONS: &str = "\
|
||||
Propose one edit to the inventory below, so that the lines below can be encoded and lowered to English more faithfully.
|
||||
Propose one edit to the inventory below, so that the lines below can be encoded and lowered, each in its own language, more faithfully.
|
||||
Answer with one JSON object and nothing else: no code fence, no commentary.
|
||||
Shape: {\"add\": [{\"family\", \"gloss\", \"value_kind\", \"applies_to\"}], \"retire\": [ids]}; 1 to 8 changes in all, adds and retires together.
|
||||
- family: one of Concept, Frame, Role, Relation, SpeechAct, Feature, ReferentKind, Extension.
|
||||
|
|
@ -65,7 +67,7 @@ A feature is {feature, value}; value is {\"Grade\": 0 to 1000}, {\"Option\": lab
|
|||
/// The instructions a lowering prompt starts with: the lowerer's whole brief.
|
||||
/// Fixed per run by `PROMPT_SOURCE_DIGEST`.
|
||||
pub const LOWER_INSTRUCTIONS: &str = "\
|
||||
Say the packet below in English (en-US), as one utterance of its speaker: all of its meaning and nothing it does not carry.
|
||||
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.
|
||||
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.";
|
||||
|
|
@ -81,14 +83,14 @@ Answer with one JSON object and nothing else: {\"text\": \"...\"}, the continuat
|
|||
/// reads; it cannot be built from a slice, and no accessor returns text or an
|
||||
/// item.
|
||||
///
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.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") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded).unwrap();
|
||||
/// # 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();
|
||||
/// assert!(t.is_empty());
|
||||
/// let _ = t.len();
|
||||
/// ```
|
||||
///
|
||||
/// ```compile_fail
|
||||
|
|
@ -100,7 +102,7 @@ Answer with one JSON object and nothing else: {\"text\": \"...\"}, the continuat
|
|||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.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") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded).unwrap();
|
||||
/// # 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();
|
||||
/// ```
|
||||
|
|
@ -111,7 +113,7 @@ Answer with one JSON object and nothing else: {\"text\": \"...\"}, the continuat
|
|||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.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") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded).unwrap();
|
||||
/// # 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);
|
||||
/// ```
|
||||
|
|
@ -120,11 +122,14 @@ pub struct TrainItems {
|
|||
}
|
||||
|
||||
impl TrainItems {
|
||||
pub(crate) fn from_corpus(corpus: &CorpusStore) -> anyhow::Result<Self> {
|
||||
/// The live Train items whose language is in `panel`.
|
||||
pub(crate) fn from_corpus(corpus: &CorpusStore, panel: &[String]) -> anyhow::Result<Self> {
|
||||
let items = corpus
|
||||
.all()?
|
||||
.into_iter()
|
||||
.filter(|item| SplitScheme::V1.live(item) == Some(Split::Train))
|
||||
.filter(|item| {
|
||||
SplitScheme::V1.live(item) == Some(Split::Train) && panel.contains(&item.language)
|
||||
})
|
||||
.collect();
|
||||
Ok(Self { items })
|
||||
}
|
||||
|
|
@ -252,11 +257,11 @@ impl<'cp> EncoderOffer<'cp> {
|
|||
}
|
||||
|
||||
/// What the editor is told of how its last edit fared: whether it was kept, and
|
||||
/// the held-out composite at the current inventory, rounded to the nearest
|
||||
/// [`LADDER_STEP`]. The head composite changes only when an edit is accepted and
|
||||
/// an accept needs a gain above one step, so a run releases at most
|
||||
/// `1 / LADDER_STEP` distinct values (Blum and Hardt 2015, the Ladder). Nothing
|
||||
/// else about the held-out split is a field.
|
||||
/// the held-out composite at the current inventory of each panel language,
|
||||
/// rounded to the nearest [`LADDER_STEP`]. A head composite changes only when an
|
||||
/// edit is accepted and an accept needs a gain above one step, so a run releases
|
||||
/// at most `1 / LADDER_STEP` distinct values per language (Blum and Hardt 2015,
|
||||
/// the Ladder). Nothing else about the held-out split is a field.
|
||||
///
|
||||
/// ```
|
||||
/// let f = weksa::context::EditFeedback::none();
|
||||
|
|
@ -266,18 +271,18 @@ impl<'cp> EncoderOffer<'cp> {
|
|||
/// Its fields are private:
|
||||
///
|
||||
/// ```compile_fail,E0451
|
||||
/// let _ = weksa::context::EditFeedback { last: None, head: None };
|
||||
/// let _ = weksa::context::EditFeedback { last: None, head: vec![] };
|
||||
/// ```
|
||||
///
|
||||
/// and only the run builds one with a reading:
|
||||
///
|
||||
/// ```compile_fail,E0624
|
||||
/// let _ = weksa::context::EditFeedback::new(Some(true), Some(0.5));
|
||||
/// let _ = weksa::context::EditFeedback::new(Some(true), vec![]);
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct EditFeedback {
|
||||
last: Option<bool>,
|
||||
head: Option<f64>,
|
||||
head: Vec<(String, f64)>,
|
||||
}
|
||||
|
||||
impl EditFeedback {
|
||||
|
|
@ -285,16 +290,20 @@ impl EditFeedback {
|
|||
pub fn none() -> Self {
|
||||
Self {
|
||||
last: None,
|
||||
head: None,
|
||||
head: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// `last`: whether the previous edit was accepted; `head`: the composite at
|
||||
/// the current inventory, rounded here to the nearest [`LADDER_STEP`].
|
||||
pub(crate) fn new(last: Option<bool>, head: Option<f64>) -> Self {
|
||||
/// `last`: whether the previous edit was accepted; `head`: each panel
|
||||
/// language's composite at the current inventory, in panel order, rounded
|
||||
/// here to the nearest [`LADDER_STEP`].
|
||||
pub(crate) fn new(last: Option<bool>, head: Vec<(String, f64)>) -> Self {
|
||||
Self {
|
||||
last,
|
||||
head: head.map(|x| (x / LADDER_STEP).round() * LADDER_STEP),
|
||||
head: head
|
||||
.into_iter()
|
||||
.map(|(tag, x)| (tag, (x / LADDER_STEP).round() * LADDER_STEP))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -304,7 +313,15 @@ impl EditFeedback {
|
|||
Some(false) => "rejected",
|
||||
None => "none",
|
||||
};
|
||||
let head = self.head.map_or("none".to_string(), |x| format!("{x:.3}"));
|
||||
let head = if self.head.is_empty() {
|
||||
"none".to_string()
|
||||
} else {
|
||||
self.head
|
||||
.iter()
|
||||
.map(|(tag, x)| format!("{tag} {x:.3}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
};
|
||||
format!("last edit: {last}; validation composite at head: {head}")
|
||||
}
|
||||
}
|
||||
|
|
@ -312,12 +329,12 @@ impl EditFeedback {
|
|||
/// Text for a model call, and the keys of the items it holds. The only
|
||||
/// constructors are [`Prompt::mint`], [`Prompt::encode`] and [`Prompt::lower`].
|
||||
///
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// # let dir = tempfile::tempdir().unwrap();
|
||||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.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") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded).unwrap();
|
||||
/// # 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();
|
||||
/// let inv = run.inventory().inventory().unwrap();
|
||||
/// let feedback = weksa::context::EditFeedback::none();
|
||||
|
|
@ -335,7 +352,7 @@ impl EditFeedback {
|
|||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.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") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded).unwrap();
|
||||
/// # 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();
|
||||
/// let feedback = weksa::context::EditFeedback::none();
|
||||
|
|
@ -348,7 +365,7 @@ impl EditFeedback {
|
|||
/// # let p = |n: &str| dir.path().join(n);
|
||||
/// # for n in ["c.cc", "i.cc", "e.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") };
|
||||
/// # let run = weksa::run::RunStores::create(&p("run"), "t", paths, "m", "e", weksa::run::LowererReach::ToolLessRelay, weksa::corpus::ModelFamily::Unrecorded).unwrap();
|
||||
/// # 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();
|
||||
/// let feedback = weksa::context::EditFeedback::none();
|
||||
|
|
@ -502,17 +519,19 @@ impl Prompt {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`LOWER_INSTRUCTIONS`], the meaning of `input` (no kind, constraints,
|
||||
/// scene, speaker or group), the en-US forms of every entity its referents
|
||||
/// cite, and the entries of `inventory` live at the view's revision. `item`
|
||||
/// [`LOWER_INSTRUCTIONS`], the `language: <tag>` line, the meaning of
|
||||
/// `input` (no kind, constraints, scene, speaker or group), the forms in
|
||||
/// `language` of every entity its referents cite, and the entries of `inventory` live at the view's revision. `item`
|
||||
/// goes only to [`Prompt::item_keys`], never into the text. The view's
|
||||
/// speaker and group fields are never read; a speaker's entity id appears
|
||||
/// only when the encoder cites that entity on a referent, which is naming a
|
||||
/// cited entity like any other. A cited entity the port lacks, or one with
|
||||
/// no en-US form, is an error and no prompt.
|
||||
/// no form in `language`, is an error and no prompt. The caller chooses the
|
||||
/// language; the view has none.
|
||||
pub fn lower(
|
||||
input: &LoweringInput,
|
||||
item: &CorpusItemId,
|
||||
language: &str,
|
||||
inventory: &Inventory,
|
||||
entities: &dyn EntityForms,
|
||||
) -> Result<Prompt, LowerError> {
|
||||
|
|
@ -520,7 +539,7 @@ impl Prompt {
|
|||
for id in input.referents().iter().filter_map(|r| r.entity.as_ref()) {
|
||||
let found = entity(entities, id)?;
|
||||
let named = found
|
||||
.forms_in(TARGET_LANGUAGE)
|
||||
.forms_in(language)
|
||||
.filter(|f| !f.is_empty())
|
||||
.ok_or_else(|| LowerError::NoForm { entity: id.clone() })?;
|
||||
forms.insert(id.as_str(), named.to_vec());
|
||||
|
|
@ -536,7 +555,7 @@ impl Prompt {
|
|||
};
|
||||
let rev = input.inventory_rev();
|
||||
let text = format!(
|
||||
"{LOWER_INSTRUCTIONS}\n\npacket: {}\n\nentities: {}\n\ninventory rev:{rev}{}",
|
||||
"{LOWER_INSTRUCTIONS}\n\nlanguage: {language}\n\npacket: {}\n\nentities: {}\n\ninventory rev:{rev}{}",
|
||||
serde_json::to_string(&meaning).expect("a meaning is plain data"),
|
||||
serde_json::to_string(&forms).expect("forms are plain data"),
|
||||
inventory_lines(inventory, rev)
|
||||
|
|
@ -583,34 +602,39 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn the_feedback_line_prints_exact_values() {
|
||||
let en = |x: f64| vec![("en-US".to_string(), x)];
|
||||
let line = |last, head| EditFeedback::new(last, head).line();
|
||||
assert_eq!(
|
||||
EditFeedback::none().line(),
|
||||
"last edit: none; validation composite at head: none"
|
||||
);
|
||||
assert_eq!(
|
||||
line(Some(true), Some(0.5)),
|
||||
"last edit: accepted; validation composite at head: 0.500"
|
||||
line(Some(true), en(0.5)),
|
||||
"last edit: accepted; validation composite at head: en-US 0.500"
|
||||
);
|
||||
assert_eq!(
|
||||
line(Some(false), Some(0.0)),
|
||||
"last edit: rejected; validation composite at head: 0.000"
|
||||
line(Some(false), en(0.0)),
|
||||
"last edit: rejected; validation composite at head: en-US 0.000"
|
||||
);
|
||||
assert_eq!(line(None, None), EditFeedback::none().line());
|
||||
assert_eq!(line(None, vec![]), EditFeedback::none().line());
|
||||
// The head is rounded to the nearest step before it is printed.
|
||||
assert_eq!(
|
||||
line(None, Some(0.5012)),
|
||||
"last edit: none; validation composite at head: 0.500"
|
||||
line(None, en(0.5012)),
|
||||
"last edit: none; validation composite at head: en-US 0.500"
|
||||
);
|
||||
assert_eq!(
|
||||
line(None, Some(0.5026)),
|
||||
"last edit: none; validation composite at head: 0.505"
|
||||
line(None, en(0.5026)),
|
||||
"last edit: none; validation composite at head: en-US 0.505"
|
||||
);
|
||||
assert_eq!(
|
||||
line(None, Some(0.9999)),
|
||||
"last edit: none; validation composite at head: 1.000"
|
||||
line(None, en(0.9999)),
|
||||
"last edit: none; validation composite at head: en-US 1.000"
|
||||
);
|
||||
// One value per panel language, in the order given.
|
||||
assert_eq!(
|
||||
line(None, vec![("en-US".into(), 0.5026), ("ja-JP".into(), 0.41)]),
|
||||
"last edit: none; validation composite at head: en-US 0.505, ja-JP 0.410"
|
||||
);
|
||||
assert_eq!(EditFeedback::new(None, Some(0.5026)).head, Some(0.505));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -43,8 +43,10 @@ use crate::phones::{Phone, PhoneInventory, SegmentError};
|
|||
use crate::store::EntityStore;
|
||||
use crate::validate::LoweringInput;
|
||||
|
||||
/// The language tag this lowerer's entity forms are read under, spelled as an
|
||||
/// entity's `LanguageForms::language` and a corpus item's language are.
|
||||
/// The language tag of the template table and the phones: what `weksa lower`
|
||||
/// says in. The golf loop lowers each item in its own language and does not
|
||||
/// read this. Spelled as an entity's `LanguageForms::language` and a corpus
|
||||
/// item's language are.
|
||||
pub const TARGET_LANGUAGE: &str = "en-US";
|
||||
|
||||
/// The lowerer's whole view of the entity store: one entity by id. Narrow so a
|
||||
|
|
|
|||
|
|
@ -99,6 +99,10 @@ enum LoopCommand {
|
|||
/// that family wrote. Defaults to unrecorded.
|
||||
#[arg(long, default_value = "unrecorded")]
|
||||
answerer_family: ModelFamilyArg,
|
||||
/// The languages the run offers, reads and decides on: BCP 47 tags,
|
||||
/// comma-separated. Every one needs a live Train and Validation item.
|
||||
#[arg(long, value_delimiter = ',', required = true)]
|
||||
panel: Vec<String>,
|
||||
},
|
||||
/// Run one round of the run at ROOT. The stores are read only from the
|
||||
/// run's record: the global --corpus, --inventory and --entities are not
|
||||
|
|
@ -329,7 +333,8 @@ struct AddArgs {
|
|||
/// NFC-normalized here; `--name` byte offsets index the normalized text.
|
||||
#[arg(long)]
|
||||
text: String,
|
||||
#[arg(long, default_value = "en-US")]
|
||||
/// The item's language, a BCP 47 tag; no language is the default.
|
||||
#[arg(long)]
|
||||
language: String,
|
||||
/// An entity id.
|
||||
#[arg(long)]
|
||||
|
|
@ -765,6 +770,7 @@ fn main() -> Result<()> {
|
|||
embed_model,
|
||||
lowerer_reach,
|
||||
answerer_family,
|
||||
panel,
|
||||
}) => {
|
||||
let run = RunStores::create(
|
||||
&root,
|
||||
|
|
@ -778,6 +784,7 @@ fn main() -> Result<()> {
|
|||
&embed_model,
|
||||
lowerer_reach,
|
||||
answerer_family.into(),
|
||||
&panel,
|
||||
)?;
|
||||
println!("opened run:{}", run.record().label);
|
||||
}
|
||||
|
|
@ -821,12 +828,12 @@ fn main() -> Result<()> {
|
|||
std::process::exit(3);
|
||||
}
|
||||
AblationOutcome::Read(readings) => {
|
||||
for (split, reading) in readings {
|
||||
for (language, split, reading) in readings {
|
||||
let name = match split {
|
||||
Split::Train => "train",
|
||||
_ => "validation",
|
||||
};
|
||||
println!("{name} {}", reading.line());
|
||||
println!("{language} {name} {}", reading.line());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -13,8 +13,10 @@
|
|||
//! word, never one word per character: per-character words would make every
|
||||
//! honest single-word gloss share bigrams with a held-out sentence, which is
|
||||
//! the leak signature. A change between such a run and any other script also
|
||||
//! ends a word. A leak in these scripts shows at n = 1 and in chrF; a
|
||||
//! dictionary segmenter is the ja-JP panel's to add.
|
||||
//! ends a word. The n >= 2 coverage signature is therefore blind on these
|
||||
//! scripts: a leak there shows at n = 1 and in max pairwise chrF, which is
|
||||
//! character n-grams and script-blind. A dictionary segmenter is follow-up
|
||||
//! `ja-word-segmenter`.
|
||||
|
||||
use caseless::default_case_fold_str;
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Decision",
|
||||
"description": "One closed edit-accept cycle: digests and numbers, no text. Key\n`run:<label>:cycle:<k>`. Only [`cycle`] builds one.\n\n```\nfn f(d: &weksa::run::Decision) -> u32 { d.k() }\n```\n\nEvery field is given, so the literal below fails only on privacy; its twin is\nthe same literal in the unit test `a_decision_literal_compiles_in_the_lib`.\n\n```compile_fail,E0451\nuse weksa::run::{Call, Decision, Reading, Verdict};\nfn f() -> Decision {\n let r = Reading { chrf: 0.0, meaning: 0.0, composite: 0.0 };\n Decision {\n k: 1,\n base_round: 1,\n base_rev: 1,\n base_inventory_digest: String::new(),\n edit: Call { prompt: String::new(), items: vec![] },\n edit_answer_digest: None,\n candidate_rev: None,\n candidate_inventory_digest: None,\n candidate_round: None,\n base: r,\n candidate: None,\n floor: r,\n threshold: 0.0,\n verdict: Verdict::Accepted,\n }\n}\n```",
|
||||
"description": "One closed edit-accept cycle: digests and numbers, no text. Key\n`run:<label>:cycle:<k>`. Only [`cycle`] builds one.\n\n```\nfn f(d: &weksa::run::Decision) -> u32 { d.k() }\n```\n\nEvery field is given, so the literal below fails only on privacy; its twin is\nthe same literal in the unit test `a_decision_literal_compiles_in_the_lib`.\n\n```compile_fail,E0451\nuse weksa::run::{Call, Decision, Verdict};\nfn f() -> Decision {\n Decision {\n k: 1,\n base_round: 1,\n base_rev: 1,\n base_inventory_digest: String::new(),\n edit: Call { prompt: String::new(), items: vec![] },\n edit_answer_digest: None,\n candidate_rev: None,\n candidate_inventory_digest: None,\n candidate_round: None,\n panel: vec![],\n verdict: Verdict::Accepted,\n }\n}\n```",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"base": {
|
||||
"$ref": "#/$defs/Reading"
|
||||
},
|
||||
"base_inventory_digest": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -21,16 +18,6 @@
|
|||
"format": "uint32",
|
||||
"minimum": 0
|
||||
},
|
||||
"candidate": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/Reading"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"candidate_inventory_digest": {
|
||||
"type": [
|
||||
"string",
|
||||
|
|
@ -64,17 +51,17 @@
|
|||
"null"
|
||||
]
|
||||
},
|
||||
"floor": {
|
||||
"$ref": "#/$defs/Reading"
|
||||
},
|
||||
"k": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
"panel": {
|
||||
"description": "One row per panel language, in panel order.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/PanelReading"
|
||||
}
|
||||
},
|
||||
"verdict": {
|
||||
"$ref": "#/$defs/Verdict"
|
||||
|
|
@ -86,10 +73,8 @@
|
|||
"base_rev",
|
||||
"base_inventory_digest",
|
||||
"edit",
|
||||
"base",
|
||||
"floor",
|
||||
"threshold",
|
||||
"verdict"
|
||||
"verdict",
|
||||
"panel"
|
||||
],
|
||||
"$defs": {
|
||||
"Call": {
|
||||
|
|
@ -119,6 +104,41 @@
|
|||
"Meaning"
|
||||
]
|
||||
},
|
||||
"PanelReading": {
|
||||
"description": "One panel language's row of a cycle: the head's Validation reading, the\ncandidate's (none when the edit never reached a round), the language's\nmeasured floor and the threshold derived from it.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"base": {
|
||||
"$ref": "#/$defs/Reading"
|
||||
},
|
||||
"candidate": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/Reading"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"floor": {
|
||||
"$ref": "#/$defs/Reading"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"threshold": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"language",
|
||||
"base",
|
||||
"floor",
|
||||
"threshold"
|
||||
]
|
||||
},
|
||||
"Reading": {
|
||||
"description": "One validation reading: chrF 0 to 100, meaning 0 to 1, and their composite.",
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "RoundRecord",
|
||||
"description": "One scored round. It holds no text and no Test reading: per-item rows for\nTrain only, Validation only as means and refusal counts by stage. Only\n`run::round` builds one.\n\n```\nfn f(r: &weksa::run::RoundRecord) -> u32 { r.n() }\n```\n\nEvery field is given, so the literal below fails only on privacy; its twin\nis the same literal in the unit test `a_round_record_literal_compiles_in_the_lib`.\n\n```compile_fail,E0451\nuse weksa::run::{RefusalCounts, RoundRecord};\nuse weksa::score::SplitMean;\nfn f() -> RoundRecord {\n RoundRecord {\n n: 1,\n inventory_rev: 1,\n prompt_digest: String::new(),\n corpus_digest: String::new(),\n entity_digest: String::new(),\n model: String::new(),\n embedder: String::new(),\n chrf_version: String::new(),\n calls: vec![],\n train: vec![],\n train_mean: SplitMean::default(),\n train_scored: 0,\n validation_mean: SplitMean::default(),\n validation_scored: 0,\n validation_refused: RefusalCounts::default(),\n inventory_digest: String::new(),\n cycle: None,\n admit_rules: vec![],\n parse_reasons: vec![],\n own_family: 0,\n }\n}\n```",
|
||||
"description": "One scored round. It holds no text and no Test reading: per-item rows for\nTrain only, Validation only as means and refusal counts by stage. Only\n`run::round` builds one.\n\n```\nfn f(r: &weksa::run::RoundRecord) -> u32 { r.n() }\n```\n\nEvery field is given, so the literal below fails only on privacy; its twin\nis the same literal in the unit test `a_round_record_literal_compiles_in_the_lib`.\n\n```compile_fail,E0451\nuse weksa::run::RoundRecord;\nfn f() -> RoundRecord {\n RoundRecord {\n n: 1,\n inventory_rev: 1,\n prompt_digest: String::new(),\n corpus_digest: String::new(),\n entity_digest: String::new(),\n model: String::new(),\n embedder: String::new(),\n chrf_version: String::new(),\n calls: vec![],\n train: vec![],\n inventory_digest: String::new(),\n cycle: None,\n admit_rules: vec![],\n parse_reasons: vec![],\n own_family: 0,\n languages: vec![],\n }\n}\n```",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"admit_rules": {
|
||||
|
|
@ -49,6 +49,13 @@
|
|||
"format": "uint32",
|
||||
"minimum": 0
|
||||
},
|
||||
"languages": {
|
||||
"description": "One reading per panel language, in panel order. No pooled mean exists.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/LanguageRound"
|
||||
}
|
||||
},
|
||||
"model": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -80,27 +87,6 @@
|
|||
"items": {
|
||||
"$ref": "#/$defs/TrainRow"
|
||||
}
|
||||
},
|
||||
"train_mean": {
|
||||
"$ref": "#/$defs/SplitMean"
|
||||
},
|
||||
"train_scored": {
|
||||
"description": "Train items that lowered and scored; `train_mean` is over all of them\nand counts a refused item as 0.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0
|
||||
},
|
||||
"validation_mean": {
|
||||
"$ref": "#/$defs/SplitMean"
|
||||
},
|
||||
"validation_refused": {
|
||||
"$ref": "#/$defs/RefusalCounts"
|
||||
},
|
||||
"validation_scored": {
|
||||
"description": "Validation items that lowered and scored; `validation_mean` is over all\nof them and counts a refused item as 0, so refusing a weak item cannot\nraise it.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
@ -114,12 +100,8 @@
|
|||
"chrf_version",
|
||||
"calls",
|
||||
"train",
|
||||
"train_mean",
|
||||
"validation_mean",
|
||||
"validation_refused",
|
||||
"train_scored",
|
||||
"validation_scored",
|
||||
"inventory_digest"
|
||||
"inventory_digest",
|
||||
"languages"
|
||||
],
|
||||
"$defs": {
|
||||
"Call": {
|
||||
|
|
@ -141,6 +123,44 @@
|
|||
"items"
|
||||
]
|
||||
},
|
||||
"LanguageRound": {
|
||||
"description": "One panel language's readings in a round: the means over that language's\nitems only (a refused item counts 0) and how many items lowered and scored.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"train_mean": {
|
||||
"$ref": "#/$defs/SplitMean"
|
||||
},
|
||||
"train_scored": {
|
||||
"description": "Train items that lowered and scored; `train_mean` is over all of them.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0
|
||||
},
|
||||
"validation_mean": {
|
||||
"$ref": "#/$defs/SplitMean"
|
||||
},
|
||||
"validation_refused": {
|
||||
"$ref": "#/$defs/RefusalCounts"
|
||||
},
|
||||
"validation_scored": {
|
||||
"description": "Validation items that lowered and scored; `validation_mean` is over all\nof them and counts a refused item as 0, so refusing a weak item cannot\nraise it.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"language",
|
||||
"train_mean",
|
||||
"train_scored",
|
||||
"validation_mean",
|
||||
"validation_scored",
|
||||
"validation_refused"
|
||||
]
|
||||
},
|
||||
"Outcome": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,6 +49,13 @@
|
|||
"description": "The answering model.",
|
||||
"type": "string"
|
||||
},
|
||||
"panel": {
|
||||
"description": "The languages the run offers, reads and decides on: BCP 47-form tags,\nsorted, unique, at least one, set once at open. An item of any other\nlanguage is never offered, minted from, probed or scored.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"prompt_digest": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
@ -65,7 +72,8 @@
|
|||
"embedder",
|
||||
"chrf_version",
|
||||
"inventory_digest",
|
||||
"lowerer_reach"
|
||||
"lowerer_reach",
|
||||
"panel"
|
||||
],
|
||||
"$defs": {
|
||||
"LowererReach": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue