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:
parent
77a3dda59d
commit
a3d760ff12
3 changed files with 62 additions and 13 deletions
3
cli.py
3
cli.py
|
|
@ -782,8 +782,7 @@ class HermesCLI:
|
||||||
timeout_seconds=float(os.getenv("HERMES_NOUS_TIMEOUT_SECONDS", "15")),
|
timeout_seconds=float(os.getenv("HERMES_NOUS_TIMEOUT_SECONDS", "15")),
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
from hermes_cli.auth import AuthError
|
message = format_auth_error(exc)
|
||||||
message = format_auth_error(exc) if isinstance(exc, AuthError) else str(exc)
|
|
||||||
self.console.print(f"[bold red]{message}[/]")
|
self.console.print(f"[bold red]{message}[/]")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,18 @@ def clear_provider_auth(provider_id: Optional[str] = None) -> bool:
|
||||||
return True
|
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
|
# 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:
|
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 = get_config_path()
|
||||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,14 +89,31 @@ def cmd_model(args):
|
||||||
current_model = current_model.get("default", "")
|
current_model = current_model.get("default", "")
|
||||||
current_model = current_model or "(not set)"
|
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 = {
|
provider_labels = {
|
||||||
"openrouter": "OpenRouter",
|
"openrouter": "OpenRouter",
|
||||||
"nous": "Nous Portal",
|
"nous": "Nous Portal",
|
||||||
|
"custom": "Custom endpoint",
|
||||||
}
|
}
|
||||||
active_label = provider_labels.get(active, "Custom endpoint")
|
active_label = provider_labels.get(active, active)
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print(f" Current model: {current_model}")
|
print(f" Current model: {current_model}")
|
||||||
|
|
@ -177,7 +194,7 @@ def _prompt_provider_choice(choices):
|
||||||
|
|
||||||
def _model_flow_openrouter(config, current_model=""):
|
def _model_flow_openrouter(config, current_model=""):
|
||||||
"""OpenRouter provider: ensure API key, then pick 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
|
from hermes_cli.config import get_env_value, save_env_value
|
||||||
|
|
||||||
api_key = get_env_value("OPENROUTER_API_KEY")
|
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_BASE_URL", "")
|
||||||
save_env_value("OPENAI_API_KEY", "")
|
save_env_value("OPENAI_API_KEY", "")
|
||||||
_save_model_choice(selected)
|
_save_model_choice(selected)
|
||||||
# Update config provider
|
|
||||||
|
# Update config provider and deactivate any OAuth provider
|
||||||
from hermes_cli.config import load_config, save_config
|
from hermes_cli.config import load_config, save_config
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
model = cfg.get("model")
|
model = cfg.get("model")
|
||||||
|
|
@ -225,6 +243,7 @@ def _model_flow_openrouter(config, current_model=""):
|
||||||
model["provider"] = "openrouter"
|
model["provider"] = "openrouter"
|
||||||
model["base_url"] = "https://openrouter.ai/api/v1"
|
model["base_url"] = "https://openrouter.ai/api/v1"
|
||||||
save_config(cfg)
|
save_config(cfg)
|
||||||
|
deactivate_provider()
|
||||||
print(f"Default model set to: {selected} (via OpenRouter)")
|
print(f"Default model set to: {selected} (via OpenRouter)")
|
||||||
else:
|
else:
|
||||||
print("No change.")
|
print("No change.")
|
||||||
|
|
@ -234,9 +253,11 @@ def _model_flow_nous(config, current_model=""):
|
||||||
"""Nous Portal provider: ensure logged in, then pick model."""
|
"""Nous Portal provider: ensure logged in, then pick model."""
|
||||||
from hermes_cli.auth import (
|
from hermes_cli.auth import (
|
||||||
get_provider_auth_state, _prompt_model_selection, _save_model_choice,
|
get_provider_auth_state, _prompt_model_selection, _save_model_choice,
|
||||||
resolve_nous_runtime_credentials, fetch_nous_models,
|
_update_config_for_provider, resolve_nous_runtime_credentials,
|
||||||
AuthError, format_auth_error, _login_nous, PROVIDER_REGISTRY,
|
fetch_nous_models, AuthError, format_auth_error,
|
||||||
|
_login_nous, PROVIDER_REGISTRY,
|
||||||
)
|
)
|
||||||
|
from hermes_cli.config import get_env_value, save_env_value
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
state = get_provider_auth_state("nous")
|
state = get_provider_auth_state("nous")
|
||||||
|
|
@ -256,7 +277,7 @@ def _model_flow_nous(config, current_model=""):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"Login failed: {exc}")
|
print(f"Login failed: {exc}")
|
||||||
return
|
return
|
||||||
# login_nous already handles model selection, so we're done
|
# login_nous already handles model selection + config update
|
||||||
return
|
return
|
||||||
|
|
||||||
# Already logged in — fetch models and select
|
# 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)
|
selected = _prompt_model_selection(model_ids, current_model=current_model)
|
||||||
if selected:
|
if selected:
|
||||||
_save_model_choice(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)")
|
print(f"Default model set to: {selected} (via Nous Portal)")
|
||||||
else:
|
else:
|
||||||
print("No change.")
|
print("No change.")
|
||||||
|
|
@ -286,7 +314,7 @@ def _model_flow_nous(config, current_model=""):
|
||||||
|
|
||||||
def _model_flow_custom(config):
|
def _model_flow_custom(config):
|
||||||
"""Custom endpoint: collect URL, API key, and model name."""
|
"""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
|
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
|
||||||
|
|
||||||
current_url = get_env_value("OPENAI_BASE_URL") or ""
|
current_url = get_env_value("OPENAI_BASE_URL") or ""
|
||||||
|
|
@ -325,16 +353,19 @@ def _model_flow_custom(config):
|
||||||
if model_name:
|
if model_name:
|
||||||
_save_model_choice(model_name)
|
_save_model_choice(model_name)
|
||||||
|
|
||||||
# Update config to reflect custom provider
|
# Update config and deactivate any OAuth provider
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
model = cfg.get("model")
|
model = cfg.get("model")
|
||||||
if isinstance(model, dict):
|
if isinstance(model, dict):
|
||||||
model["provider"] = "auto"
|
model["provider"] = "auto"
|
||||||
model["base_url"] = effective_url
|
model["base_url"] = effective_url
|
||||||
save_config(cfg)
|
save_config(cfg)
|
||||||
|
deactivate_provider()
|
||||||
|
|
||||||
print(f"Default model set to: {model_name} (via {effective_url})")
|
print(f"Default model set to: {model_name} (via {effective_url})")
|
||||||
else:
|
else:
|
||||||
|
if base_url or api_key:
|
||||||
|
deactivate_provider()
|
||||||
print("Endpoint saved. Use `/model` in chat or `hermes model` to set a model.")
|
print("Endpoint saved. Use `/model` in chat or `hermes model` to set a model.")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue