fix(cost-map): keep alias_of rows out of alias lists and add them to the registry schema

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
kerry 2026-11-07 07:01:08 +00:00
parent 42e31fcc08
commit 91e6cf2c27
6 changed files with 55 additions and 15 deletions

View file

@ -189,13 +189,6 @@ impl Catalog {
}
let mut aliases: IndexMap<String, String> = IndexMap::new();
for (alias, target) in pointer_rows {
if entries.contains_key(&target) {
aliases.insert(alias, target);
} else {
problems.push(Error::from_key(&alias, Repr::AliasTargetMissing { target }));
}
}
for (model, names) in alias_lists {
for alias in names {
if entries.contains_key(&alias) {
@ -213,6 +206,21 @@ impl Catalog {
}
}
}
for (alias, target) in pointer_rows {
if !entries.contains_key(&target) {
problems.push(Error::from_key(&alias, Repr::AliasTargetMissing { target }));
} else if let Some(taken_by) = aliases.get(&alias) {
problems.push(Error::from_key(
&alias,
Repr::AliasTaken {
alias: alias.clone(),
taken_by: taken_by.clone(),
},
));
} else {
aliases.insert(alias, target);
}
}
let fallback_generalizations = sections
.iter()
@ -362,8 +370,7 @@ impl Catalog {
let mut catalog = self;
catalog.store.edit(|rows| {
rows.entries.retain(|name, _| !rejected.contains(name));
rows.aliases
.retain(|alias, owner| !rejected.contains(owner) && !rejected.contains(alias));
rows.aliases.retain(|_, owner| !rejected.contains(owner));
});
catalog
.sections
@ -436,7 +443,6 @@ impl Catalog {
}
}
/// One row as read: a model, or an `alias_of` pointer at one
struct RowRead {
model: Option<Model>,
aliases: Option<Vec<String>>,

View file

@ -104,9 +104,18 @@ pub fn registry_json_schema() -> Schema {
"title".into(),
"LiteLLM model prices and context window registry".into(),
);
definitions.insert(
"aliasRow".into(),
json!({
"type": "object",
"required": ["alias_of"],
"properties": {"alias_of": {"type": "string"}},
"additionalProperties": false,
}),
);
root.insert(
"additionalProperties".into(),
json!({"$ref": "#/$defs/modelEntry"}),
json!({"anyOf": [{"$ref": "#/$defs/modelEntry"}, {"$ref": "#/$defs/aliasRow"}]}),
);
root.insert("$defs".into(), Value::Object(definitions));
Schema::from(root.clone())
@ -153,6 +162,7 @@ fn decorate_model_entry(schema: &mut Value) {
let rates = json!({FLAT_KEY_PATTERN: {"type": "number", "minimum": 0}});
let object = schema.as_object_mut().unwrap();
object.insert("required".into(), json!(["litellm_provider"]));
object.insert("not".into(), json!({"required": ["alias_of"]}));
object.insert("additionalProperties".into(), Value::Bool(true));
object.insert(
"patternProperties".into(),

View file

@ -149,7 +149,7 @@ fn a_bad_entry_is_reported_by_model_and_key_and_kept_raw() {
)]
#[case::alias_of_claimed_by_a_list(
json!({"Real": {"litellm_provider": "t"}, "real-latest": {"alias_of": "Real"}, "Other": {"litellm_provider": "t", "aliases": ["real-latest"]}}),
r#"Other: alias "real-latest" already belongs to Real"#
r#"real-latest: alias "real-latest" already belongs to Other"#
)]
#[case::aliases_not_a_list(
json!({"a": {"litellm_provider": "t", "aliases": "bad"}}),
@ -450,3 +450,18 @@ fn a_dangling_alias_of_row_is_rejected_and_dropped_from_the_written_document() {
assert!(catalog.lookup("ghost-latest").is_none());
assert_eq!(written(&catalog), r#"{"Real":{"litellm_provider":"t"}}"#);
}
#[rstest]
fn a_pointer_row_that_repeats_a_listed_alias_is_dropped_and_the_model_is_kept() {
let parsed = loaded(json!({
"Real": {"litellm_provider": "t", "aliases": ["real-latest"]},
"real-latest": {"alias_of": "Real"}
}));
let (catalog, removed) = parsed.value.without_rejected(&parsed.errors);
assert_eq!(removed.len(), 1);
assert_eq!(catalog.lookup("real-latest").unwrap().canonical_key, "Real");
assert_eq!(
written(&catalog),
r#"{"Real":{"litellm_provider":"t","aliases":["real-latest"]}}"#
);
}

View file

@ -35,6 +35,7 @@ fn generated_registry_schema_validates_checked_in_catalog() {
#[case(json!({"example": {"litellm_provider": "test"}}))]
#[case(json!({"example": {"litellm_provider": "test", "future_field": true}}))]
#[case(json!({"sample_spec": {"litellm_provider": "placeholder"}}))]
#[case(json!({"example": {"litellm_provider": "test"}, "example-latest": {"alias_of": "example"}}))]
fn generated_registry_schema_keeps_reader_compatibility(#[case] document: Value) {
assert!(registry_validator().is_valid(&document));
}
@ -54,6 +55,9 @@ fn generated_registry_schema_keeps_reader_compatibility(#[case] document: Value)
#[case::empty_windows(json!({"litellm_provider": "test", "off_peak_pricing": {"windows": []}}))]
#[case::invalid_weekday(json!({"litellm_provider": "test", "off_peak_pricing": {"windows": [{"hours_utc": "00:00-01:00", "weekdays": [0]}]}}))]
#[case::invalid_aliases(json!({"litellm_provider": "test", "aliases": "wrong"}))]
#[case::alias_of_on_a_model(json!({"litellm_provider": "test", "alias_of": "other"}))]
#[case::alias_row_with_other_fields(json!({"alias_of": "other", "mode": "chat"}))]
#[case::alias_of_not_a_string(json!({"alias_of": 4}))]
#[case::non_object_model(json!(4))]
fn generated_registry_schema_rejects_invalid_entries(#[case] entry: Value) {
assert!(!registry_validator().is_valid(&json!({"example": entry})));

View file

@ -606,7 +606,7 @@ def _expand_model_aliases(model_cost: dict[str, dict[str, object]]) -> dict[str,
for model_name, model_info in model_cost.items():
aliases: object = model_info.get("aliases")
if aliases is None:
if aliases is None or model_name in pointer_rows:
continue
keys_with_aliases.append(model_name)
if not isinstance(aliases, list):

View file

@ -130,12 +130,15 @@ def test_finalize_pops_key_and_installs_rules():
set_fallback_generalizations(previous)
def test_alias_of_rows_are_served_as_their_target_and_dangling_ones_are_dropped(caplog) -> None:
def test_alias_of_rows_are_served_as_their_target_and_dangling_ones_are_dropped(
caplog: pytest.LogCaptureFixture,
) -> None:
raw: Final = {
"real": {"litellm_provider": "openai", "mode": "chat", "input_cost_per_token": 1e-6},
"real-latest": {"alias_of": "real"},
"ghost-latest": {"alias_of": "ghost"},
"chain-latest": {"alias_of": "real-latest"},
"listed-latest": {"alias_of": "missing", "aliases": ["extra"]},
}
with caplog.at_level("WARNING"):
finalized: Final = _finalize_model_cost_map(raw)
@ -143,7 +146,9 @@ def test_alias_of_rows_are_served_as_their_target_and_dangling_ones_are_dropped(
assert "alias_of" not in finalized["real"]
assert set(finalized) == {"real", "real-latest"}
assert finalized["real-latest"]["litellm_provider"] == "openai"
assert {"ghost-latest", "chain-latest"} <= {record.getMessage().split("'")[1] for record in caplog.records}
assert {"ghost-latest", "chain-latest", "listed-latest"} == {
record.getMessage().split("'")[1] for record in caplog.records
}
def test_finalize_with_no_block_clears_rules():