diff --git a/cli.py b/cli.py index 044f265e297..4d370aa221f 100644 --- a/cli.py +++ b/cli.py @@ -4633,16 +4633,10 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): 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 that survived the slash split: - # ``ri.language-model-service..language-model.anthropic-claude-4-7-opus`` - # → ``claude-4-7-opus``. The double-dot is Palantir's RID separator. - if model_short.startswith("ri.") and ".." in model_short: - _tail = model_short.split("..", 1)[1] - # Drop the leading namespace token (``language-model.``). - if "." in _tail: - _tail = _tail.split(".", 1)[1] - if _tail: - model_short = _tail + # 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: @@ -8058,14 +8052,18 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): ) 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, @@ -8389,8 +8387,12 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): # 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.]" @@ -8402,7 +8404,7 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): # 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, diff --git a/gateway/slash_commands.py b/gateway/slash_commands.py index 03c3017c8ba..419a11ad293 100644 --- a/gateway/slash_commands.py +++ b/gateway/slash_commands.py @@ -1663,11 +1663,17 @@ class GatewaySlashCommandsMixin: "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.]" ) @@ -1743,9 +1749,11 @@ class GatewaySlashCommandsMixin: 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 @@ -1939,10 +1947,13 @@ class GatewaySlashCommandsMixin: # 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.]" @@ -2038,7 +2049,7 @@ class GatewaySlashCommandsMixin: # 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, diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index ee58f99ef87..5d58c5911eb 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -200,6 +200,51 @@ _NOUS_HERMES_NON_AGENTIC_RE = re.compile( ) +# Opaque internal model-ID display +# --------------------------------------------------------------------------- +# Some proxies (notably Palantir Foundry's LLM-proxy) identify models by +# resource-instance IDs that are deeply nested, verbose, and pure noise to +# read in CLI status output, e.g.: +# +# ri.language-model-service..language-model.anthropic-claude-4-7-opus +# +# The provider_label (e.g. "palantir-claude46") already carries the routing +# context, so the only useful information left in the opaque ID is the +# trailing slug. Strip the boilerplate prefix for *display* — never for +# wire-side comparison, persistence, config writes, alias lookup, or +# anything that round-trips back into the API. +# +# Match by substring on a known prefix so we never accidentally truncate +# a legitimate model name that happens to contain dots. + +_OPAQUE_MODEL_PREFIXES: tuple[str, ...] = ( + "ri.language-model-service..language-model.", +) + + +def format_model_for_display(model_name: str) -> str: + """Return a human-friendly form of *model_name* for CLI status output. + + Strips known opaque proxy prefixes (Palantir Foundry's + ``ri.language-model-service..language-model.*``) and returns the + trailing slug. Falls through to the original string for everything + else, so real model IDs (``claude-4-7-opus-20260101``, + ``gpt-5-4``, ``meta-llama/Llama-3.3-70B-Instruct``) are untouched. + + This is a DISPLAY-ONLY helper. Do NOT use the return value for any + wire-side operation — the proxy expects the full opaque ID, and + callers that compare or persist must keep the original. + """ + if not model_name: + return model_name + for prefix in _OPAQUE_MODEL_PREFIXES: + if model_name.startswith(prefix): + tail = model_name[len(prefix):] + return tail if tail else model_name + return model_name + + +# --------------------------------------------------------------------------- def is_nous_hermes_non_agentic(model_name: str) -> bool: """Return True if *model_name* is a real Nous Hermes 3/4 chat model.