mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-13 15:40:00 +00:00
fix(doctor): skip LLM checks when models block is empty (#5296)
`config.example.yaml` ships a `models:` key with every entry commented
out, so it parses as None rather than an empty list and the `[]` default
in `.get("models", [])` never applies. Iterating that None raised
TypeError, which the surrounding broad handler rendered as a check
result:
✗ LLM API key check ('NoneType' object is not iterable)
✗ LLM auth check ('NoneType' object is not iterable)
✗ LLM package check ('NoneType' object is not iterable)
Every fresh install hit this before configuring a model, turning one
actionable error into four and hiding the real "models configured" hint
behind internal exception text.
Fall back on a falsy value at the three iteration sites so the checks
return no results when nothing is configured. `check_models_configured`
gets the same treatment for consistency; it was already correct because
it tests truthiness rather than iterating.
The existing tests missed this because they use `models: []`, an
explicit empty list, which iterates fine. The added regression tests use
the commented-out shape that `make config` actually produces.
`make doctor` now reports 1 error instead of 4 on a fresh clone.
Fixes #5295
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
parent
a2808e8292
commit
611801d5c0
@ -187,6 +187,14 @@ class TestCheckModelsConfigured:
|
|||||||
result = doctor.check_models_configured(tmp_path / "config.yaml")
|
result = doctor.check_models_configured(tmp_path / "config.yaml")
|
||||||
assert result.status == "skip"
|
assert result.status == "skip"
|
||||||
|
|
||||||
|
def test_commented_out_models_block(self, tmp_path):
|
||||||
|
# config.example.yaml ships a `models:` key whose entries are all
|
||||||
|
# commented out, so it parses as None rather than an empty list.
|
||||||
|
cfg = tmp_path / "config.yaml"
|
||||||
|
cfg.write_text("config_version: 5\nmodels:\n # - name: default\n")
|
||||||
|
result = doctor.check_models_configured(cfg)
|
||||||
|
assert result.status == "fail"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# check_llm_api_key
|
# check_llm_api_key
|
||||||
@ -216,6 +224,14 @@ class TestCheckLLMApiKey:
|
|||||||
results = doctor.check_llm_api_key(tmp_path / "config.yaml")
|
results = doctor.check_llm_api_key(tmp_path / "config.yaml")
|
||||||
assert results == []
|
assert results == []
|
||||||
|
|
||||||
|
def test_commented_out_models_block_returns_empty(self, tmp_path):
|
||||||
|
# Regression: iterating a null `models:` raised TypeError, which the
|
||||||
|
# broad handler rendered as "('NoneType' object is not iterable)".
|
||||||
|
cfg = tmp_path / "config.yaml"
|
||||||
|
cfg.write_text("config_version: 5\nmodels:\n # - name: default\n")
|
||||||
|
results = doctor.check_llm_api_key(cfg)
|
||||||
|
assert results == []
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# check_llm_auth
|
# check_llm_auth
|
||||||
@ -237,6 +253,12 @@ class TestCheckLLMAuth:
|
|||||||
results = doctor.check_llm_auth(cfg)
|
results = doctor.check_llm_auth(cfg)
|
||||||
assert any(result.status == "ok" and "Claude auth available" in result.label for result in results)
|
assert any(result.status == "ok" and "Claude auth available" in result.label for result in results)
|
||||||
|
|
||||||
|
def test_commented_out_models_block_returns_empty(self, tmp_path):
|
||||||
|
cfg = tmp_path / "config.yaml"
|
||||||
|
cfg.write_text("config_version: 5\nmodels:\n # - name: default\n")
|
||||||
|
assert doctor.check_llm_auth(cfg) == []
|
||||||
|
assert doctor.check_llm_package(cfg) == []
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# check_web_search
|
# check_web_search
|
||||||
|
|||||||
@ -280,7 +280,7 @@ def check_models_configured(config_path: Path) -> CheckResult:
|
|||||||
return CheckResult("models configured", "skip")
|
return CheckResult("models configured", "skip")
|
||||||
try:
|
try:
|
||||||
data = _load_yaml_file(config_path)
|
data = _load_yaml_file(config_path)
|
||||||
models = data.get("models", [])
|
models = data.get("models") or []
|
||||||
if models:
|
if models:
|
||||||
return CheckResult("models configured", "ok", f"{len(models)} model(s)")
|
return CheckResult("models configured", "ok", f"{len(models)} model(s)")
|
||||||
return CheckResult(
|
return CheckResult(
|
||||||
@ -326,7 +326,7 @@ def check_llm_api_key(config_path: Path) -> list[CheckResult]:
|
|||||||
with open(config_path, encoding="utf-8") as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
data = yaml.safe_load(f) or {}
|
data = yaml.safe_load(f) or {}
|
||||||
|
|
||||||
for model in data.get("models", []):
|
for model in data.get("models") or []:
|
||||||
# Collect all values that look like $ENV_VAR references
|
# Collect all values that look like $ENV_VAR references
|
||||||
def _collect_env_refs(obj: object) -> list[str]:
|
def _collect_env_refs(obj: object) -> list[str]:
|
||||||
refs: list[str] = []
|
refs: list[str] = []
|
||||||
@ -373,7 +373,7 @@ def check_llm_package(config_path: Path) -> list[CheckResult]:
|
|||||||
data = yaml.safe_load(f) or {}
|
data = yaml.safe_load(f) or {}
|
||||||
|
|
||||||
seen_packages: set[str] = set()
|
seen_packages: set[str] = set()
|
||||||
for model in data.get("models", []):
|
for model in data.get("models") or []:
|
||||||
use = model.get("use", "")
|
use = model.get("use", "")
|
||||||
if ":" in use:
|
if ":" in use:
|
||||||
package_path = use.split(":")[0]
|
package_path = use.split(":")[0]
|
||||||
@ -408,7 +408,7 @@ def check_llm_auth(config_path: Path) -> list[CheckResult]:
|
|||||||
results: list[CheckResult] = []
|
results: list[CheckResult] = []
|
||||||
try:
|
try:
|
||||||
data = _load_yaml_file(config_path)
|
data = _load_yaml_file(config_path)
|
||||||
for model in data.get("models", []):
|
for model in data.get("models") or []:
|
||||||
use = model.get("use", "")
|
use = model.get("use", "")
|
||||||
model_name = model.get("name", "default")
|
model_name = model.get("name", "default")
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user