mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
Merge branch 'main' of github.com:NousResearch/hermes-agent into feat/ink-refactor
This commit is contained in:
commit
7e4dd6ea02
220 changed files with 23482 additions and 1959 deletions
|
|
@ -21,6 +21,7 @@ OpenRouter variant suffixes (``:free``, ``:extended``, ``:fast``).
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
|
|
@ -57,10 +58,36 @@ _HERMES_MODEL_WARNING = (
|
|||
"(Claude, GPT, Gemini, DeepSeek, etc.)."
|
||||
)
|
||||
|
||||
# Match only the real Nous Research Hermes 3 / Hermes 4 chat families.
|
||||
# The previous substring check (`"hermes" in name.lower()`) false-positived on
|
||||
# unrelated local Modelfiles like ``hermes-brain:qwen3-14b-ctx16k`` that just
|
||||
# happen to carry "hermes" in their tag but are fully tool-capable.
|
||||
#
|
||||
# Positive examples the regex must match:
|
||||
# NousResearch/Hermes-3-Llama-3.1-70B, hermes-4-405b, openrouter/hermes3:70b
|
||||
# Negative examples it must NOT match:
|
||||
# hermes-brain:qwen3-14b-ctx16k, qwen3:14b, claude-opus-4-6
|
||||
_NOUS_HERMES_NON_AGENTIC_RE = re.compile(
|
||||
r"(?:^|[/:])hermes[-_ ]?[34](?:[-_.:]|$)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def is_nous_hermes_non_agentic(model_name: str) -> bool:
|
||||
"""Return True if *model_name* is a real Nous Hermes 3/4 chat model.
|
||||
|
||||
Used to decide whether to surface the non-agentic warning at startup.
|
||||
Callers in :mod:`cli.py` and here should go through this single helper
|
||||
so the two sites don't drift.
|
||||
"""
|
||||
if not model_name:
|
||||
return False
|
||||
return bool(_NOUS_HERMES_NON_AGENTIC_RE.search(model_name))
|
||||
|
||||
|
||||
def _check_hermes_model_warning(model_name: str) -> str:
|
||||
"""Return a warning string if *model_name* looks like a Hermes LLM model."""
|
||||
if "hermes" in model_name.lower():
|
||||
"""Return a warning string if *model_name* is a Nous Hermes 3/4 chat model."""
|
||||
if is_nous_hermes_non_agentic(model_name):
|
||||
return _HERMES_MODEL_WARNING
|
||||
return ""
|
||||
|
||||
|
|
@ -908,6 +935,65 @@ def list_authenticated_providers(
|
|||
seen_slugs.add(pid)
|
||||
seen_slugs.add(hermes_slug)
|
||||
|
||||
# --- 2b. Cross-check canonical provider list ---
|
||||
# Catches providers that are in CANONICAL_PROVIDERS but weren't found
|
||||
# in PROVIDER_TO_MODELS_DEV or HERMES_OVERLAYS (keeps /model in sync
|
||||
# with `hermes model`).
|
||||
try:
|
||||
from hermes_cli.models import CANONICAL_PROVIDERS as _canon_provs
|
||||
except ImportError:
|
||||
_canon_provs = []
|
||||
|
||||
for _cp in _canon_provs:
|
||||
if _cp.slug in seen_slugs:
|
||||
continue
|
||||
|
||||
# Check credentials via PROVIDER_REGISTRY (auth.py)
|
||||
_cp_config = _auth_registry.get(_cp.slug)
|
||||
_cp_has_creds = False
|
||||
if _cp_config and _cp_config.api_key_env_vars:
|
||||
_cp_has_creds = any(os.environ.get(ev) for ev in _cp_config.api_key_env_vars)
|
||||
# Also check auth store and credential pool
|
||||
if not _cp_has_creds:
|
||||
try:
|
||||
from hermes_cli.auth import _load_auth_store
|
||||
_cp_store = _load_auth_store()
|
||||
_cp_providers_store = _cp_store.get("providers", {})
|
||||
_cp_pool_store = _cp_store.get("credential_pool", {})
|
||||
if _cp_store and (
|
||||
_cp.slug in _cp_providers_store
|
||||
or _cp.slug in _cp_pool_store
|
||||
):
|
||||
_cp_has_creds = True
|
||||
except Exception:
|
||||
pass
|
||||
if not _cp_has_creds:
|
||||
try:
|
||||
from agent.credential_pool import load_pool
|
||||
_cp_pool = load_pool(_cp.slug)
|
||||
if _cp_pool.has_credentials():
|
||||
_cp_has_creds = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not _cp_has_creds:
|
||||
continue
|
||||
|
||||
_cp_model_ids = curated.get(_cp.slug, [])
|
||||
_cp_total = len(_cp_model_ids)
|
||||
_cp_top = _cp_model_ids[:max_models]
|
||||
|
||||
results.append({
|
||||
"slug": _cp.slug,
|
||||
"name": _cp.label,
|
||||
"is_current": _cp.slug == current_provider,
|
||||
"is_user_defined": False,
|
||||
"models": _cp_top,
|
||||
"total_models": _cp_total,
|
||||
"source": "canonical",
|
||||
})
|
||||
seen_slugs.add(_cp.slug)
|
||||
|
||||
# --- 3. User-defined endpoints from config ---
|
||||
if user_providers and isinstance(user_providers, dict):
|
||||
for ep_name, ep_cfg in user_providers.items():
|
||||
|
|
@ -917,9 +1003,16 @@ def list_authenticated_providers(
|
|||
api_url = ep_cfg.get("api", "") or ep_cfg.get("url", "") or ""
|
||||
default_model = ep_cfg.get("default_model", "")
|
||||
|
||||
# Build models list from both default_model and full models array
|
||||
models_list = []
|
||||
if default_model:
|
||||
models_list.append(default_model)
|
||||
# Also include the full models list from config
|
||||
cfg_models = ep_cfg.get("models", [])
|
||||
if isinstance(cfg_models, list):
|
||||
for m in cfg_models:
|
||||
if m and m not in models_list:
|
||||
models_list.append(m)
|
||||
|
||||
# Try to probe /v1/models if URL is set (but don't block on it)
|
||||
# For now just show what we know from config
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue