feat: implement provider deactivation and enhance configuration updates

- Added a new function to deactivate the active provider without deleting credentials, facilitating smoother transitions between different provider types.
- Updated the model flow logic to ensure the active provider is correctly set in the configuration, including handling custom endpoints and OAuth providers.
- Improved error handling in the CLI to consistently format authentication error messages.
- Enhanced the model selection process to reflect the effective provider based on configuration and environment variables.
This commit is contained in:
teknium1
2026-02-20 18:17:55 -08:00
parent 77a3dda59d
commit a3d760ff12
3 changed files with 62 additions and 13 deletions

3
cli.py
View File

@@ -782,8 +782,7 @@ class HermesCLI:
timeout_seconds=float(os.getenv("HERMES_NOUS_TIMEOUT_SECONDS", "15")),
)
except Exception as exc:
from hermes_cli.auth import AuthError
message = format_auth_error(exc) if isinstance(exc, AuthError) else str(exc)
message = format_auth_error(exc)
self.console.print(f"[bold red]{message}[/]")
return False

View File

@@ -261,6 +261,18 @@ def clear_provider_auth(provider_id: Optional[str] = None) -> bool:
return True
def deactivate_provider() -> None:
"""
Clear active_provider in auth.json without deleting credentials.
Used when the user switches to a non-OAuth provider (OpenRouter, custom)
so auto-resolution doesn't keep picking the OAuth provider.
"""
with _auth_store_lock():
auth_store = _load_auth_store()
auth_store["active_provider"] = None
_save_auth_store(auth_store)
# =============================================================================
# Provider Resolution — picks which provider to use
# =============================================================================
@@ -799,7 +811,14 @@ def get_auth_status(provider_id: Optional[str] = None) -> Dict[str, Any]:
# =============================================================================
def _update_config_for_provider(provider_id: str, inference_base_url: str) -> Path:
"""Update config.yaml to reflect the active provider after login."""
"""Update config.yaml and auth.json to reflect the active provider."""
# Set active_provider in auth.json so auto-resolution picks this provider
with _auth_store_lock():
auth_store = _load_auth_store()
auth_store["active_provider"] = provider_id
_save_auth_store(auth_store)
# Update config.yaml model section
config_path = get_config_path()
config_path.parent.mkdir(parents=True, exist_ok=True)

View File

@@ -89,14 +89,31 @@ def cmd_model(args):
current_model = current_model.get("default", "")
current_model = current_model or "(not set)"
active = resolve_provider("auto")
# Read effective provider the same way the CLI does at startup:
# config.yaml model.provider > env var > auto-detect
import os
config_provider = None
model_cfg = config.get("model")
if isinstance(model_cfg, dict):
config_provider = model_cfg.get("provider")
effective_provider = (
os.getenv("HERMES_INFERENCE_PROVIDER")
or config_provider
or "auto"
)
active = resolve_provider(effective_provider)
# Detect custom endpoint
if active == "openrouter" and get_env_value("OPENAI_BASE_URL"):
active = "custom"
# Map active provider to a display name
provider_labels = {
"openrouter": "OpenRouter",
"nous": "Nous Portal",
"custom": "Custom endpoint",
}
active_label = provider_labels.get(active, "Custom endpoint")
active_label = provider_labels.get(active, active)
print()
print(f" Current model: {current_model}")
@@ -177,7 +194,7 @@ def _prompt_provider_choice(choices):
def _model_flow_openrouter(config, current_model=""):
"""OpenRouter provider: ensure API key, then pick model."""
from hermes_cli.auth import _prompt_model_selection, _save_model_choice
from hermes_cli.auth import _prompt_model_selection, _save_model_choice, deactivate_provider
from hermes_cli.config import get_env_value, save_env_value
api_key = get_env_value("OPENROUTER_API_KEY")
@@ -217,7 +234,8 @@ def _model_flow_openrouter(config, current_model=""):
save_env_value("OPENAI_BASE_URL", "")
save_env_value("OPENAI_API_KEY", "")
_save_model_choice(selected)
# Update config provider
# Update config provider and deactivate any OAuth provider
from hermes_cli.config import load_config, save_config
cfg = load_config()
model = cfg.get("model")
@@ -225,6 +243,7 @@ def _model_flow_openrouter(config, current_model=""):
model["provider"] = "openrouter"
model["base_url"] = "https://openrouter.ai/api/v1"
save_config(cfg)
deactivate_provider()
print(f"Default model set to: {selected} (via OpenRouter)")
else:
print("No change.")
@@ -234,9 +253,11 @@ def _model_flow_nous(config, current_model=""):
"""Nous Portal provider: ensure logged in, then pick model."""
from hermes_cli.auth import (
get_provider_auth_state, _prompt_model_selection, _save_model_choice,
resolve_nous_runtime_credentials, fetch_nous_models,
AuthError, format_auth_error, _login_nous, PROVIDER_REGISTRY,
_update_config_for_provider, resolve_nous_runtime_credentials,
fetch_nous_models, AuthError, format_auth_error,
_login_nous, PROVIDER_REGISTRY,
)
from hermes_cli.config import get_env_value, save_env_value
import argparse
state = get_provider_auth_state("nous")
@@ -256,7 +277,7 @@ def _model_flow_nous(config, current_model=""):
except Exception as exc:
print(f"Login failed: {exc}")
return
# login_nous already handles model selection, so we're done
# login_nous already handles model selection + config update
return
# Already logged in — fetch models and select
@@ -279,6 +300,13 @@ def _model_flow_nous(config, current_model=""):
selected = _prompt_model_selection(model_ids, current_model=current_model)
if selected:
_save_model_choice(selected)
# Reactivate Nous as the provider and update config
inference_url = creds.get("base_url", "")
_update_config_for_provider("nous", inference_url)
# Clear any custom endpoint that might conflict
if get_env_value("OPENAI_BASE_URL"):
save_env_value("OPENAI_BASE_URL", "")
save_env_value("OPENAI_API_KEY", "")
print(f"Default model set to: {selected} (via Nous Portal)")
else:
print("No change.")
@@ -286,7 +314,7 @@ def _model_flow_nous(config, current_model=""):
def _model_flow_custom(config):
"""Custom endpoint: collect URL, API key, and model name."""
from hermes_cli.auth import _save_model_choice
from hermes_cli.auth import _save_model_choice, deactivate_provider
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
current_url = get_env_value("OPENAI_BASE_URL") or ""
@@ -325,16 +353,19 @@ def _model_flow_custom(config):
if model_name:
_save_model_choice(model_name)
# Update config to reflect custom provider
# Update config and deactivate any OAuth provider
cfg = load_config()
model = cfg.get("model")
if isinstance(model, dict):
model["provider"] = "auto"
model["base_url"] = effective_url
save_config(cfg)
deactivate_provider()
print(f"Default model set to: {model_name} (via {effective_url})")
else:
if base_url or api_key:
deactivate_provider()
print("Endpoint saved. Use `/model` in chat or `hermes model` to set a model.")