Compare commits
1 Commits
burn/web-c
...
burn/373-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1899878c27 |
@@ -1026,6 +1026,16 @@ class GatewayRunner:
|
||||
cfg = _y.safe_load(_f) or {}
|
||||
fb = cfg.get("fallback_providers") or cfg.get("fallback_model") or None
|
||||
if fb:
|
||||
# Treat empty dict / disabled fallback as "not configured"
|
||||
if isinstance(fb, dict):
|
||||
_enabled = fb.get("enabled")
|
||||
if _enabled is False or (
|
||||
isinstance(_enabled, str)
|
||||
and _enabled.strip().lower() in ("false", "0", "no", "off")
|
||||
):
|
||||
return None
|
||||
if not fb.get("provider") and not fb.get("model"):
|
||||
return None
|
||||
return fb
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -1421,6 +1421,7 @@ def validate_config_structure(config: Optional[Dict[str, Any]] = None) -> List["
|
||||
))
|
||||
|
||||
# ── fallback_model must be a top-level dict with provider + model ────
|
||||
# Blank or explicitly disabled fallback is intentional — skip validation.
|
||||
fb = config.get("fallback_model")
|
||||
if fb is not None:
|
||||
if not isinstance(fb, dict):
|
||||
@@ -1430,21 +1431,40 @@ def validate_config_structure(config: Optional[Dict[str, Any]] = None) -> List["
|
||||
"Change to:\n"
|
||||
" fallback_model:\n"
|
||||
" provider: openrouter\n"
|
||||
" model: anthropic/claude-sonnet-4",
|
||||
" model: anthropic/claude-sonnet-4\n"
|
||||
"Or disable with:\n"
|
||||
" fallback_model:\n"
|
||||
" enabled: false",
|
||||
))
|
||||
elif fb:
|
||||
if not fb.get("provider"):
|
||||
issues.append(ConfigIssue(
|
||||
"warning",
|
||||
"fallback_model is missing 'provider' field — fallback will be disabled",
|
||||
"Add: provider: openrouter (or another provider)",
|
||||
))
|
||||
if not fb.get("model"):
|
||||
issues.append(ConfigIssue(
|
||||
"warning",
|
||||
"fallback_model is missing 'model' field — fallback will be disabled",
|
||||
"Add: model: anthropic/claude-sonnet-4 (or another model)",
|
||||
))
|
||||
# Skip warnings when fallback is explicitly disabled (enabled: false)
|
||||
_enabled = fb.get("enabled")
|
||||
if _enabled is False or (isinstance(_enabled, str) and _enabled.strip().lower() in ("false", "0", "no", "off")):
|
||||
pass # intentionally disabled — no warnings
|
||||
else:
|
||||
# Check if both fields are blank (intentional disable)
|
||||
provider = fb.get("provider")
|
||||
model = fb.get("model")
|
||||
provider_blank = not provider or (isinstance(provider, str) and not provider.strip())
|
||||
model_blank = not model or (isinstance(model, str) and not model.strip())
|
||||
|
||||
# Only warn if at least one field is set (user might be trying to configure)
|
||||
# If both are blank, treat as intentionally disabled
|
||||
if not provider_blank or not model_blank:
|
||||
if provider_blank:
|
||||
issues.append(ConfigIssue(
|
||||
"warning",
|
||||
"fallback_model is missing 'provider' field — fallback will be disabled",
|
||||
"Add: provider: openrouter (or another provider)\n"
|
||||
"Or disable with: enabled: false",
|
||||
))
|
||||
if model_blank:
|
||||
issues.append(ConfigIssue(
|
||||
"warning",
|
||||
"fallback_model is missing 'model' field — fallback will be disabled",
|
||||
"Add: model: anthropic/claude-sonnet-4 (or another model)\n"
|
||||
"Or disable with: enabled: false",
|
||||
))
|
||||
|
||||
# ── Check for fallback_model accidentally nested inside custom_providers ──
|
||||
if isinstance(cp, dict) and "fallback_model" not in config and "fallback_model" in (cp or {}):
|
||||
|
||||
@@ -136,6 +136,83 @@ class TestFallbackModelValidation:
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
def test_blank_fallback_fields_no_issues(self):
|
||||
"""Blank fallback_model fields (both empty) should not trigger warnings."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_model": {
|
||||
"provider": "",
|
||||
"model": "",
|
||||
},
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
def test_blank_fallback_fields_with_whitespace_no_issues(self):
|
||||
"""Blank fallback_model fields with whitespace should not trigger warnings."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_model": {
|
||||
"provider": " ",
|
||||
"model": " ",
|
||||
},
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
def test_none_fallback_fields_no_issues(self):
|
||||
"""None fallback_model fields should not trigger warnings."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_model": {
|
||||
"provider": None,
|
||||
"model": None,
|
||||
},
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
def test_enabled_false_no_issues(self):
|
||||
"""enabled: false should suppress warnings."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_model": {
|
||||
"enabled": False,
|
||||
},
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
def test_enabled_false_string_no_issues(self):
|
||||
"""enabled: 'false' (string) should suppress warnings."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_model": {
|
||||
"enabled": "false",
|
||||
},
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
def test_partial_blank_fallback_warns(self):
|
||||
"""Partial blank fallback (only one field blank) should warn."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_model": {
|
||||
"provider": "",
|
||||
"model": "anthropic/claude-sonnet-4",
|
||||
},
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 1
|
||||
assert "provider" in fb_issues[0].message
|
||||
|
||||
def test_valid_fallback_with_enabled_true(self):
|
||||
"""Valid fallback with enabled: true should not warn."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_model": {
|
||||
"enabled": True,
|
||||
"provider": "openrouter",
|
||||
"model": "anthropic/claude-sonnet-4",
|
||||
},
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback" in i.message.lower()]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
|
||||
class TestMissingModelSection:
|
||||
"""Warn when custom_providers exists but model section is missing."""
|
||||
|
||||
Reference in New Issue
Block a user