feat: sort models by release date
This commit is contained in:
2 files changed
+800
-775
No files matched your search
+762
-762
File diff suppressed because it is too large.
Load diff
+38
-13
@@ -316,11 +316,13 @@ pub struct CatalogBuild {
|
|||||||
///
|
///
|
||||||
/// Per provider: API models (models.dev primary, OpenRouter gap-fill) are
|
/// Per provider: API models (models.dev primary, OpenRouter gap-fill) are
|
||||||
/// curated by `exclude` globs and `rules`, hand-owned `models:` entries are
|
/// curated by `exclude` globs and `rules`, hand-owned `models:` entries are
|
||||||
/// merged in field-wise (hand-owned wins) and placed first, API models are
|
/// merged in field-wise (hand-owned wins, inheriting the API model's release
|
||||||
/// sorted newest-first by release date (name-ascending tiebreak, undated
|
/// date), and all models are sorted newest-first by release date
|
||||||
/// last), and `variants` clones are inserted immediately after their base
|
/// (name-ascending tiebreak). Undated models sort last: hand-owned ones in
|
||||||
/// model. Providers without usable data are routed to `degraded` or `failed`
|
/// quirks.yaml order (preserving curation for quirks-owned providers), then
|
||||||
/// instead of erroring; a provider never emits zero models.
|
/// API ones name-ascending. `variants` clones are inserted immediately after
|
||||||
|
/// their base model. Providers without usable data are routed to `degraded`
|
||||||
|
/// or `failed` instead of erroring; a provider never emits zero models.
|
||||||
pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> CatalogBuild {
|
pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> CatalogBuild {
|
||||||
let modelsdev: Option<Value> = sources
|
let modelsdev: Option<Value> = sources
|
||||||
.modelsdev
|
.modelsdev
|
||||||
@@ -398,9 +400,15 @@ pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> Cat
|
|||||||
{
|
{
|
||||||
Some(position) => {
|
Some(position) => {
|
||||||
let api = api_models.remove(position);
|
let api = api_models.remove(position);
|
||||||
merged_hand.push(overlay_hand_owned(hand, api.data));
|
merged_hand.push(SourcedModel {
|
||||||
|
data: overlay_hand_owned(hand, api.data),
|
||||||
|
release_date: api.release_date,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
None => merged_hand.push(hand),
|
None => merged_hand.push(SourcedModel {
|
||||||
|
data: hand,
|
||||||
|
release_date: None,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,9 +421,17 @@ pub fn build_catalog(sources: &FetchedSources, quirks: &[ProviderQuirks]) -> Cat
|
|||||||
(None, None) => a.data.name.cmp(&b.data.name),
|
(None, None) => a.data.name.cmp(&b.data.name),
|
||||||
});
|
});
|
||||||
|
|
||||||
let bases = merged_hand
|
let mut all_models = merged_hand;
|
||||||
.into_iter()
|
all_models.extend(api_models);
|
||||||
.chain(api_models.into_iter().map(|model| model.data));
|
all_models.sort_by(|a, b| match (&a.release_date, &b.release_date) {
|
||||||
|
(Some(a_date), Some(b_date)) => b_date
|
||||||
|
.cmp(a_date)
|
||||||
|
.then_with(|| a.data.name.cmp(&b.data.name)),
|
||||||
|
(Some(_), None) => Ordering::Less,
|
||||||
|
(None, Some(_)) => Ordering::Greater,
|
||||||
|
(None, None) => Ordering::Equal,
|
||||||
|
});
|
||||||
|
let bases = all_models.into_iter().map(|model| model.data);
|
||||||
|
|
||||||
let variants = provider_quirks
|
let variants = provider_quirks
|
||||||
.map(|provider_quirks| provider_quirks.variants.as_slice())
|
.map(|provider_quirks| provider_quirks.variants.as_slice())
|
||||||
@@ -1222,7 +1238,7 @@ rules:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ordering_hand_owned_first_then_release_date_desc_name_asc() {
|
fn ordering_release_date_desc_hand_owned_interleaved_undated_last() {
|
||||||
let shuffled_payloads = [
|
let shuffled_payloads = [
|
||||||
r#"{"openai":{"models":{
|
r#"{"openai":{"models":{
|
||||||
"b":{"release_date":"2026-05-01"},
|
"b":{"release_date":"2026-05-01"},
|
||||||
@@ -1245,16 +1261,25 @@ rules:
|
|||||||
models:
|
models:
|
||||||
- name: n2
|
- name: n2
|
||||||
- name: n1
|
- name: n1
|
||||||
|
- name: b
|
||||||
|
input_price: 9
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
for payload in shuffled_payloads {
|
for payload in shuffled_payloads {
|
||||||
let build = build_catalog(&make_sources(Some(payload), None), &quirks);
|
let build = build_catalog(&make_sources(Some(payload), None), &quirks);
|
||||||
let names: Vec<&str> = find_provider(&build, "openai")
|
let openai = find_provider(&build, "openai");
|
||||||
|
let names: Vec<&str> = openai
|
||||||
.models
|
.models
|
||||||
.iter()
|
.iter()
|
||||||
.map(|model| model.name.as_str())
|
.map(|model| model.name.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(names, ["n2", "n1", "c", "a", "b", "y", "z"]);
|
// Dated models sort newest-first with a name-ascending tiebreak;
|
||||||
|
// hand-owned "b" inherits its API release date and interleaves
|
||||||
|
// instead of pinning to the top. Undated models sort last:
|
||||||
|
// hand-owned in quirks order (n2, n1), then API name-ascending.
|
||||||
|
assert_eq!(names, ["c", "a", "b", "n2", "n1", "y", "z"]);
|
||||||
|
// "b" is still the hand-owned overlay, not the bare API entry.
|
||||||
|
assert_eq!(find_model(openai, "b").input_price, Some(9.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user