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
8 changes: 7 additions & 1 deletion raven/config/update_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,13 @@ def _apply(_text: str | None) -> tuple[str | None, list[str]]:
# has no parameter to be restated with at all, so a wholesale write
# loses it every time. Re-adding corrects the tags it names and
# leaves the rest of the row alone.
merged = {**(overlays.get(model) or {}), **overlay}
previous = {}
target = merge_key(name, model)
for stored in list(overlays):
if merge_key(name, stored) == target:
# Match the reader's last-row precedence for existing aliases.
previous = overlays.pop(stored) or {}
merged = {**previous, **overlay}
# An empty string is how a caller clears a field it can otherwise
# only restate; None would be "unstated" and leave it alone.
merged = {k: v for k, v in merged.items() if v != ""}
Expand Down
54 changes: 54 additions & 0 deletions tests/test_config_update_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,60 @@ def test_an_overlay_merges_into_the_row_it_already_had(cfg_path: Path) -> None:
assert row["capabilities"] == ["reasoning"]


@pytest.mark.parametrize(
("existing", "incoming"),
[("team-model", "hosted-vllm/team-model"), ("hosted-vllm/team-model", "team-model")],
)
@pytest.mark.parametrize(
"patch",
[{"capabilities": ["reasoning"]}, {"label": ""}, {"capabilities": []}],
)
def test_overlay_updates_match_model_identity(cfg_path: Path, existing: str, incoming: str, patch: dict) -> None:
original = {"label": "Team model", "description": "Keep this description", "capabilities": ["function-call"]}
add_provider_model("hosted_vllm", existing, overlay=original, config_path=cfg_path)
add_provider_model("hosted_vllm", "other-model", overlay={"label": "Other model"}, config_path=cfg_path)

models = add_provider_model("hosted_vllm", incoming, overlay=patch, config_path=cfg_path)

overlays = _read(cfg_path)["providers"]["hosted_vllm"]["modelOverlay"]
assert models == [existing, "other-model"]
assert set(overlays) == {incoming, "other-model"}
for field, value in (original | patch).items():
assert overlays[incoming][field] == value
assert overlays["other-model"]["label"] == "Other model"


@pytest.mark.parametrize("clear", [False, True])
def test_overlay_alias_cleanup_preserves_effective_row(cfg_path: Path, clear: bool) -> None:
cfg_path.write_text(
json.dumps(
{
"providers": {
"hosted_vllm": {
"models": ["team-model"],
"modelOverlay": {
"hosted-vllm/team-model": {"label": "Stale name", "description": "Stale description"},
"team-model": {"label": "Current name", "description": ""},
},
}
}
}
)
)

patch = {"label": ""} if clear else {"capabilities": ["reasoning"]}
add_provider_model("hosted_vllm", "hosted-vllm/team-model", overlay=patch, config_path=cfg_path)

overlays = _read(cfg_path)["providers"]["hosted_vllm"]["modelOverlay"]
if clear:
assert overlays == {}
else:
assert list(overlays) == ["hosted-vllm/team-model"]
assert overlays["hosted-vllm/team-model"]["label"] == "Current name"
assert overlays["hosted-vllm/team-model"]["description"] == ""
assert overlays["hosted-vllm/team-model"]["capabilities"] == ["reasoning"]


def test_re_adding_corrects_the_field_it_names(cfg_path: Path) -> None:
"""Merging must not turn a correction into an append: re-adding is how a
person fixes a tag they got wrong, so a restated field replaces."""
Expand Down
22 changes: 22 additions & 0 deletions tests/test_rpc_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2042,3 +2042,25 @@ def no_everos(name: str, *args, **kwargs):
monkeypatch.setattr(builtins, "__import__", no_everos)
result = await model_save_key({"slug": "deepseek", "api_key": "new-key"})
assert result["provider"]["slug"] == "deepseek"


async def test_add_model_preserves_metadata_under_a_bare_id(fake_home: Path) -> None:
_write_config(
fake_home,
{
"providers": {
"hosted_vllm": {
"apiBase": "http://localhost:9999/v1",
"models": ["team-model"],
"modelOverlay": {"team-model": {"label": "Team model", "description": "Keep this description"}},
}
}
},
)

result = await model_add_model({"slug": "hosted_vllm", "model": "team-model", "capabilities": ["reasoning"]})

row = result["provider"]["model_labels"]["team-model"]
assert row["label"] == "Team model"
assert row["description"] == "Keep this description"
assert row["capabilities"] == ["reasoning"]
Loading