Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions crates/libsy/src/algorithms/util/affinity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

//! Model affinity as a single SDK component.
//!
//! [`AffinityRouter`] retains the first model chosen for a request's stable identity and
//! forces that model on later requests sharing the identity. It is one object that plays
//! [`AffinityRouter`] retains the first model and category chosen for a request's stable identity
//! and forces that decision on later requests sharing the identity. It is one object that plays
//! both SDK roles, so registering it as a processor and a classifier cannot drift apart:
//!
//! - As a [`Processor`] it *writes* the assignment: [`Event::Decision`] carries the request
Expand Down Expand Up @@ -85,7 +85,7 @@ pub struct AffinityRouter {
///
/// Held on the instance so the two roles share one process-local map through a
/// single registered [`Arc`](std::sync::Arc); bounded by [`MAX_ASSIGNMENTS`].
assignments: Mutex<HashMap<RoutingIdentity, ModelId>>,
assignments: Mutex<HashMap<RoutingIdentity, (ModelId, Option<Category>)>>,
/// Whether the "no identity to key on" warning has already been emitted.
unkeyed_warning_emitted: AtomicBool,
}
Expand Down Expand Up @@ -177,6 +177,7 @@ where
if let Event::Decision {
request,
selected_model_id,
category,
..
} = event
&& let Some(key) = self.affinity_key(request)
Expand All @@ -185,7 +186,7 @@ where
let writable = self.release_on_user_turn || !assignments.contains_key(&key);
if self.should_latch(selected_model_id) && writable {
evict_if_full(&mut assignments);
assignments.insert(key, selected_model_id.clone());
assignments.insert(key, (selected_model_id.clone(), category));
}
}
Ok(())
Expand Down Expand Up @@ -249,7 +250,7 @@ where
let mut assignments = self.assignments.lock();
let assigned = assignments.get(&key).cloned();
let assigned = match assigned.as_ref() {
Some(target) if !available.is_empty() && !available.contains(target) => {
Some((target, _)) if !available.is_empty() && !available.contains(target) => {
assignments.remove(&key);
None
}
Expand All @@ -260,10 +261,10 @@ where
}
Ok((
Classification::Scores(match assigned {
Some(target) => vec![Score {
Some((target, category)) => vec![Score {
confidence: 1.0,
target: target.clone(),
category: None,
category: category.clone(),
}],
None => Vec::new(),
}),
Expand Down
4 changes: 2 additions & 2 deletions crates/libsy/src/core/classifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ pub struct Score {
/// The target (model / tier) being recommended.
pub target: ModelId,
/// The category `target` was drawn from, when the classifier picked one. The rest of
/// that category is what the turn falls through on failure, so a decision made without
/// a category — an affinity replay, say — leaves this `None`.
/// that category is what the turn falls through on failure. A decision made without
/// a category leaves this `None`.
pub category: Option<Category>,
}

Expand Down
3 changes: 1 addition & 2 deletions crates/libsy/src/core/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ pub enum Event<'a> {
/// The model selected for `request`.
selected_model_id: &'a ModelId,
/// The category `selected_model_id` was drawn from, when the deciding
/// classifier picked one. `None` for a decision made without a category,
/// such as an affinity replay.
/// classifier picked one. `None` for a decision made without a category.
category: Option<Category>,
/// Offered so a processor can inspect the runtime model categories.
driver: &'a Driver,
Expand Down
Loading