Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 69 additions & 5 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,52 @@ def format_duration_compact(*args, **kwargs):
return f"{days:.1f}d"


# Cached reverse map of config.yaml ``model_aliases:`` so the TUI can show
# friendly names instead of full Palantir RIDs / long catalog IDs. Built
# lazily on first call; cache is process-lifetime (config is read once at
# session start, so further invalidation is unnecessary).
_REVERSE_ALIAS_CACHE: dict[str, str] | None = None


def _reverse_alias_for_display(model_name: str) -> str:
"""Return the shortest configured alias for ``model_name``, or ``model_name``.

Looks up both ``model_aliases:`` (dict-based, full DirectAlias entries)
and ``model.aliases:`` (string-based, set via ``hermes config set``)
from config.yaml. Multiple aliases pointing at the same model — the
shortest wins, so ``opus47`` beats ``palantir-claude47``.
"""
global _REVERSE_ALIAS_CACHE
if not model_name:
return model_name
if _REVERSE_ALIAS_CACHE is None:
rmap: dict[str, str] = {}
try:
from hermes_cli.config import load_config
cfg = load_config() or {}
ma = cfg.get("model_aliases")
if isinstance(ma, dict):
for alias, entry in ma.items():
if isinstance(entry, dict):
m = str(entry.get("model", "") or "").strip()
if m and (m not in rmap or len(alias) < len(rmap[m])):
rmap[m] = alias
mdl = cfg.get("model", {}) or {}
if isinstance(mdl, dict):
simple = mdl.get("aliases")
if isinstance(simple, dict):
for alias, val in simple.items():
if isinstance(val, str) and val.strip():
v = val.strip()
m = v.split("/", 1)[1] if "/" in v else v
if m and (m not in rmap or len(alias) < len(rmap[m])):
rmap[m] = alias
except Exception:
pass
_REVERSE_ALIAS_CACHE = rmap
return _REVERSE_ALIAS_CACHE.get(model_name, model_name)


def format_token_count_compact(*args, **kwargs):
value = int(args[0] if args else kwargs.get("value", 0))
abs_value = abs(value)
Expand Down Expand Up @@ -4580,7 +4626,17 @@ def _get_status_bar_snapshot(self) -> Dict[str, Any]:
# _try_activate_fallback() switches provider/model.
agent = getattr(self, "agent", None)
model_name = (getattr(agent, "model", None) or self.model or "unknown")
model_short = model_name.split("/")[-1] if "/" in model_name else model_name
# Friendly display: prefer reverse-alias from config.yaml ``model_aliases:``
# before slash/length truncation. This turns long Palantir RIDs like
# ``ri.language-model-service..language-model.anthropic-claude-4-7-opus``
# into the user's chosen short name (e.g. ``opus-4.7``) in the status bar.
model_short = _reverse_alias_for_display(model_name)
if model_short == model_name:
model_short = model_name.split("/")[-1] if "/" in model_name else model_name
# Strip Palantir RID prefixes via the shared display formatter so
# this site and ``ModelSwitchResult`` confirmation can't drift.
from hermes_cli.model_switch import format_model_for_display
model_short = format_model_for_display(model_short)
if model_short.endswith(".gguf"):
model_short = model_short[:-5]
if len(model_short) > 26:
Expand Down Expand Up @@ -7996,14 +8052,18 @@ def _apply_model_switch_result(self, result, persist_global: bool) -> None:
)
return

from hermes_cli.model_switch import format_model_for_display
_display_old = format_model_for_display(old_model)
_display_new = format_model_for_display(result.new_model)

self._pending_model_switch_note = (
f"[Note: model was just switched from {old_model} to {result.new_model} "
f"[Note: model was just switched from {_display_old} to {_display_new} "
f"via {result.provider_label or result.target_provider}. "
f"Adjust your self-identification accordingly.]"
)

provider_label = result.provider_label or result.target_provider
_cprint(f" ✓ Model switched: {result.new_model}")
_cprint(f" ✓ Model switched: {_display_new}")
_cprint(f" Provider: {provider_label}")

# Context: always resolve via the provider-aware chain so Codex OAuth,
Expand Down Expand Up @@ -8327,8 +8387,12 @@ def _handle_model_switch(self, cmd_original: str):
# Store a note to prepend to the next user message so the model
# knows a switch occurred (avoids injecting system messages mid-history
# which breaks providers and prompt caching).
from hermes_cli.model_switch import format_model_for_display
_display_old = format_model_for_display(old_model)
_display_new = format_model_for_display(result.new_model)

self._pending_model_switch_note = (
f"[Note: model was just switched from {old_model} to {result.new_model} "
f"[Note: model was just switched from {_display_old} to {_display_new} "
f"via {result.provider_label or result.target_provider}. "
f"{'This override applies to the next turn only. ' if one_turn else ''}"
f"Adjust your self-identification accordingly.]"
Expand All @@ -8340,7 +8404,7 @@ def _handle_model_switch(self, cmd_original: str):

# Display confirmation with full metadata
provider_label = result.provider_label or result.target_provider
_cprint(f" ✓ Model switched: {result.new_model}")
_cprint(f" ✓ Model switched: {_display_new}")
_cprint(f" Provider: {provider_label}")

# Context: always resolve via the provider-aware chain so Codex OAuth,
Expand Down
23 changes: 17 additions & 6 deletions gateway/slash_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -1663,11 +1663,17 @@ async def _on_model_selected_scoped(
"Failed to persist model switch to DB: %s", exc
)

# Store model note + session override
# Store model note + session override. Use display
# form (strips opaque Palantir prefix) for the user-
# visible note; session-override map still gets the
# full opaque ID, which is what the wire needs.
from hermes_cli.model_switch import format_model_for_display
_display_cur = format_model_for_display(_cur_model)
_display_new = format_model_for_display(result.new_model)
if not hasattr(_self, "_pending_model_notes"):
_self._pending_model_notes = {}
_self._pending_model_notes[_session_key] = (
f"[Note: model was just switched from {_cur_model} to {result.new_model} "
f"[Note: model was just switched from {_display_cur} to {_display_new} "
f"via {result.provider_label or result.target_provider}. "
f"Adjust your self-identification accordingly.]"
)
Expand Down Expand Up @@ -1743,9 +1749,11 @@ async def _on_model_selected_scoped(
except Exception as e:
logger.warning("Failed to persist model switch: %s", e)

# Build confirmation text
# Build confirmation text. Use display form so opaque
# Palantir IDs (ri.language-model-service..*) get
# shortened to their trailing slug for the UI.
plabel = result.provider_label or result.target_provider
lines = [t("gateway.model.switched", model=result.new_model)]
lines = [t("gateway.model.switched", model=format_model_for_display(result.new_model))]
lines.append(t("gateway.model.provider_label", provider=plabel))
mi = result.model_info
from hermes_cli.model_switch import resolve_display_context_length
Expand Down Expand Up @@ -1939,10 +1947,13 @@ async def _finish_switch() -> str:

# Store a note to prepend to the next user message so the model
# knows about the switch (avoids system messages mid-history).
# Display form strips opaque Palantir RID prefixes; the override
# map below keeps the full ID for the wire.
from hermes_cli.model_switch import format_model_for_display
if not hasattr(self, "_pending_model_notes"):
self._pending_model_notes = {}
self._pending_model_notes[session_key] = (
f"[Note: model was just switched from {current_model} to {result.new_model} "
f"[Note: model was just switched from {format_model_for_display(current_model)} to {format_model_for_display(result.new_model)} "
f"via {result.provider_label or result.target_provider}. "
f"{'This override applies to the next turn only. ' if one_turn else ''}"
f"Adjust your self-identification accordingly.]"
Expand Down Expand Up @@ -2038,7 +2049,7 @@ async def _finish_switch() -> str:

# Build confirmation message with full metadata
provider_label = result.provider_label or result.target_provider
lines = [t("gateway.model.switched", model=result.new_model)]
lines = [t("gateway.model.switched", model=format_model_for_display(result.new_model))]
lines.append(t("gateway.model.provider_label", provider=provider_label))

# Context: always resolve via the provider-aware chain so Codex OAuth,
Expand Down
Loading
Loading