fix(doctor): reject placeholder model credentials (#8184)

* fix(doctor): reject placeholder model credentials

* fix(doctor): reject whitespace-only model keys

* docs: drop changelog edit from doctor PR
This commit is contained in:
杰尼龟 2026-11-13 16:32:26 +08:00 committed by GitHub
parent ba4e60dec4
commit 5f6d7c06e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 33 additions and 2 deletions

View File

@ -76,6 +76,10 @@ make install
make dev
```
Run `make doctor` after setting model credentials. It reports obvious placeholder
API keys as incomplete configuration; replace them with real keys before testing
model calls.
Open http://localhost:2026.
- All configuration options, providers and manual setup: [Configuration](docs/guide/configuration.md)

View File

@ -357,6 +357,24 @@ class TestCheckLLMApiKey:
assert all(r.fix is not None for r in failed)
assert any("OPENAI_API_KEY" in (r.fix or "") for r in failed)
@pytest.mark.parametrize(
"placeholder",
["your-openai-api-key", "replace-with-your-totokens-api-key", " "],
)
def test_placeholder_key_is_not_reported_as_ready(self, tmp_path, monkeypatch, placeholder):
cfg = tmp_path / "config.yaml"
cfg.write_text(
"models:\n - name: relay\n use: langchain_openai:ChatOpenAI\n model: gpt-6-luna\n api_key: $RELAY_API_KEY\n",
encoding="utf-8",
)
monkeypatch.setenv("RELAY_API_KEY", placeholder)
results = doctor.check_llm_api_key(cfg)
assert len(results) == 1
assert results[0].status == "fail"
assert "RELAY_API_KEY" in (results[0].fix or "")
def test_missing_config_returns_empty(self, tmp_path):
results = doctor.check_llm_api_key(tmp_path / "config.yaml")
assert results == []

View File

@ -57,6 +57,8 @@ Cases marked for real Compose run only when a `docker` CLI is installed.
files mark the env files optional so a fresh checkout still starts and stops.
- **Support bundles export no secrets** and show an external runtime home as
`{DEER_FLOW_HOME}`.
- **Doctor does not echo model credentials.** Obvious sample key values fail the
model readiness check even when the environment variable is nonempty.
## Start, stop, clean

View File

@ -497,6 +497,12 @@ def _memory_backend_config_fix(detail: str) -> str | None:
return None
def _looks_like_key_placeholder(value: str) -> bool:
"""Recognize sample key values without logging or transmitting them."""
normalized = value.strip().lower()
return normalized.startswith(("your-", "replace-with-", "sk-your-", "sk-replace-with-"))
def check_llm_api_key(config_path: Path) -> list[CheckResult]:
"""Check that each model's env var is set in the environment."""
if not config_path.exists():
@ -532,14 +538,15 @@ def check_llm_api_key(config_path: Path) -> list[CheckResult]:
model_name = model.get("name", "default")
for var in env_refs:
label = f"{var} set (model: {model_name})"
if os.environ.get(var):
value = os.environ.get(var)
if value and value.strip() and not _looks_like_key_placeholder(value):
results.append(CheckResult(label, "ok"))
else:
results.append(
CheckResult(
label,
"fail",
fix=f"Add {var}=<your-key> to your .env file",
fix=f"Set {var} to a real key in your .env file",
)
)
except Exception as exc: