no-mistakes(review): Derive model fields recursively; fix historical latency and key claims
This commit is contained in:
+51
-35
@@ -36,41 +36,57 @@ def warn(rule, message):
|
||||
WARNINGS.append(f"[{rule}] {message}")
|
||||
|
||||
|
||||
def _provider_model_fields(label, value):
|
||||
"""Model-bearing fields from a dict-shaped or list-shaped provider section."""
|
||||
fields = []
|
||||
if isinstance(value, dict):
|
||||
fields.append((f"{label}.model", value.get("model")))
|
||||
elif isinstance(value, list):
|
||||
for i, item in enumerate(value):
|
||||
if isinstance(item, dict):
|
||||
fields.append((f"{label}[{i}].model", item.get("model")))
|
||||
return fields
|
||||
# Derivation rule: a model name is any scalar under a mapping key named `model` or
|
||||
# `model_name`, at any depth. The top-level `model:` SECTION is the exception where the model
|
||||
# name lives under `default`/`model`/`model_name` inside that section, so it is descended
|
||||
# specially. The only other exception is key `models` (litellm key-generation params carry a
|
||||
# list of model names). EXTEND THE ALLOWLIST for a new exception; do NOT add another field by
|
||||
# hand.
|
||||
MODEL_KEYS = ("model", "model_name")
|
||||
MODEL_SECTION_KEYS = ("default", "model", "model_name")
|
||||
MODEL_LIST_KEYS = ("models",)
|
||||
|
||||
|
||||
def _model_name_fields(cfg):
|
||||
"""Every model-name-bearing field in an agent config, as (path, value) pairs."""
|
||||
fields = []
|
||||
model = cfg.get("model") or {}
|
||||
if isinstance(model, dict):
|
||||
for key, value in model.items():
|
||||
if key == "default" or "model" in key:
|
||||
fields.append((f"model.{key}", value))
|
||||
comp = cfg.get("compression") or {}
|
||||
if isinstance(comp, dict):
|
||||
fields.append(("compression.model", comp.get("model")))
|
||||
aux = cfg.get("auxiliary") or {}
|
||||
if isinstance(aux, dict):
|
||||
for name in ("vision", "web_extract", "compression"):
|
||||
section = aux.get(name) or {}
|
||||
if isinstance(section, dict):
|
||||
fields.append((f"auxiliary.{name}.model", section.get("model")))
|
||||
deleg = cfg.get("delegation") or {}
|
||||
if isinstance(deleg, dict):
|
||||
fields.append(("delegation.model", deleg.get("model")))
|
||||
fields.extend(_provider_model_fields("fallback_providers", cfg.get("fallback_providers")))
|
||||
fields.extend(_provider_model_fields("custom_providers", cfg.get("custom_providers")))
|
||||
return fields
|
||||
def _iter_model_values(node, path=""):
|
||||
"""Yield (path, value) for every model-name-bearing scalar in a config."""
|
||||
if isinstance(node, dict):
|
||||
for key, value in node.items():
|
||||
child = f"{path}.{key}" if path else key
|
||||
if key in MODEL_KEYS:
|
||||
if isinstance(value, dict):
|
||||
for subkey in MODEL_SECTION_KEYS:
|
||||
subvalue = value.get(subkey)
|
||||
if isinstance(subvalue, str):
|
||||
yield (f"{child}.{subkey}", subvalue)
|
||||
for subkey, subvalue in value.items():
|
||||
if isinstance(subvalue, (dict, list)):
|
||||
yield from _iter_model_values(subvalue, f"{child}.{subkey}")
|
||||
elif isinstance(value, list):
|
||||
yield from _iter_model_values(value, child)
|
||||
else:
|
||||
yield (child, value)
|
||||
elif key in MODEL_LIST_KEYS:
|
||||
yield from _iter_model_list(value, child)
|
||||
elif isinstance(value, (dict, list)):
|
||||
yield from _iter_model_values(value, child)
|
||||
elif isinstance(node, list):
|
||||
for i, item in enumerate(node):
|
||||
yield from _iter_model_values(item, f"{path}[{i}]")
|
||||
|
||||
|
||||
def _iter_model_list(node, path):
|
||||
"""Yield scalars under an allowlisted `models` key (list of names or list of dicts)."""
|
||||
if isinstance(node, list):
|
||||
for i, item in enumerate(node):
|
||||
yield from _iter_model_list(item, f"{path}[{i}]")
|
||||
elif isinstance(node, dict):
|
||||
for key, value in node.items():
|
||||
if key in MODEL_KEYS and isinstance(value, str):
|
||||
yield (f"{path}.{key}", value)
|
||||
elif isinstance(value, (dict, list)):
|
||||
yield from _iter_model_list(value, f"{path}.{key}")
|
||||
else:
|
||||
yield (path, node)
|
||||
|
||||
|
||||
def audit(path):
|
||||
@@ -217,7 +233,7 @@ def audit(path):
|
||||
)
|
||||
|
||||
# --- No raw or retired model names (Rule 7/8 spirit) ---
|
||||
# Any retired/raw name in ANY model-bearing field is a hard failure: such a config gets
|
||||
# Any retired/raw name found by the derivation above is a hard failure: such a config gets
|
||||
# 400 Invalid model name (or lands in the wrong pool) at runtime. gpu-light/gemma-4-12b/
|
||||
# crew-auto were retired 2026-09-12; raw model names must use stable aliases instead.
|
||||
bad_model_names = {
|
||||
@@ -228,7 +244,7 @@ def audit(path):
|
||||
"qwen3.6-35B-udq4": "strix-moe",
|
||||
"ornith-1.0-35b": "strix-moe",
|
||||
}
|
||||
for field_path, value in _model_name_fields(cfg):
|
||||
for field_path, value in _iter_model_values(cfg):
|
||||
if value in bad_model_names:
|
||||
check(
|
||||
False,
|
||||
|
||||
Reference in New Issue
Block a user