Compare commits

...

1 Commits

Author SHA1 Message Date
Timmy Time
c735936f24 Fix #373: blank fallback_model fields no longer trigger gateway warnings
Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 41s
When users blank fallback_model fields (set provider and model to empty
strings or None), the validation now treats this as intentionally disabling
fallback instead of showing warnings.

Changes:
- Modified validate_config_structure() to check if both provider and model
  are blank before showing warnings
- Added 4 new tests for blank fallback_model fields
- Updated hint messages to mention the disable option

Behavior:
- Both fields blank: no warnings (intentional disable)
- One field blank: warning shown (likely misconfiguration)
- Empty dict {}: no warnings (existing behavior)

Fixes #373
2026-04-13 18:55:43 -04:00
2 changed files with 66 additions and 12 deletions

View File

@@ -1433,18 +1433,27 @@ def validate_config_structure(config: Optional[Dict[str, Any]] = None) -> List["
" model: anthropic/claude-sonnet-4",
))
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)",
))
# If both provider and model are blank/empty, treat as intentionally disabled
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), or set both fields blank to disable",
))
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), or set both fields blank to disable",
))
# ── 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 {}):

View File

@@ -136,6 +136,51 @@ 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_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
class TestMissingModelSection:
"""Warn when custom_providers exists but model section is missing."""