b4fbd6fe9f
Deploy Site / deploy-vercel (push) Has been skipped
Deploy Site / deploy-docs (push) Has been skipped
Build Skills Index / build-index (push) Has been skipped
Build Skills Index / trigger-deploy (push) Waiting to run
CI / Deny unrelated histories (push) Has been skipped
CI / CI timing report (push) Blocked by required conditions
CI / Detect affected areas (push) Successful in 27m35s
CI / OSV scan (push) Failing after 4s
CI / Build&Test Docker image (push) Successful in 9s
CI / Supply-chain scan (push) Has been skipped
CI / Lint Docker scripts (push) Failing after 5m13s
CI / Check contributors (push) Failing after 12m8s
CI / Docs Site (push) Failing after 12m8s
CI / TypeScript (push) Failing after 12m8s
CI / Python lints (push) Failing after 12m9s
CI / Python tests (push) Failing after 12m9s
CI / Check uv.lock (push) Failing after 23m22s
CI / All required checks pass (push) Waiting to run
136 lines
5.5 KiB
Python
136 lines
5.5 KiB
Python
"""CLI helpers for configuring Mixture of Agents."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from hermes_cli.config import load_config, save_config
|
|
from hermes_cli.inventory import build_models_payload, load_picker_context
|
|
from hermes_cli.moa_config import DEFAULT_MOA_PRESET_NAME, normalize_moa_config
|
|
|
|
|
|
def _prompt_choice(title: str, rows: list[str], default: int = 0) -> int:
|
|
try:
|
|
from hermes_cli.curses_ui import curses_radiolist
|
|
|
|
return curses_radiolist(title, rows, selected=default, cancel_returns=default)
|
|
except Exception:
|
|
for idx, row in enumerate(rows, start=1):
|
|
print(f"{idx}. {row}")
|
|
raw = input(f"{title} [{default + 1}]: ").strip()
|
|
if not raw:
|
|
return default
|
|
try:
|
|
return max(0, min(len(rows) - 1, int(raw) - 1))
|
|
except ValueError:
|
|
return default
|
|
|
|
|
|
def _model_options() -> list[dict[str, Any]]:
|
|
payload = build_models_payload(
|
|
load_picker_context(),
|
|
include_unconfigured=True,
|
|
picker_hints=True,
|
|
canonical_order=True,
|
|
pricing=True,
|
|
capabilities=True,
|
|
max_models=200,
|
|
)
|
|
providers = payload.get("providers") or []
|
|
return [p for p in providers if p.get("slug") and p.get("models")]
|
|
|
|
|
|
def _pick_slot(current: dict[str, str] | None = None) -> dict[str, str]:
|
|
providers = _model_options()
|
|
if not providers:
|
|
raise RuntimeError("No configured model providers found. Run `hermes model` first.")
|
|
current_provider = (current or {}).get("provider", "")
|
|
provider_default = next(
|
|
(idx for idx, p in enumerate(providers) if p.get("slug") == current_provider),
|
|
0,
|
|
)
|
|
provider_rows = [f"{p.get('name') or p.get('slug')} ({p.get('slug')})" for p in providers]
|
|
provider = providers[_prompt_choice("Select provider", provider_rows, provider_default)]
|
|
models = list(provider.get("models") or [])
|
|
if not models:
|
|
raise RuntimeError(f"Provider {provider.get('slug')} has no selectable models")
|
|
current_model = (current or {}).get("model", "")
|
|
model_default = models.index(current_model) if current_model in models else 0
|
|
model = models[_prompt_choice(f"Select model for {provider.get('slug')}", models, model_default)]
|
|
return {"provider": str(provider.get("slug") or ""), "model": str(model)}
|
|
|
|
|
|
def _print_config(config: dict[str, Any]) -> None:
|
|
cfg = normalize_moa_config(config.get("moa") if isinstance(config, dict) else {})
|
|
print("Mixture of Agents presets")
|
|
print(f"Default: {cfg['default_preset']}")
|
|
active = cfg.get("active_preset") or "(off)"
|
|
print(f"Active in config: {active}")
|
|
for name, preset in cfg["presets"].items():
|
|
marker = "*" if name == cfg["default_preset"] else " "
|
|
print(f"\n{marker} {name}")
|
|
print(" Reference models:")
|
|
for idx, slot in enumerate(preset["reference_models"], start=1):
|
|
print(f" {idx}. {slot['provider']}:{slot['model']}")
|
|
agg = preset["aggregator"]
|
|
print(f" Aggregator: {agg['provider']}:{agg['model']}")
|
|
|
|
|
|
def cmd_moa(args) -> None:
|
|
"""Manage Mixture of Agents model presets."""
|
|
cfg = load_config()
|
|
sub = getattr(args, "moa_command", None) or "list"
|
|
|
|
if sub in {"list", "ls"}:
|
|
_print_config(cfg)
|
|
return
|
|
|
|
if sub in {"config", "configure"}:
|
|
moa = normalize_moa_config(cfg.get("moa") if isinstance(cfg, dict) else {})
|
|
preset_name = (getattr(args, "name", None) or moa.get("default_preset") or DEFAULT_MOA_PRESET_NAME).strip()
|
|
current = moa["presets"].get(preset_name, moa["presets"][moa["default_preset"]])
|
|
print(f"Configure MoA preset: {preset_name}")
|
|
print("Pick at least one reference model; choose Done when finished.")
|
|
refs: list[dict[str, str]] = []
|
|
existing = list(current.get("reference_models") or [])
|
|
idx = 0
|
|
while True:
|
|
base = existing[idx] if idx < len(existing) else None
|
|
refs.append(_pick_slot(base))
|
|
idx += 1
|
|
choice = _prompt_choice("Add another reference model?", ["Add another", "Done"], 1)
|
|
if choice == 1:
|
|
break
|
|
print("Configure aggregator model.")
|
|
current = dict(current)
|
|
current["reference_models"] = refs
|
|
current["aggregator"] = _pick_slot(current.get("aggregator"))
|
|
moa["presets"][preset_name] = current
|
|
moa.setdefault("default_preset", preset_name)
|
|
cfg["moa"] = normalize_moa_config(moa)
|
|
save_config(cfg)
|
|
print(f"Saved MoA preset: {preset_name}")
|
|
_print_config(cfg)
|
|
return
|
|
|
|
if sub == "delete":
|
|
moa = normalize_moa_config(cfg.get("moa") if isinstance(cfg, dict) else {})
|
|
preset_name = (getattr(args, "name", None) or "").strip()
|
|
if not preset_name:
|
|
raise SystemExit("Usage: hermes moa delete <name>")
|
|
if preset_name not in moa["presets"]:
|
|
raise SystemExit(f"Unknown MoA preset: {preset_name}")
|
|
if len(moa["presets"]) <= 1:
|
|
raise SystemExit("Cannot delete the only MoA preset")
|
|
del moa["presets"][preset_name]
|
|
if moa["default_preset"] == preset_name:
|
|
moa["default_preset"] = next(iter(moa["presets"]))
|
|
if moa.get("active_preset") == preset_name:
|
|
moa["active_preset"] = ""
|
|
cfg["moa"] = normalize_moa_config(moa)
|
|
save_config(cfg)
|
|
print(f"Deleted MoA preset: {preset_name}")
|
|
return
|
|
|
|
raise SystemExit(f"Unknown moa subcommand: {sub}")
|