feat(plugins): the model picker takes a title, public as prompt_model_selection

_prompt_model_selection gains title= (default 'Select default model:'), so a
plugin's route editor can say 'Select the model for this route:'. It is public
for plugins as hermes_cli.auth_model_picker.prompt_model_selection (listed in the
admission lint's public surfaces, with agent.delegation_backends) and documented
in the delegation provider guide.
This commit is contained in:
Teknium committed 2026-10-22 14:37:10 -07:00
1 parent 070dac76fa
commit 7a8569baa9
3 files changed
+17 -9

No files matched your search

+11 -7
View File
@@ -133,9 +133,8 @@ class _ModelPickerRows:
def label(self, mid: str) -> str:
return "".join(text for text, _style in self.segments(mid))
def menu_title(self) -> str:
"""``Select default model:`` plus an aligned pricing header hint when priced."""
title = "Select default model:"
def menu_title(self, title: str = "Select default model:") -> str:
"""*title* plus an aligned pricing header hint when priced."""
if self.has_pricing:
# Each choice is " {label}" (2 spaces) plus a 3-char cursor region ("-> " or " "),
# so content starts at col 5.
@@ -155,9 +154,10 @@ def _prompt_model_selection(
pricing: Optional[dict[str, dict[str, str]]] = None,
unavailable_models: Optional[list[str]] = None, portal_url: str = "",
unavailable_message: str = "", confirm_provider: str = "", confirm_base_url: str = "",
confirm_api_key: str = "", notes: Optional[dict[str, str]] = None,
confirm_api_key: str = "", notes: Optional[dict[str, str]] = None, title: str = "Select default model:",
) -> Optional[str]:
"""Interactive model picker; current_model listed first. Returns the chosen model ID or None.
"""Interactive model picker; current_model listed first. Returns the chosen model ID or None. *title* heads
the menu (a plugin picking a model for something other than the default says so).
With *pricing* (``{model_id: {prompt, completion}}``) a compact price column is shown; models in
*unavailable_models* render grayed out and unselectable with an upgrade link to *portal_url*.
@@ -194,7 +194,7 @@ def _prompt_model_selection(
_DIM = "\033[2m"
_RESET = "\033[0m"
menu_title = rows.menu_title()
menu_title = rows.menu_title(title)
_upgrade_url = (portal_url or DEFAULT_NOUS_PORTAL_URL).rstrip("/")
n = len(ordered)
@@ -223,7 +223,7 @@ def _prompt_model_selection(
model_search_labels += [_CUSTOM_LABEL, _SKIP_LABEL]
idx = curses_radiolist(
"Select default model:",
title,
choices,
selected=0, # cursor on the current model (index 0 if it was reordered to top)
cancel_returns=-1,
@@ -304,3 +304,7 @@ def _save_model_choice(model_id: str) -> None:
from hermes_cli.observability.shared_metrics_setup import note_provider_setup_saved
note_provider_setup_saved()
# Public for plugins (catalog rule 9): the interactive model picker under a caller's title.
prompt_model_selection = _prompt_model_selection
@@ -27,7 +27,7 @@ from hermes_cli.plugin_validate_core_override import _hermes_top_level, _python_
_PUBLIC = frozenset({
# developer-guide / plugins docs
"agent.account_usage", "agent.browser_provider", "agent.context_engine", "agent.credential_pool",
"agent.delegation_provider",
"agent.delegation_backends", "agent.delegation_provider", "hermes_cli.auth_model_picker:prompt_model_selection",
"agent.image_gen_provider", "agent.memory_manager", "agent.memory_provider", "agent.secret_scope",
"agent.secret_sources.base", "agent.subagent_lifecycle", "agent.terminal_env_provider",
"agent.transports", "agent.transports.chat_completions", "agent.video_gen_provider",
@@ -159,7 +159,11 @@ uses:
| `list_backends() -> list[BackendInfo]` | `default` first, then every registered backend: `name`, `label`, `description`, `available`, `reason` (the fix, when unavailable). |
| `get_session_backend(session_key) -> str \| None` | The session's override, `None` when it follows `config.yaml`. |
| `set_session_backend(session_key, name \| None)` | Switch one session (`None` clears). Raises `ValueError` with the reason for a backend that cannot run. |
| `set_global_backend(name, *, session_key="")` | Write `delegation.backend` to the active profile's `config.yaml` and drop that session's override. |
| `set_global_backend(name, *, session_key="")` | Write `delegation.backend` to the active profile's `config.yaml` and drop that session's override. Raises `PermissionError` on a package-manager-managed install. |
A plugin command that edits model routes can reuse the interactive model picker under its own heading:
`hermes_cli.auth_model_picker.prompt_model_selection(model_ids, current_model="", title="Select the model for this route:")`
returns the chosen model id, or `None` when cancelled.
`session_key` is the `session_key` a plugin command handler receives. A session switch decides routing
from that session's next `delegate_task` call; the `delegate_task` schema (your parameters) follows