fix(picker): remove max_models=50 cap in interactive model pickers
The interactive model pickers (Desktop REST API, TUI model.options, CLI /model) were hard-capped at max_models=50, which truncated large provider catalogs like Kilo Gateway (336 models) to just 50 entries. This made most models undiscoverable via the picker search box. Changes: - Change build_models_payload() default from max_models=50 to None (unlimited) - Change list_authenticated_providers() default from max_models=8 to None - Change list_picker_providers() default from max_models=8 to None - Fix all [:max_models] slicing to handle None as 'no limit' - Remove max_models=50 from 5 interactive picker callers: * web_server.py: get_model_options (Desktop /api/model/options) * web_server.py: get_recommended_default_model * model_switch.py: prewarm_picker_cache_async * tui_gateway/server.py: model.options JSON-RPC * cli.py: HermesCLI model picker - Telegram/Discord inline keyboard picker (gateway/slash_commands.py) still passes max_models=50 explicitly — unchanged behavior. The total_models field was already in the response payload and is now meaningful since models.length == total_models for interactive pickers. Fixes #48279
This commit is contained in:
parent
4ed2f33994
commit
9705e7944a
2
cli.py
2
cli.py
@ -6971,7 +6971,7 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
|
|||||||
try:
|
try:
|
||||||
if ctx is None:
|
if ctx is None:
|
||||||
raise RuntimeError("inventory context unavailable")
|
raise RuntimeError("inventory context unavailable")
|
||||||
providers = build_models_payload(ctx, max_models=50)["providers"]
|
providers = build_models_payload(ctx)["providers"]
|
||||||
except Exception:
|
except Exception:
|
||||||
providers = []
|
providers = []
|
||||||
|
|
||||||
|
|||||||
@ -117,7 +117,7 @@ def build_models_payload(
|
|||||||
pricing: bool = False,
|
pricing: bool = False,
|
||||||
capabilities: bool = False,
|
capabilities: bool = False,
|
||||||
force_fresh_nous_tier: bool = False,
|
force_fresh_nous_tier: bool = False,
|
||||||
max_models: int = 50,
|
max_models: int | None = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Build the ``{providers, model, provider}`` shape every consumer
|
"""Build the ``{providers, model, provider}`` shape every consumer
|
||||||
needs from a single substrate call.
|
needs from a single substrate call.
|
||||||
|
|||||||
@ -1188,7 +1188,6 @@ def prewarm_picker_cache_async() -> Optional["_threading.Thread"]:
|
|||||||
current_model=ctx.current_model,
|
current_model=ctx.current_model,
|
||||||
user_providers=ctx.user_providers,
|
user_providers=ctx.user_providers,
|
||||||
custom_providers=ctx.custom_providers,
|
custom_providers=ctx.custom_providers,
|
||||||
max_models=50,
|
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Best-effort warmup — never surface errors into the session.
|
# Best-effort warmup — never surface errors into the session.
|
||||||
@ -1206,7 +1205,7 @@ def list_authenticated_providers(
|
|||||||
custom_providers: list | None = None,
|
custom_providers: list | None = None,
|
||||||
*,
|
*,
|
||||||
force_fresh_nous_tier: bool = False,
|
force_fresh_nous_tier: bool = False,
|
||||||
max_models: int = 8,
|
max_models: int | None = None,
|
||||||
current_model: str = "",
|
current_model: str = "",
|
||||||
) -> List[dict]:
|
) -> List[dict]:
|
||||||
"""Detect which providers have credentials and list their curated models.
|
"""Detect which providers have credentials and list their curated models.
|
||||||
@ -1426,7 +1425,7 @@ def list_authenticated_providers(
|
|||||||
if hermes_id in _MODELS_DEV_PREFERRED:
|
if hermes_id in _MODELS_DEV_PREFERRED:
|
||||||
model_ids = _merge_with_models_dev(hermes_id, model_ids)
|
model_ids = _merge_with_models_dev(hermes_id, model_ids)
|
||||||
total = len(model_ids)
|
total = len(model_ids)
|
||||||
top = model_ids[:max_models]
|
top = model_ids[:max_models] if max_models else model_ids
|
||||||
|
|
||||||
slug = hermes_id
|
slug = hermes_id
|
||||||
pinfo = _mdev_pinfo(mdev_id)
|
pinfo = _mdev_pinfo(mdev_id)
|
||||||
@ -1589,7 +1588,7 @@ def list_authenticated_providers(
|
|||||||
if hermes_slug in _MODELS_DEV_PREFERRED:
|
if hermes_slug in _MODELS_DEV_PREFERRED:
|
||||||
model_ids = _merge_with_models_dev(hermes_slug, model_ids)
|
model_ids = _merge_with_models_dev(hermes_slug, model_ids)
|
||||||
total = len(model_ids)
|
total = len(model_ids)
|
||||||
top = model_ids[:max_models]
|
top = model_ids[:max_models] if max_models else model_ids
|
||||||
|
|
||||||
results.append({
|
results.append({
|
||||||
"slug": hermes_slug,
|
"slug": hermes_slug,
|
||||||
@ -1664,7 +1663,7 @@ def list_authenticated_providers(
|
|||||||
if not _cp_model_ids:
|
if not _cp_model_ids:
|
||||||
_cp_model_ids = curated.get(_cp.slug, [])
|
_cp_model_ids = curated.get(_cp.slug, [])
|
||||||
_cp_total = len(_cp_model_ids)
|
_cp_total = len(_cp_model_ids)
|
||||||
_cp_top = _cp_model_ids[:max_models]
|
_cp_top = _cp_model_ids[:max_models] if max_models else _cp_model_ids
|
||||||
|
|
||||||
results.append({
|
results.append({
|
||||||
"slug": _cp.slug,
|
"slug": _cp.slug,
|
||||||
@ -2040,7 +2039,7 @@ def list_picker_providers(
|
|||||||
current_base_url: str = "",
|
current_base_url: str = "",
|
||||||
user_providers: dict = None,
|
user_providers: dict = None,
|
||||||
custom_providers: list | None = None,
|
custom_providers: list | None = None,
|
||||||
max_models: int = 8,
|
max_models: int | None = None,
|
||||||
current_model: str = "",
|
current_model: str = "",
|
||||||
) -> List[dict]:
|
) -> List[dict]:
|
||||||
"""Interactive-picker variant of :func:`list_authenticated_providers`.
|
"""Interactive-picker variant of :func:`list_authenticated_providers`.
|
||||||
@ -2083,7 +2082,7 @@ def list_picker_providers(
|
|||||||
except Exception:
|
except Exception:
|
||||||
live_ids = list(p.get("models", []))
|
live_ids = list(p.get("models", []))
|
||||||
p = dict(p)
|
p = dict(p)
|
||||||
p["models"] = live_ids[:max_models]
|
p["models"] = live_ids[:max_models] if max_models else live_ids
|
||||||
p["total_models"] = len(live_ids)
|
p["total_models"] = len(live_ids)
|
||||||
|
|
||||||
has_models = bool(p.get("models"))
|
has_models = bool(p.get("models"))
|
||||||
|
|||||||
@ -3323,7 +3323,6 @@ def get_model_options(profile: Optional[str] = None):
|
|||||||
with _profile_scope(profile):
|
with _profile_scope(profile):
|
||||||
return build_models_payload(
|
return build_models_payload(
|
||||||
load_picker_context(),
|
load_picker_context(),
|
||||||
max_models=50,
|
|
||||||
include_unconfigured=True,
|
include_unconfigured=True,
|
||||||
picker_hints=True,
|
picker_hints=True,
|
||||||
canonical_order=True,
|
canonical_order=True,
|
||||||
@ -3398,7 +3397,7 @@ def get_recommended_default_model(provider: str = ""):
|
|||||||
try:
|
try:
|
||||||
from hermes_cli.inventory import build_models_payload, load_picker_context
|
from hermes_cli.inventory import build_models_payload, load_picker_context
|
||||||
|
|
||||||
payload = build_models_payload(load_picker_context(), max_models=50)
|
payload = build_models_payload(load_picker_context())
|
||||||
for row in payload.get("providers", []):
|
for row in payload.get("providers", []):
|
||||||
if str(row.get("slug", "")).lower() == slug:
|
if str(row.get("slug", "")).lower() == slug:
|
||||||
models = row.get("models") or []
|
models = row.get("models") or []
|
||||||
|
|||||||
@ -660,3 +660,31 @@ def test_two_custom_providers_with_overlap_both_survive():
|
|||||||
assert a_row["total_models"] == 2
|
assert a_row["total_models"] == 2
|
||||||
assert b_row["total_models"] == 2
|
assert b_row["total_models"] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_models_payload_no_max_models_returns_full_list():
|
||||||
|
"""When max_models is not passed (None), build_models_payload must
|
||||||
|
return the full model list — not truncate to the old default of 50.
|
||||||
|
Regression for #48279: Kilo Gateway picker was capped at 50 of 336
|
||||||
|
models, making most models undiscoverable via search."""
|
||||||
|
full_models = [f"model-{i}" for i in range(100)]
|
||||||
|
rows = [
|
||||||
|
{
|
||||||
|
"slug": "kilocode",
|
||||||
|
"name": "Kilo Code",
|
||||||
|
"models": full_models,
|
||||||
|
"total_models": len(full_models),
|
||||||
|
"is_current": False,
|
||||||
|
"is_user_defined": False,
|
||||||
|
"source": "built-in",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
ctx = _empty_ctx()
|
||||||
|
with _list_auth_returning(rows):
|
||||||
|
# No max_models argument — should return all 100 models
|
||||||
|
payload = build_models_payload(ctx)
|
||||||
|
|
||||||
|
kilo_row = next(r for r in payload["providers"] if r["slug"] == "kilocode")
|
||||||
|
assert kilo_row["models"] == full_models
|
||||||
|
assert kilo_row["total_models"] == 100
|
||||||
|
assert len(kilo_row["models"]) == 100
|
||||||
|
|
||||||
|
|||||||
@ -9496,7 +9496,6 @@ def _(rid, params: dict) -> dict:
|
|||||||
canonical_order=True,
|
canonical_order=True,
|
||||||
pricing=True,
|
pricing=True,
|
||||||
capabilities=True,
|
capabilities=True,
|
||||||
max_models=50,
|
|
||||||
)
|
)
|
||||||
return _ok(rid, payload)
|
return _ok(rid, payload)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user