mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
Adds 'hermes proxy start' — a local HTTP server that lets external apps (OpenViking, Karakeep, Open WebUI, ...) use a Hermes-managed provider subscription as their LLM endpoint. The proxy attaches the user's real OAuth-resolved credentials to each forwarded request, refreshing them automatically; the client can send any bearer (it gets stripped). Ships with one adapter — Nous Portal. The UpstreamAdapter ABC and registry in hermes_cli/proxy/adapters/ are designed for additional OAuth providers to plug in by name without server changes. Commands: hermes proxy start [--provider nous] [--host 127.0.0.1] [--port 8645] hermes proxy status hermes proxy providers Allowed Portal paths: /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models. Anything else returns 404 with a clear error pointing at the allowed list. aiohttp is gated like gateway/platforms/api_server.py (try-import, clean runtime error if missing). No new core dependency. Tests: 24 unit tests + 1 separate E2E that spawns the real subprocess and verifies the upstream receives the right bearer with the client's header stripped.
35 lines
1.1 KiB
Python
35 lines
1.1 KiB
Python
"""Upstream adapter registry for the local proxy server.
|
|
|
|
Each adapter wraps a provider's OAuth state and exposes a uniform interface
|
|
the proxy server can use to forward requests with a freshly-minted bearer
|
|
token. See :class:`UpstreamAdapter` for the contract.
|
|
"""
|
|
|
|
from typing import Dict, Type
|
|
|
|
from hermes_cli.proxy.adapters.base import UpstreamAdapter
|
|
from hermes_cli.proxy.adapters.nous_portal import NousPortalAdapter
|
|
|
|
# Registry of available adapter classes keyed by provider name as used on
|
|
# the ``hermes proxy start --provider <name>`` CLI flag.
|
|
ADAPTERS: Dict[str, Type[UpstreamAdapter]] = {
|
|
"nous": NousPortalAdapter,
|
|
}
|
|
|
|
|
|
def get_adapter(name: str) -> UpstreamAdapter:
|
|
"""Instantiate an adapter by provider name.
|
|
|
|
Raises:
|
|
ValueError: if ``name`` is not a registered adapter.
|
|
"""
|
|
key = (name or "").strip().lower()
|
|
if key not in ADAPTERS:
|
|
available = ", ".join(sorted(ADAPTERS)) or "(none)"
|
|
raise ValueError(
|
|
f"Unknown proxy upstream provider: {name!r}. Available: {available}"
|
|
)
|
|
return ADAPTERS[key]()
|
|
|
|
|
|
__all__ = ["UpstreamAdapter", "ADAPTERS", "get_adapter"]
|