Reuse an existing category's spelling in the categorizer; document the Interests page (step 9)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9PrjtUS16PAQve8D4bHgc
This commit is contained in:
2026-09-13 05:45:53 +00:00
co-authored by Claude Fable 5.1
parent 055b1df769
commit af70da1493
2 changed files with 17 additions and 7 deletions
+5 -1
View File
@@ -214,7 +214,10 @@ sees complete issues and article chapters and can download available formats
from a single download menu. An from a single download menu. An
`admin` can additionally follow direct dashboard links from issue entries and `admin` can additionally follow direct dashboard links from issue entries and
article chapters, rate articles, and use every `/dashboard/*` page, including article chapters, rate articles, and use every `/dashboard/*` page, including
settings and jobs. Personalization is shared across accounts for now. settings and jobs. `/dashboard/interests` lists the standing interests with
their LLM-assigned category and a rating-derived weight, adds or deletes them,
and links every interest name in the UI to the articles it matched.
Personalization is shared across accounts for now.
Visitors can request an account with their preferred username at Visitors can request an account with their preferred username at
`/request-access`; admins review open requests on `/dashboard/users`. Approving `/request-access`; admins review open requests on `/dashboard/users`. Approving
@@ -250,6 +253,7 @@ Set `server.jobs_enabled = false` to make starts unavailable.
| `backfill-social` | Refresh recent social scores. | | `backfill-social` | Refresh recent social scores. |
| `features-prune` | Remove stale embeddings and curation telemetry. | | `features-prune` | Remove stale embeddings and curation telemetry. |
| `import-ratings` | Fetch, embed, and rate URLs queued from the Ratings page. | | `import-ratings` | Fetch, embed, and rate URLs queued from the Ratings page. |
| `interests-categorize` | File uncategorized interests under categories with the bulk model. |
The Ratings page accepts up to 500 historical article URLs at a time with one The Ratings page accepts up to 500 historical article URLs at a time with one
verdict and optional note. Imports run in the background and show per-URL verdict and optional note. Imports run in the background and show per-URL
+12 -6
View File
@@ -354,8 +354,8 @@ async fn categorize_with_llm(db: &Db, pending: &[Interest], llm: &LlmClient) ->
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
let existing_categories = categories let existing_categories = categories
.iter() .iter()
.map(|category| category.to_lowercase()) .map(|category| (category.to_lowercase(), *category))
.collect::<BTreeSet<_>>(); .collect::<HashMap<_, _>>();
let mut assigned = BTreeSet::new(); let mut assigned = BTreeSet::new();
let mut new_categories = BTreeSet::new(); let mut new_categories = BTreeSet::new();
let now = Timestamp::now(); let now = Timestamp::now();
@@ -367,10 +367,16 @@ async fn categorize_with_llm(db: &Db, pending: &[Interest], llm: &LlmClient) ->
if !(1..=60).contains(&category.chars().count()) || !assigned.insert(interest.id) { if !(1..=60).contains(&category.chars().count()) || !assigned.insert(interest.id) {
continue; continue;
} }
// A model that answers "software" for an existing "Software" must not
// split the category.
let category = match existing_categories.get(&category.to_lowercase()) {
Some(existing) => existing,
None => {
new_categories.insert(category.to_string());
category
}
};
set_category(db, interest.id, Some(category), now).await?; set_category(db, interest.id, Some(category), now).await?;
if !existing_categories.contains(&category.to_lowercase()) {
new_categories.insert(category.to_string());
}
} }
let mut message = format!( let mut message = format!(
@@ -750,7 +756,7 @@ mod tests {
let backend = Arc::new(MockBackend::new()); let backend = Arc::new(MockBackend::new());
backend.push( backend.push(
r#"{"assignments":[ r#"{"assignments":[
{"interest":"RUST MACROS","category":" Software "}, {"interest":"RUST MACROS","category":" software "},
{"interest":"Wheel-thrown pottery","category":"Creative crafts"}, {"interest":"Wheel-thrown pottery","category":"Creative crafts"},
{"interest":"Not in the batch","category":"Made up"} {"interest":"Not in the batch","category":"Made up"}
]}"#, ]}"#,