fix(free-tier): the desktop card body leaves the "To sign in" tail off; the button is the door
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -264,7 +264,8 @@ def nous_rate_limit_guard(
|
||||
"failed": True,
|
||||
"error": _nous_msg,
|
||||
# The free tier's card body and its sign-in door (agent/error_surface.py).
|
||||
**({"free_tier": {"kind": "rate_limited", "message": _nous_msg}} if _welcome else {}),
|
||||
**({"free_tier": {"kind": "rate_limited", "message": anon_auth.FREE_TIER_RATE_LIMIT_CARD.format(
|
||||
reset=anon_auth.friendly_wait(_nous_remaining))}} if _welcome else {}),
|
||||
}, FailoverReason.rate_limit.value, True))
|
||||
except Exception:
|
||||
pass # Never let rate guard break the agent loop
|
||||
|
||||
@@ -706,7 +706,7 @@ def _print_nonretryable_auth_guidance(
|
||||
_vlines(agent, " • Check credits: https://openrouter.ai/settings/credits")
|
||||
|
||||
|
||||
def _welcome_tier_guidance(classified: Any, *, model: Any, in_chat: bool) -> str:
|
||||
def _welcome_tier_guidance(classified: Any, *, model: Any, in_chat: bool, door: bool = True) -> str:
|
||||
"""Copy for a Nous free-tier refusal the classifier parsed (``welcome_refusal`` /
|
||||
``welcome_route`` in ``error_context``); empty for every other error."""
|
||||
ctx = getattr(classified, "error_context", None) or {}
|
||||
@@ -715,8 +715,8 @@ def _welcome_tier_guidance(classified: Any, *, model: Any, in_chat: bool) -> str
|
||||
return ""
|
||||
from hermes_cli.anon_auth import welcome_refusal_copy, welcome_route_refusal_copy
|
||||
if refusal:
|
||||
return welcome_refusal_copy(refusal, model=str(model or ""), in_chat=in_chat)
|
||||
return welcome_route_refusal_copy(str(route), in_chat=in_chat)
|
||||
return welcome_refusal_copy(refusal, model=str(model or ""), in_chat=in_chat, door=door)
|
||||
return welcome_route_refusal_copy(str(route), in_chat=in_chat, door=door)
|
||||
|
||||
|
||||
def _welcome_surface_kind(classified: Any) -> str:
|
||||
@@ -881,7 +881,9 @@ def nonretryable_client_error_result(
|
||||
"failure_retryable": bool(classified.retryable),
|
||||
})
|
||||
if _welcome_hint:
|
||||
_stamp_free_tier(result, _welcome_surface_kind(classified), _final_response)
|
||||
# The card form: the desktop renders the sign-in as a button, so no "To sign in" tail.
|
||||
_stamp_free_tier(result, _welcome_surface_kind(classified),
|
||||
_welcome_tier_guidance(classified, model=model, in_chat=True, door=False))
|
||||
return result
|
||||
|
||||
|
||||
@@ -1011,7 +1013,9 @@ def max_retries_exhausted_result(
|
||||
"billing_block": _billing_block,
|
||||
})
|
||||
if _free_tier_kind:
|
||||
_stamp_free_tier(result, _free_tier_kind, _final_response)
|
||||
_stamp_free_tier(result, _free_tier_kind, (
|
||||
_welcome_tier_guidance(classified, model=model, in_chat=True, door=False)
|
||||
if _welcome_hint else _final_response))
|
||||
return result
|
||||
|
||||
|
||||
|
||||
+14
-11
@@ -675,11 +675,13 @@ def parse_welcome_refusal(body: Any) -> Optional[Dict[str, Any]]:
|
||||
"upgrade_url": upgrade_url if isinstance(upgrade_url, str) else ""}
|
||||
|
||||
|
||||
def welcome_refusal_copy(refusal: Dict[str, Any], *, model: str = "", in_chat: bool = True) -> str:
|
||||
def welcome_refusal_copy(refusal: Dict[str, Any], *, model: str = "", in_chat: bool = True, door: bool = True) -> str:
|
||||
"""User copy for a structured welcome-tier refusal: what happened and the one way forward.
|
||||
|
||||
Never guest / anonymous / claim; ``in_chat`` picks ``/login`` over the terminal verb."""
|
||||
signin = _SIGNIN_CHAT if in_chat else _SIGNIN_TERMINAL
|
||||
Never guest / anonymous / claim; ``in_chat`` picks ``/login`` over the terminal verb.
|
||||
``door=False`` leaves the "To sign in: …" tail off, for a surface that renders the sign-in as
|
||||
a button beside the sentence (the desktop's error card)."""
|
||||
signin = (_SIGNIN_CHAT if in_chat else _SIGNIN_TERMINAL) if door else ""
|
||||
reason = str(refusal.get("reason") or "")
|
||||
alternates = refusal.get("alternates") or []
|
||||
serves = alternates[0] if alternates else GUEST_MODEL
|
||||
@@ -690,17 +692,17 @@ def welcome_refusal_copy(refusal: Dict[str, Any], *, model: str = "", in_chat: b
|
||||
return (f"{what} available without signing in, so Hermes uses {serves} for now. "
|
||||
f"Sign in for more models. {signin}")
|
||||
if reason == "feature_not_free":
|
||||
return f"That isn't available without signing in. Sign in to use it, it's free. {signin}"
|
||||
return f"That isn't available without signing in. Sign in to use it, it's free. {signin}".rstrip()
|
||||
if reason == "at_capacity":
|
||||
return ("Chatting without signing in is really busy right now. Sign in to skip the queue, "
|
||||
f"it's free, or try again in {wait}. {signin}")
|
||||
f"it's free, or try again in {wait}. {signin}").rstrip()
|
||||
if reason == "admission_closed":
|
||||
return ("Chatting without signing in is full right now. Sign in to keep going, "
|
||||
f"it's free, or try again in {wait}. {signin}")
|
||||
f"it's free, or try again in {wait}. {signin}").rstrip()
|
||||
if reason == "rate_limited":
|
||||
return (f"You've used up the allowance for chatting without signing in. It refreshes in {wait}. "
|
||||
f"Sign in for a bigger allowance, it's free. {signin}")
|
||||
return f"Hermes couldn't send that without signing in. Signing in is free. {signin}"
|
||||
f"Sign in for a bigger allowance, it's free. {signin}").rstrip()
|
||||
return f"Hermes couldn't send that without signing in. Signing in is free. {signin}".rstrip()
|
||||
|
||||
|
||||
def welcome_route_refusal(status: Any, message: Any, base_url: Any = None) -> Optional[str]:
|
||||
@@ -723,11 +725,11 @@ def welcome_route_refusal(status: Any, message: Any, base_url: Any = None) -> Op
|
||||
return kind
|
||||
|
||||
|
||||
def welcome_route_refusal_copy(kind: str, *, in_chat: bool = True) -> str:
|
||||
def welcome_route_refusal_copy(kind: str, *, in_chat: bool = True, door: bool = True) -> str:
|
||||
template = _WELCOME_ROUTE_COPY.get(kind) or "Hermes couldn't reach the free model on this route."
|
||||
return template.format(
|
||||
host=DEFAULT_NOUS_WELCOME_URL, signin=_SIGNIN_CHAT if in_chat else _SIGNIN_TERMINAL,
|
||||
model_hint=_MODEL_HINT_CHAT if in_chat else _MODEL_HINT_TERMINAL)
|
||||
host=DEFAULT_NOUS_WELCOME_URL, signin=(_SIGNIN_CHAT if in_chat else _SIGNIN_TERMINAL) if door else "",
|
||||
model_hint=_MODEL_HINT_CHAT if in_chat else _MODEL_HINT_TERMINAL).rstrip()
|
||||
|
||||
|
||||
def note_model_switch(agent: Any, headers: Any) -> Optional[str]:
|
||||
@@ -1034,6 +1036,7 @@ from hermes_cli.anon_sign_in import ( # noqa: E402
|
||||
Code as Code,
|
||||
Completed as Completed,
|
||||
Declined as Declined,
|
||||
FREE_TIER_RATE_LIMIT_CARD as FREE_TIER_RATE_LIMIT_CARD,
|
||||
FREE_TIER_RATE_LIMIT_CHAT as FREE_TIER_RATE_LIMIT_CHAT,
|
||||
Failed as Failed,
|
||||
LOGIN_BUSY_ELSEWHERE as LOGIN_BUSY_ELSEWHERE,
|
||||
|
||||
@@ -47,9 +47,11 @@ LOGIN_STARTING = "Starting sign-in..."
|
||||
LOGIN_DM_ONLY = "Sign in from a direct message with Hermes."
|
||||
LOGIN_BUSY_ELSEWHERE = "Another sign-in is already running on this Hermes. Try again in a few minutes."
|
||||
LOGIN_NOT_ALLOWED = "Only an operator of this Hermes can sign it in."
|
||||
FREE_TIER_RATE_LIMIT_CHAT = (
|
||||
# The card form (a surface with its own sign-in button) and the chat form (names /login).
|
||||
FREE_TIER_RATE_LIMIT_CARD = (
|
||||
"You've used up the allowance for chatting without signing in. It refreshes in {reset}. "
|
||||
"Sign in for a bigger allowance, it's free: /login.")
|
||||
"Sign in for a bigger allowance, it's free.")
|
||||
FREE_TIER_RATE_LIMIT_CHAT = FREE_TIER_RATE_LIMIT_CARD[:-1] + ": /login."
|
||||
|
||||
|
||||
def format_wait_line(expires_in: int) -> str:
|
||||
|
||||
@@ -181,8 +181,11 @@ class TestTerminalResultsCarryTheFreeTierBlock:
|
||||
self._terminal_agent(), err, classified, status_code=403, api_kwargs=None, api_messages=[],
|
||||
messages=[], conversation_history=[], api_call_count=1, approx_tokens=10,
|
||||
provider="nous", base_url=WELCOME, model="nous/welcome")
|
||||
assert result["free_tier"] == {"kind": "disabled", "message": result["final_response"]}
|
||||
# The chat text names /login; the card text (a button beside it) leaves that tail off.
|
||||
assert "switched off" in result["final_response"] and "/login" in result["final_response"]
|
||||
assert result["free_tier"]["kind"] == "disabled"
|
||||
assert result["free_tier"]["message"] == result["final_response"].replace(" To sign in: /login.", "")
|
||||
assert "/login" not in result["free_tier"]["message"]
|
||||
assert result["error"] == "HTTP 403: no permissions" # the technical detail stays in the log line
|
||||
|
||||
def test_an_exhausted_capacity_refusal_is_stamped_at_capacity(self):
|
||||
@@ -194,8 +197,8 @@ class TestTerminalResultsCarryTheFreeTierBlock:
|
||||
api_kwargs=None, api_messages=[], messages=[], conversation_history=[], api_call_count=3,
|
||||
approx_tokens=10, provider="nous", base_url=WELCOME, model="nous/welcome")
|
||||
assert result["free_tier"]["kind"] == "at_capacity"
|
||||
assert result["free_tier"]["message"] == result["final_response"]
|
||||
assert "really busy" in result["final_response"]
|
||||
assert "really busy" in result["free_tier"]["message"] and "/login" not in result["free_tier"]["message"]
|
||||
assert "/login" in result["final_response"]
|
||||
|
||||
def test_a_spent_outage_on_the_welcome_host_is_stamped_outage(self):
|
||||
from agent.turn_recovery import max_retries_exhausted_result
|
||||
|
||||
Reference in New Issue
Block a user