fix(delegation): route saves tell a null extra key from an omitted one

_clean_model_option read extra route keys with entry.get(), so an
explicit null and an omitted key looked the same: a declared key sent
as null was dropped, and a stored extra key holding null survived a
save that left it out (the editor's way of turning a setting off).
Both loops now decide on whether the key is present: null is kept as a
value, and an omitted key is removed.
This commit is contained in:
unsupportedpastels authored and Mark S. committed 2026-11-05 15:44:58 -05:00
1 parent fcb3085b9b
commit 27f4a045da
2 files changed
+26 -3

No files matched your search

+4 -3
View File
@@ -190,9 +190,9 @@ def _clean_model_option(index: int, entry: dict, stored: dict[str, dict],
if entry.get(field) not in (None, "", [], {}):
row[field] = entry[field]
# The backend's own keys (DelegationProvider.route_keys, e.g. a ``daybreak`` opt-in) are kept as sent; the
# backend validates them. Absent = the editor turned it off.
# backend validates them. Presence decides, not the value: null is a value, absent = the editor turned it off.
for field in declared:
if field not in row and field not in ROUTE_KEYS and entry.get(field) is not None:
if field not in row and field not in ROUTE_KEYS and field in entry:
row[field] = entry[field]
# A route the renderer came from (a rename names it in original_name; once that save landed, it is stored
# under its new name).
@@ -200,7 +200,8 @@ def _clean_model_option(index: int, entry: dict, stored: dict[str, dict],
# Any other key the stored route carries (hand-written, or declared by a backend that is not loaded now)
# survives an untouched round-trip, so a save never silently drops a setting; a key nobody stored is dropped.
for field, value in source.items():
if field not in row and field not in ROUTE_KEYS and field != "api_key" and entry.get(field) == value:
if (field not in row and field not in ROUTE_KEYS and field != "api_key"
and field in entry and entry[field] == value):
row[field] = value
key = entry.get("api_key")
if is_redacted_credential_preview(key):
@@ -215,6 +215,28 @@ def test_an_undeclared_key_survives_only_an_untouched_round_trip(client, homes):
assert "legacy" not in _delegation(work)["backends"]["model_map"]["options"][0]
def test_a_null_extra_key_is_a_value_and_leaving_it_out_removes_it(client, homes):
_root, work = homes
route = {"name": "deep", "description": "Careful", "provider": "anthropic", "model": "m-deep"}
def save(**extra):
resp = client.put("/api/delegation/backends/model_map?profile=work", json={"options": [{**route, **extra}]})
assert resp.status_code == 200, resp.text
return _delegation(work)["backends"]["model_map"]["options"][0]
# A declared key sent as null is kept as sent; leaving it out removes it.
assert save(daybreak=None) == {**route, "daybreak": None}
assert "daybreak" not in save()
# A stored null in an undeclared key survives a save that sends it back, and goes when the save leaves it out.
block = _delegation(work)
block["backends"]["model_map"]["options"][0]["legacy"] = None # hand-written in config.yaml
(work / "config.yaml").write_text(yaml.safe_dump({"plugins": {"enabled": ["routes"]}, "delegation": block}),
encoding="utf-8")
assert save(legacy=None) == {**route, "legacy": None}
assert "legacy" not in save()
def test_profile_routes_only_name_existing_profiles(client, homes):
_root, work = homes
before = (work / "config.yaml").read_text(encoding="utf-8")