Files
hermes-agent/agent/provider_base.py
Teknium 8dcb2b6ada refactor(agent/providers): shared ProviderBase/CatalogProviderBase and provider_media; compact contract docs
- provider_base.py: ProviderBase (name/display_name/get_setup_schema) and
  CatalogProviderBase (default_model/list_models/is_available) replace the
  identical default-method bodies duplicated across 7 provider ABCs
- provider_media.py: one save_b64/save_bytes/save_url/cache_dir implementation
  behind image_gen_provider and video_gen_provider
- memory_manager.py: _each_provider fan-out helper replaces per-hook
  try/except loops; _signature_params/_has_var_kwargs unify signature probes
- image_routing.py: _resolve_inference_value shared by base_url/api_key
  resolution; _dict_or_empty/_clean_str/_custom_provider_entries helpers
- MemoryProvider/ContextEngine/TTS/browser/web/terminal-env ABC docstrings
  compacted to their invariants; method names and signatures unchanged
2026-09-02 13:53:28 -07:00

78 lines
2.8 KiB
Python

"""Shared base classes for the pluggable-backend provider ABCs.
Every tool-provider ABC (browser, TTS, image/video gen, transcription, web
search, terminal env) shares the same identity + ``hermes tools`` picker
surface; the previous per-ABC copies of these defaults were byte-identical.
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"``). Default: a minimal entry
derived from ``display_name`` with no env vars — 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).
Default True. 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": ...}`` plus optional
provider-specific keys). Default: empty (no user-selectable models)."""
return []
def default_model(self) -> Optional[str]:
"""Id of the first catalog entry, or None when the catalog is empty."""
models = self.list_models()
if models:
return models[0].get("id")
return None