build --llm-for <prefix>, and rate reads the mails
The words model can be pointed at a subset of pages (`aggregates.yaml` for the mails alone) while everything else keeps the words it has. `rate --repo` rates each mail's prose on its own, LIX and length, since a person reads a mail with no card around it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
1191551f28
commit
b5f5c46ff2
+50
@@ -158,6 +158,41 @@ fn followup_page(repo: Option<&Path>, step: &Value) -> Option<Value> {
|
||||
|
||||
/// `/` is `questions/index.yaml`; `/a/b` is `questions/a/b.yaml` or
|
||||
/// `questions/a/b/index.yaml`.
|
||||
/// Every `mail:` on a state in the repo's aggregates.yaml, as
|
||||
/// (`bucket:state`, subject, body) with the `{placeholders}` taken
|
||||
/// out, so they read as the person will read them.
|
||||
pub(crate) fn mail_texts(repo: &Path) -> Option<Vec<(String, String, String)>> {
|
||||
let raw = std::fs::read_to_string(repo.join("aggregates.yaml")).ok()?;
|
||||
let doc: Value = serde_yaml::from_str(&raw).ok()?;
|
||||
let strip = |s: &str| {
|
||||
let mut out = String::new();
|
||||
let mut skip = false;
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'{' => skip = true,
|
||||
'}' => skip = false,
|
||||
_ if !skip => out.push(c),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
out.split_whitespace().collect::<Vec<_>>().join(" ")
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
for aggregate in doc["aggregates"].as_array().into_iter().flatten() {
|
||||
let bucket = aggregate["bucket"].as_str().unwrap_or_default();
|
||||
for (state, spec) in aggregate["states"].as_object().into_iter().flatten() {
|
||||
if let Some(mail) = spec.get("mail") {
|
||||
out.push((
|
||||
format!("{bucket}:{state}"),
|
||||
strip(mail["subject"].as_str().unwrap_or_default()),
|
||||
strip(mail["body"].as_str().unwrap_or_default()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
|
||||
pub(crate) fn load_page(repo: &Path, route: &str) -> Option<Value> {
|
||||
let rel = route.trim_matches('/');
|
||||
let candidates = if rel.is_empty() {
|
||||
@@ -213,6 +248,21 @@ pub fn rate(argv: &[String]) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let mut report = BTreeMap::new();
|
||||
// The mails first, when the repo carries any: a person reads a
|
||||
// mail alone, with no card around it, so its prose is rated on
|
||||
// its own - LIX and length.
|
||||
if let Some(mails) = repo.as_deref().and_then(mail_texts) {
|
||||
if !mails.is_empty() {
|
||||
println!("{:<40} {:>6} {:<14} {:>6}", "mail", "LIX", "", "words");
|
||||
for (name, subject, body) in &mails {
|
||||
let text = format!("{subject}. {body}");
|
||||
let score = lix(&text);
|
||||
println!("{:<40} {:>6.0} {:<14} {:>6}", name, score, lix_band(score), text.split_whitespace().count());
|
||||
report.insert(format!("mail {name}"), json!({ "lix": score, "words": text.split_whitespace().count() }));
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
println!("{:<28} {:>6} {:<14} {:>8} {:>8} {:>8} {:>8} {:<22} {:<22}", "need", "LIX", "", "which", "where", "asked", "next", "gutted twin says", "page load");
|
||||
for need in model["needs"].as_array().into_iter().flatten() {
|
||||
let id = need["id"].as_str().unwrap_or_default();
|
||||
|
||||
Reference in New Issue
Block a user