fix: improve /model user feedback + update docs

User messaging improvements:
- Rejection: '(>_<) Error: not a valid model' instead of '(^_^) Warning: Error:'
- Rejection: shows 'Model unchanged' + tip about /model and /provider
- Session-only: explains 'this session only' with reason and 'will revert on restart'
- Saved: clear '(saved to config)' confirmation

Docs updated:
- cli-commands.md, cli.md, messaging/index.md: /model now shows
  provider:model syntax, /provider command added to tables

Test fixes: deduplicated test names, assertions match new messages.
This commit is contained in:
teknium1 2026-03-08 06:13:11 -07:00
parent d07d867718
commit a23bcb81ce
6 changed files with 29 additions and 16 deletions

18
cli.py
View file

@ -2101,8 +2101,10 @@ class HermesCLI:
validation = {"accepted": True, "persist": True, "recognized": False, "message": None}
if not validation.get("accepted"):
print(f"(^_^) Warning: {validation.get('message')}")
print(f"(^_^) Current model unchanged: {self.model}")
print(f"(>_<) {validation.get('message')}")
print(f" Model unchanged: {self.model}")
if "Did you mean" not in (validation.get("message") or ""):
print(" Tip: Use /model to see available models, /provider to see providers")
else:
self.model = new_model
self.agent = None # Force re-init
@ -2123,13 +2125,13 @@ class HermesCLI:
if saved_model:
print(f"(^_^)b Model changed to: {new_model}{provider_note} (saved to config)")
else:
print(f"(^_^) Model changed to: {new_model}{provider_note} (session only)")
print(f"(^_^) Model changed to: {new_model}{provider_note} (this session only)")
else:
print(f"(^_^) Model changed to: {new_model}{provider_note} (session only)")
message = validation.get("message")
if message:
print(f" Warning: {message}")
message = validation.get("message") or ""
print(f"(^_^) Model changed to: {new_model}{provider_note} (this session only)")
if message:
print(f" Reason: {message}")
print(" Note: Model will revert on restart. Use a verified model to save to config.")
else:
from hermes_cli.models import curated_models_for_provider, normalize_provider, _PROVIDER_LABELS
from hermes_cli.auth import resolve_provider as _resolve_provider

View file

@ -1414,7 +1414,9 @@ class GatewayRunner:
validation = {"accepted": True, "persist": True, "recognized": False, "message": None}
if not validation.get("accepted"):
return f"⚠️ {validation.get('message')}"
msg = validation.get("message", "Invalid model")
tip = "\n\nUse `/model` to see available models, `/provider` to see providers" if "Did you mean" not in msg else ""
return f"⚠️ {msg}{tip}"
# Persist to config only if validation approves
if validation.get("persist"):
@ -1445,7 +1447,10 @@ class GatewayRunner:
if validation.get("message"):
warning = f"\n⚠️ {validation['message']}"
persist_note = "saved to config" if validation.get("persist") else "session only"
if validation.get("persist"):
persist_note = "saved to config"
else:
persist_note = "this session only — will revert on restart"
return f"🤖 Model changed to `{new_model}` ({persist_note}){provider_note}{warning}\n_(takes effect on next message)_"
async def _handle_provider_command(self, event: MessageEvent) -> str:

View file

@ -41,10 +41,11 @@ class TestModelCommand:
output = capsys.readouterr().out
assert "not a valid model" in output
assert "Model unchanged" in output
assert cli_obj.model == "anthropic/claude-opus-4.6"
save_mock.assert_not_called()
def test_model_when_api_unreachable_falls_back_session_only(self, capsys):
def test_api_unreachable_falls_back_session_only(self, capsys):
cli_obj = self._make_cli()
with patch("hermes_cli.models.fetch_api_models", return_value=None), \
@ -53,6 +54,7 @@ class TestModelCommand:
output = capsys.readouterr().out
assert "session only" in output
assert "will revert on restart" in output
assert cli_obj.model == "anthropic/claude-sonnet-next"
save_mock.assert_not_called()
@ -66,8 +68,9 @@ class TestModelCommand:
output = capsys.readouterr().out
assert "not a valid model" in output
assert cli_obj.model == "anthropic/claude-opus-4.6"
fetch_mock.assert_called_once()
assert "Model unchanged" in output
assert cli_obj.model == "anthropic/claude-opus-4.6" # unchanged
assert cli_obj.agent is not None # not reset
save_mock.assert_not_called()
def test_validation_crash_falls_back_to_save(self, capsys):

View file

@ -139,7 +139,8 @@ Type `/` in the interactive CLI to see an autocomplete dropdown.
|---------|-------------|
| `/tools` | List all available tools |
| `/toolsets` | List available toolsets |
| `/model [name]` | Show or change the current model |
| `/model [provider:model]` | Show or change the current model (supports `provider:model` syntax to switch providers) |
| `/provider` | Show available providers with auth status |
| `/config` | Show current configuration |
| `/prompt [text]` | View/set custom system prompt |
| `/personality [name]` | Set a predefined personality |

View file

@ -95,7 +95,8 @@ Type `/` to see an autocomplete dropdown of all available commands.
|---------|-------------|
| `/tools` | List all available tools grouped by toolset |
| `/toolsets` | List available toolsets with descriptions |
| `/model [name]` | Show or change the current model |
| `/model [provider:model]` | Show or change the current model (supports `provider:model` syntax) |
| `/provider` | Show available providers with auth status |
| `/config` | Show current configuration |
| `/prompt [text]` | View/set/clear custom system prompt |
| `/personality [name]` | Set a predefined personality |

View file

@ -63,7 +63,8 @@ hermes gateway status # Check service status
| Command | Description |
|---------|-------------|
| `/new` or `/reset` | Start fresh conversation |
| `/model [name]` | Show or change the model |
| `/model [provider:model]` | Show or change the model (supports `provider:model` syntax) |
| `/provider` | Show available providers with auth status |
| `/personality [name]` | Set a personality |
| `/retry` | Retry the last message |
| `/undo` | Remove the last exchange |