Files
hermes-agent/agent/provider_base.py

67 lines
2.4 KiB
Python

"""Shared base classes for the pluggable-backend provider ABCs.
Every tool-provider ABC shares the same identity + ``hermes tools`` picker surface.
Concrete ABCs subclass :class:`ProviderBase` (or :class:`CatalogProviderBase` when
the backend also exposes a model catalog and is available by default) and add only
their domain methods. Plugins keep subclassing the concrete ABC, so ``isinstance``
checks and abstract-method sets are unchanged.
"""
from __future__ import annotations
import abc
from typing import Any, Dict, List, Optional
class ProviderBase(abc.ABC):
"""Identity + picker metadata common to every provider ABC."""
@property
@abc.abstractmethod
def name(self) -> str:
"""Stable short identifier used as the provider's config-key value.
Lowercase, no spaces (hyphens allowed where they preserve an existing
user-visible name). Registries key providers by this string.
"""
@property
def display_name(self) -> str:
"""Human-readable label shown in ``hermes tools``. Defaults to ``name``."""
return self.name
def get_setup_schema(self) -> Dict[str, Any]:
"""Provider row for the ``hermes tools`` picker.
Shape: ``{"name", "badge", "tag", "env_vars": [{"key", "prompt", "url"}, ...]}``
(browser providers may add ``"post_setup"``). Override to expose API key
prompts and badges.
"""
return {"name": self.display_name, "badge": "", "tag": "", "env_vars": []}
class CatalogProviderBase(ProviderBase):
"""Provider with a model catalog; available by default, ``display_name`` is title-cased."""
@property
def display_name(self) -> str:
"""Human-readable label shown in ``hermes tools``. Defaults to ``name.title()``."""
return self.name.title()
def is_available(self) -> bool:
"""True when this provider can service calls (API key present, SDK importable).
Must NOT raise and must NOT make network calls — the picker and
``hermes setup`` call it on every paint.
"""
return True
def list_models(self) -> List[Dict[str, Any]]:
"""Model catalog entries (``{"id": ..., "display": ...}`` + provider-specific keys)."""
return []
def default_model(self) -> Optional[str]:
"""Id of the first catalog entry, or None when the catalog is empty."""
models = self.list_models()
return models[0].get("id") if models else None