fix: ignore placeholder provider keys in provider activation checks
Add has_usable_secret() to reject empty, short (<4 char), and common placeholder API key values (changeme, your_api_key, placeholder, etc.) throughout the auth/runtime resolution chain. Update list_available_providers() to use provider-specific auth status via get_auth_status() instead of resolve_runtime_provider(), preventing cross-provider key fallback from making providers appear available when they aren't actually configured. Preserve keyless custom endpoint support by checking via base URL. Cherry-picked from PR #2121 by aashizpoudel.
This commit is contained in:
parent
b73d221324
commit
f304bc63b8
3 changed files with 60 additions and 26 deletions
|
|
@ -278,6 +278,33 @@ def _try_gh_cli_token() -> Optional[str]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
_PLACEHOLDER_SECRET_VALUES = {
|
||||||
|
"*",
|
||||||
|
"**",
|
||||||
|
"***",
|
||||||
|
"changeme",
|
||||||
|
"your_api_key",
|
||||||
|
"your-api-key",
|
||||||
|
"placeholder",
|
||||||
|
"example",
|
||||||
|
"dummy",
|
||||||
|
"null",
|
||||||
|
"none",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def has_usable_secret(value: Any, *, min_length: int = 4) -> bool:
|
||||||
|
"""Return True when a configured secret looks usable, not empty/placeholder."""
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return False
|
||||||
|
cleaned = value.strip()
|
||||||
|
if len(cleaned) < min_length:
|
||||||
|
return False
|
||||||
|
if cleaned.lower() in _PLACEHOLDER_SECRET_VALUES:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _resolve_api_key_provider_secret(
|
def _resolve_api_key_provider_secret(
|
||||||
provider_id: str, pconfig: ProviderConfig
|
provider_id: str, pconfig: ProviderConfig
|
||||||
) -> tuple[str, str]:
|
) -> tuple[str, str]:
|
||||||
|
|
@ -297,7 +324,7 @@ def _resolve_api_key_provider_secret(
|
||||||
|
|
||||||
for env_var in pconfig.api_key_env_vars:
|
for env_var in pconfig.api_key_env_vars:
|
||||||
val = os.getenv(env_var, "").strip()
|
val = os.getenv(env_var, "").strip()
|
||||||
if val:
|
if has_usable_secret(val):
|
||||||
return val, env_var
|
return val, env_var
|
||||||
|
|
||||||
return "", ""
|
return "", ""
|
||||||
|
|
@ -688,7 +715,7 @@ def resolve_provider(
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug("Could not detect active auth provider: %s", e)
|
logger.debug("Could not detect active auth provider: %s", e)
|
||||||
|
|
||||||
if os.getenv("OPENAI_API_KEY") or os.getenv("OPENROUTER_API_KEY"):
|
if has_usable_secret(os.getenv("OPENAI_API_KEY")) or has_usable_secret(os.getenv("OPENROUTER_API_KEY")):
|
||||||
return "openrouter"
|
return "openrouter"
|
||||||
|
|
||||||
# Auto-detect API-key providers by checking their env vars
|
# Auto-detect API-key providers by checking their env vars
|
||||||
|
|
@ -701,7 +728,7 @@ def resolve_provider(
|
||||||
if pid == "copilot":
|
if pid == "copilot":
|
||||||
continue
|
continue
|
||||||
for env_var in pconfig.api_key_env_vars:
|
for env_var in pconfig.api_key_env_vars:
|
||||||
if os.getenv(env_var, "").strip():
|
if has_usable_secret(os.getenv(env_var, "")):
|
||||||
return pid
|
return pid
|
||||||
|
|
||||||
return "openrouter"
|
return "openrouter"
|
||||||
|
|
|
||||||
|
|
@ -300,12 +300,15 @@ def list_available_providers() -> list[dict[str, str]]:
|
||||||
# Check if this provider has credentials available
|
# Check if this provider has credentials available
|
||||||
has_creds = False
|
has_creds = False
|
||||||
try:
|
try:
|
||||||
|
from hermes_cli.auth import get_auth_status, has_usable_secret
|
||||||
if pid == "custom":
|
if pid == "custom":
|
||||||
has_creds = bool(_get_custom_base_url())
|
custom_base_url = _get_custom_base_url() or os.getenv("OPENAI_BASE_URL", "")
|
||||||
|
has_creds = bool(custom_base_url.strip())
|
||||||
|
elif pid == "openrouter":
|
||||||
|
has_creds = has_usable_secret(os.getenv("OPENROUTER_API_KEY", ""))
|
||||||
else:
|
else:
|
||||||
from hermes_cli.runtime_provider import resolve_runtime_provider
|
status = get_auth_status(pid)
|
||||||
runtime = resolve_runtime_provider(requested=pid)
|
has_creds = bool(status.get("logged_in") or status.get("configured"))
|
||||||
has_creds = bool(runtime.get("api_key"))
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
result.append({
|
result.append({
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from hermes_cli.auth import (
|
||||||
resolve_codex_runtime_credentials,
|
resolve_codex_runtime_credentials,
|
||||||
resolve_api_key_provider_credentials,
|
resolve_api_key_provider_credentials,
|
||||||
resolve_external_process_provider_credentials,
|
resolve_external_process_provider_credentials,
|
||||||
|
has_usable_secret,
|
||||||
)
|
)
|
||||||
from hermes_cli.config import load_config
|
from hermes_cli.config import load_config
|
||||||
from hermes_constants import OPENROUTER_BASE_URL
|
from hermes_constants import OPENROUTER_BASE_URL
|
||||||
|
|
@ -188,12 +189,13 @@ def _resolve_named_custom_runtime(
|
||||||
if not base_url:
|
if not base_url:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
api_key = (
|
api_key_candidates = [
|
||||||
(explicit_api_key or "").strip()
|
(explicit_api_key or "").strip(),
|
||||||
or custom_provider.get("api_key", "")
|
str(custom_provider.get("api_key", "") or "").strip(),
|
||||||
or os.getenv("OPENAI_API_KEY", "").strip()
|
os.getenv("OPENAI_API_KEY", "").strip(),
|
||||||
or os.getenv("OPENROUTER_API_KEY", "").strip()
|
os.getenv("OPENROUTER_API_KEY", "").strip(),
|
||||||
)
|
]
|
||||||
|
api_key = next((candidate for candidate in api_key_candidates if has_usable_secret(candidate)), "")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"provider": "openrouter",
|
"provider": "openrouter",
|
||||||
|
|
@ -257,21 +259,23 @@ def _resolve_openrouter_runtime(
|
||||||
# provider (issues #420, #560).
|
# provider (issues #420, #560).
|
||||||
_is_openrouter_url = "openrouter.ai" in base_url
|
_is_openrouter_url = "openrouter.ai" in base_url
|
||||||
if _is_openrouter_url:
|
if _is_openrouter_url:
|
||||||
api_key = (
|
api_key_candidates = [
|
||||||
explicit_api_key
|
explicit_api_key,
|
||||||
or os.getenv("OPENROUTER_API_KEY")
|
os.getenv("OPENROUTER_API_KEY"),
|
||||||
or os.getenv("OPENAI_API_KEY")
|
os.getenv("OPENAI_API_KEY"),
|
||||||
or ""
|
]
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# Custom endpoint: use api_key from config when using config base_url (#1760).
|
# Custom endpoint: use api_key from config when using config base_url (#1760).
|
||||||
api_key = (
|
api_key_candidates = [
|
||||||
explicit_api_key
|
explicit_api_key,
|
||||||
or (cfg_api_key if use_config_base_url else "")
|
(cfg_api_key if use_config_base_url else ""),
|
||||||
or os.getenv("OPENAI_API_KEY")
|
os.getenv("OPENAI_API_KEY"),
|
||||||
or os.getenv("OPENROUTER_API_KEY")
|
os.getenv("OPENROUTER_API_KEY"),
|
||||||
or ""
|
]
|
||||||
)
|
api_key = next(
|
||||||
|
(str(candidate or "").strip() for candidate in api_key_candidates if has_usable_secret(candidate)),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
source = "explicit" if (explicit_api_key or explicit_base_url) else "env/config"
|
source = "explicit" if (explicit_api_key or explicit_base_url) else "env/config"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue