mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
union paid recs from nous portal with static list (#24509)
This commit is contained in:
parent
d186186e1a
commit
c23a87bc16
4 changed files with 226 additions and 1 deletions
|
|
@ -5271,6 +5271,7 @@ def _login_nous(args, pconfig: ProviderConfig) -> None:
|
|||
get_curated_nous_model_ids, get_pricing_for_provider,
|
||||
check_nous_free_tier, partition_nous_models_by_tier,
|
||||
union_with_portal_free_recommendations,
|
||||
union_with_portal_paid_recommendations,
|
||||
)
|
||||
model_ids = get_curated_nous_model_ids()
|
||||
|
||||
|
|
@ -5279,19 +5280,27 @@ def _login_nous(args, pconfig: ProviderConfig) -> None:
|
|||
if model_ids:
|
||||
pricing = get_pricing_for_provider("nous")
|
||||
free_tier = check_nous_free_tier()
|
||||
_portal_for_recs = auth_state.get("portal_base_url", "")
|
||||
if free_tier:
|
||||
# The Portal's freeRecommendedModels endpoint is the
|
||||
# source of truth for what's free *right now*. Augment
|
||||
# the curated list with anything new the Portal flags
|
||||
# as free so users on older Hermes builds still see
|
||||
# newly-launched free models without a CLI release.
|
||||
_portal_for_recs = auth_state.get("portal_base_url", "")
|
||||
model_ids, pricing = union_with_portal_free_recommendations(
|
||||
model_ids, pricing, _portal_for_recs,
|
||||
)
|
||||
model_ids, unavailable_models = partition_nous_models_by_tier(
|
||||
model_ids, pricing, free_tier=True,
|
||||
)
|
||||
else:
|
||||
# Paid-tier mirror: pull paidRecommendedModels so newly
|
||||
# launched paid models surface in the picker even if
|
||||
# the in-repo curated list and docs-hosted manifest
|
||||
# haven't caught up yet.
|
||||
model_ids, pricing = union_with_portal_paid_recommendations(
|
||||
model_ids, pricing, _portal_for_recs,
|
||||
)
|
||||
_portal = auth_state.get("portal_base_url", "")
|
||||
if model_ids:
|
||||
print(f"Showing {len(model_ids)} curated models — use \"Enter custom model name\" for others.")
|
||||
|
|
|
|||
|
|
@ -2590,6 +2590,7 @@ def _model_flow_nous(config, current_model="", args=None):
|
|||
check_nous_free_tier,
|
||||
partition_nous_models_by_tier,
|
||||
union_with_portal_free_recommendations,
|
||||
union_with_portal_paid_recommendations,
|
||||
)
|
||||
|
||||
model_ids = get_curated_nous_model_ids()
|
||||
|
|
@ -2645,6 +2646,10 @@ def _model_flow_nous(config, current_model="", args=None):
|
|||
# with the Portal's freeRecommendedModels list so newly-launched free
|
||||
# models show up even if this CLI build's hardcoded curated list and
|
||||
# docs-hosted manifest haven't caught up yet.
|
||||
#
|
||||
# For paid users: mirror the same idea with paidRecommendedModels so
|
||||
# newly-launched paid models surface in the picker too — independent
|
||||
# of CLI release cadence.
|
||||
unavailable_models: list[str] = []
|
||||
if free_tier:
|
||||
model_ids, pricing = union_with_portal_free_recommendations(
|
||||
|
|
@ -2653,6 +2658,10 @@ def _model_flow_nous(config, current_model="", args=None):
|
|||
model_ids, unavailable_models = partition_nous_models_by_tier(
|
||||
model_ids, pricing, free_tier=True
|
||||
)
|
||||
else:
|
||||
model_ids, pricing = union_with_portal_paid_recommendations(
|
||||
model_ids, pricing, _nous_portal_url,
|
||||
)
|
||||
|
||||
if not model_ids and not unavailable_models:
|
||||
print("No models available for Nous Portal after filtering.")
|
||||
|
|
|
|||
|
|
@ -621,6 +621,71 @@ def union_with_portal_free_recommendations(
|
|||
return (augmented_ids, augmented_pricing)
|
||||
|
||||
|
||||
def union_with_portal_paid_recommendations(
|
||||
curated_ids: list[str],
|
||||
pricing: dict[str, dict[str, str]],
|
||||
portal_base_url: str = "",
|
||||
*,
|
||||
force_refresh: bool = False,
|
||||
) -> tuple[list[str], dict[str, dict[str, str]]]:
|
||||
"""Augment curated list with the Portal's ``paidRecommendedModels``.
|
||||
|
||||
Mirror of :func:`union_with_portal_free_recommendations` for paid-tier
|
||||
users. The Portal's ``/api/nous/recommended-models`` endpoint advertises
|
||||
which paid models are blessed *right now* — independent of what the
|
||||
in-repo ``_PROVIDER_MODELS["nous"]`` list happens to contain or whether
|
||||
the docs-hosted catalog manifest has been rebuilt since the last release.
|
||||
|
||||
For paid-tier users this lets newly-launched paid models surface in the
|
||||
picker even if the user is running an older Hermes that doesn't ship
|
||||
them in its hardcoded curated list. This function returns an augmented
|
||||
``(model_ids, pricing)`` pair where:
|
||||
|
||||
* Portal paid recommendations missing from ``curated_ids`` are
|
||||
appended at the front (so the picker shows them first).
|
||||
* ``pricing`` is left untouched — we deliberately do NOT synthesize
|
||||
pricing entries for paid models. Live pricing is fetched separately
|
||||
via :func:`get_pricing_for_provider`; if the live endpoint hasn't
|
||||
published pricing yet, the picker shows a blank price column rather
|
||||
than fabricating numbers. (The free helper synthesizes ``$0`` so
|
||||
:func:`partition_nous_models_by_tier` keeps free models selectable;
|
||||
no equivalent gating applies on the paid side, so synthesis would
|
||||
only mislead the user.)
|
||||
|
||||
Failures (network, parse, missing field) are silent and degrade to
|
||||
returning the inputs unchanged — never block the picker on a
|
||||
Portal-side hiccup.
|
||||
"""
|
||||
try:
|
||||
payload = fetch_nous_recommended_models(
|
||||
portal_base_url, force_refresh=force_refresh
|
||||
)
|
||||
except Exception:
|
||||
return (list(curated_ids), dict(pricing))
|
||||
|
||||
paid_block = payload.get("paidRecommendedModels") if isinstance(payload, dict) else None
|
||||
if not isinstance(paid_block, list) or not paid_block:
|
||||
return (list(curated_ids), dict(pricing))
|
||||
|
||||
portal_paid_ids: list[str] = []
|
||||
for entry in paid_block:
|
||||
name = _extract_model_name(entry)
|
||||
if name:
|
||||
portal_paid_ids.append(name)
|
||||
if not portal_paid_ids:
|
||||
return (list(curated_ids), dict(pricing))
|
||||
|
||||
augmented_ids = list(curated_ids)
|
||||
seen = set(augmented_ids)
|
||||
# Prepend Portal paid recommendations that aren't already curated, so
|
||||
# the Portal-blessed picks surface first in the picker.
|
||||
new_ones = [mid for mid in portal_paid_ids if mid not in seen]
|
||||
if new_ones:
|
||||
augmented_ids = new_ones + augmented_ids
|
||||
|
||||
return (augmented_ids, dict(pricing))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TTL cache for free-tier detection — avoids repeated API calls within a
|
||||
# session while still picking up upgrades quickly.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue