mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-30 11:52:04 +00:00
fix(gateway): show MoA presets in model picker
This commit is contained in:
parent
789f8b7dc2
commit
ed54469d06
4 changed files with 97 additions and 0 deletions
|
|
@ -1206,6 +1206,7 @@ class GatewaySlashCommandsMixin:
|
|||
user_providers=user_provs,
|
||||
custom_providers=custom_provs,
|
||||
max_models=50,
|
||||
include_moa=True,
|
||||
)
|
||||
except Exception:
|
||||
providers = []
|
||||
|
|
|
|||
|
|
@ -2277,6 +2277,39 @@ def list_authenticated_providers(
|
|||
return results
|
||||
|
||||
|
||||
def _prepend_moa_picker_provider(providers: List[dict], current_provider: str = "") -> List[dict]:
|
||||
"""Add the virtual MoA provider row used by interactive model pickers.
|
||||
|
||||
``list_authenticated_providers()`` only returns real/auth-backed providers.
|
||||
The CLI model inventory adds MoA separately so named presets appear next to
|
||||
normal providers; gateway pickers call ``list_picker_providers()`` directly,
|
||||
so they need the same virtual row here.
|
||||
"""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.moa_config import normalize_moa_config
|
||||
|
||||
cfg = normalize_moa_config(load_config().get("moa") or {})
|
||||
models = list(cfg.get("presets", {}).keys())
|
||||
if not models:
|
||||
return providers
|
||||
moa_row = {
|
||||
"slug": "moa",
|
||||
"name": "Mixture of Agents",
|
||||
"is_current": (current_provider or "").lower() == "moa",
|
||||
"is_user_defined": False,
|
||||
"models": models,
|
||||
"total_models": len(models),
|
||||
"source": "virtual",
|
||||
"authenticated": True,
|
||||
"auth_type": "virtual",
|
||||
"warning": "Aggregator acts as the selected model; references provide analysis before each call.",
|
||||
}
|
||||
return [moa_row] + [p for p in providers if str(p.get("slug", "")).lower() != "moa"]
|
||||
except Exception:
|
||||
return providers
|
||||
|
||||
|
||||
def list_picker_providers(
|
||||
current_provider: str = "",
|
||||
current_base_url: str = "",
|
||||
|
|
@ -2284,6 +2317,7 @@ def list_picker_providers(
|
|||
custom_providers: list | None = None,
|
||||
max_models: int | None = None,
|
||||
current_model: str = "",
|
||||
include_moa: bool = False,
|
||||
) -> List[dict]:
|
||||
"""Interactive-picker variant of :func:`list_authenticated_providers`.
|
||||
|
||||
|
|
@ -2314,6 +2348,8 @@ def list_picker_providers(
|
|||
max_models=max_models,
|
||||
current_model=current_model,
|
||||
)
|
||||
if include_moa:
|
||||
providers = _prepend_moa_picker_provider(providers, current_provider=current_provider)
|
||||
|
||||
filtered: List[dict] = []
|
||||
for p in providers:
|
||||
|
|
|
|||
|
|
@ -166,3 +166,29 @@ async def test_picker_path_offloads_list_picker_providers(_isolated_config, monk
|
|||
"list_picker_providers must be dispatched via asyncio.to_thread "
|
||||
"(it was called inline on the event loop instead)"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_picker_path_requests_moa_presets(_isolated_config, monkeypatch):
|
||||
"""Gateway /model pickers must opt into the virtual MoA preset provider."""
|
||||
captured = {}
|
||||
|
||||
def _fake_list_picker_providers(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return [{"slug": "moa", "name": "Mixture of Agents", "is_current": False,
|
||||
"models": ["battle", "smart"], "total_models": 2}]
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.model_switch.list_picker_providers",
|
||||
_fake_list_picker_providers,
|
||||
)
|
||||
|
||||
runner = _make_runner()
|
||||
runner.adapters = {Platform.TELEGRAM: _FakePickerAdapter()}
|
||||
monkeypatch.setattr(runner, "_thread_metadata_for_source", lambda *a, **k: None, raising=False)
|
||||
monkeypatch.setattr(runner, "_reply_anchor_for_event", lambda *a, **k: None, raising=False)
|
||||
|
||||
result = await runner._handle_model_command(_make_event())
|
||||
|
||||
assert result is None
|
||||
assert captured["include_moa"] is True
|
||||
|
|
|
|||
|
|
@ -109,6 +109,40 @@ def test_non_openrouter_rows_passed_through_unchanged(monkeypatch):
|
|||
assert result[1]["models"] == ["gemini-3-flash-preview"]
|
||||
|
||||
|
||||
def test_include_moa_adds_virtual_provider_with_named_presets(monkeypatch):
|
||||
"""Gateway pickers opt into a virtual MoA provider so presets are tappable."""
|
||||
base = [_make_provider("minimax", models=["MiniMax-M3"])]
|
||||
moa_config = {
|
||||
"moa": {
|
||||
"default_preset": "battle",
|
||||
"presets": {
|
||||
"battle": {"enabled": True},
|
||||
"smart": {"enabled": True},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
monkeypatch.setattr(model_switch, "list_authenticated_providers",
|
||||
lambda **kw: list(base))
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", lambda: moa_config)
|
||||
monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
|
||||
lambda *a, **kw: pytest.fail("should not be called"))
|
||||
|
||||
result = model_switch.list_picker_providers(
|
||||
current_provider="moa",
|
||||
max_models=50,
|
||||
include_moa=True,
|
||||
)
|
||||
|
||||
assert [p["slug"] for p in result] == ["moa", "minimax"]
|
||||
moa = result[0]
|
||||
assert moa["name"] == "Mixture of Agents"
|
||||
assert moa["is_current"] is True
|
||||
assert moa["source"] == "virtual"
|
||||
assert moa["models"] == ["battle", "smart"]
|
||||
assert moa["total_models"] == 2
|
||||
|
||||
|
||||
def test_empty_models_row_dropped(monkeypatch):
|
||||
"""Built-in provider with an empty ``models`` list is dropped."""
|
||||
base = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue