Hermes Agent API Client
Hermes Agent API Client is a typed async Python client for authenticated capability discovery and streaming Chat Completions from the Hermes Agent API Server. The package requires Python 3.13 or later.
Installation
Install the package from PyPI:
uv add hermes-agent-api-client
Usage
HermesAgentApiClient is a single-use async context manager. Without an
injected HTTP client, it creates and closes its own httpx.AsyncClient:
from hermes_agent_api_client import HermesAgentApiClient, TerminalEvent
async def ask_hermes(request: dict[str, object]) -> None:
async with HermesAgentApiClient(
"https://hermes.example",
"bearer-key",
) as client:
capabilities = await client.probe_capabilities()
assert capabilities.chat_completions_streaming
async for event in client.stream_chat_events(
request,
session_id="profile.chat-001",
session_key="home-assistant-instance-001",
):
if isinstance(event, TerminalEvent):
print(event.outcome)
The default verify=None uses httpx's verified TLS default. An owned client may
instead receive verify=False or an ssl.SSLContext. Disabling certificate
verification weakens transport security and should be limited to controlled
environments.
To share a caller-managed client, inject it explicitly:
import httpx
from hermes_agent_api_client import HermesAgentApiClient
async def probe_with_shared_transport() -> None:
async with httpx.AsyncClient(verify=True) as http_client:
async with HermesAgentApiClient(
"https://hermes.example",
"bearer-key",
http_client=http_client,
) as client:
await client.probe_capabilities()
The caller owns an injected client, so exiting HermesAgentApiClient does not
close it. TLS verification must be configured on the injected client; passing
both http_client and a non-None verify value is rejected.
Capability discovery can distinguish a non-Hermes endpoint from a Hermes Agent that lacks required Chat Completions support using package-root imports only:
from hermes_agent_api_client import (
HermesAgentApiClient,
HermesCapabilityError,
HermesIdentityError,
)
async def discover_hermes(base_url: str, bearer_key: str) -> str:
try:
async with HermesAgentApiClient(base_url, bearer_key) as client:
capabilities = await client.probe_capabilities()
except HermesIdentityError:
return "not a Hermes Agent endpoint"
except HermesCapabilityError:
return "Hermes Agent lacks Chat Completions support"
return capabilities.model
Supported contract
The package supports only these Hermes operations:
GET /v1/capabilitiesthroughprobe_capabilities().- Streaming
POST /v1/chat/completionsthroughstream_chat_events().
The exact OpenAI-compatible request document remains a caller-owned mapping; the client does not define additional request-model semantics.
Each stream accepts independent keyword-only session_id and session_key
values. Non-None values map only to X-Hermes-Session-Id and
X-Hermes-Session-Key; the client never derives identifiers or accepts an
arbitrary headers mapping. Values must be exact built-in strings containing
1 through 256 visible ASCII characters. Session IDs additionally reject path
shapes (.., /, \, or a leading drive-letter prefix). Invalid values fail
locally on the stream's first iteration as a non-retryable
HermesTransportError, before network dispatch.
A successful capability probe returns an immutable HermesCapabilities value
with this supported shape:
object: exactlyhermes.api_server.capabilities.platform: exactlyhermes-agent.model: the advertised API/profile name.auth_type: exactlybearer.auth_required: the literal booleanTrue.chat_completions: the literal booleanTrue.chat_completions_streaming: the literal booleanTrue.
At the immutable upstream compatibility target, model is resolved in order
from an explicit API-server model override, a non-default active profile, or
the hermes-agent fallback. The client preserves the advertised string exactly:
it does not trim, normalize, replace, or truncate leading, trailing, or internal
whitespace. Strict validation requires an actual string containing 1 through
255 Unicode code points and at least one non-whitespace code point; empty,
whitespace-only, non-string, and over-limit values are rejected.
The stream yields immutable AssistantDeltaEvent, ToolProgressEvent,
UsageEvent, KeepaliveEvent, and TerminalEvent values. Terminal outcomes
are success, length, or upstream_error. Tool progress exposes only a
bounded tool_call_id, tool_name, and the closed running/completed
status. Terminal events expose only outcome, partial, and the optional
closed failure reasons output_truncated, agent_error, or unknown;
additional metadata passed when constructing a public TerminalEvent is
rejected. Additive upstream wire fields remain ignored by the private decoder.
Public failures derive from HermesContractError and expose only safe
category, status_code, and retryable metadata. HermesIdentityError and
HermesCapabilityError both inherit HermesProtocolError, so existing callers
that catch HermesProtocolError or HermesContractError continue to catch
them. Both subclasses have protocol category, no status code, and
retryable=False, with no additional instance state.
Capability-probe classification is staged and deterministic:
| Wire condition | Public exception |
|---|---|
| Top-level document is not a mapping | HermesProtocolError |
Missing or non-exact object discriminator |
HermesIdentityError |
Missing or non-exact platform discriminator |
HermesIdentityError |
Valid identity, but features is missing or not a mapping |
HermesCapabilityError |
Valid identity, but features.chat_completions is missing, false, or not the literal boolean True |
HermesCapabilityError |
Invalid or missing model |
HermesProtocolError |
| Invalid or missing bearer authentication contract | HermesProtocolError |
Missing, false, or invalid chat_completions_streaming |
HermesProtocolError |
| Malformed JSON, oversized body, or another unrelated schema failure | HermesProtocolError |
| Existing authentication HTTP status | HermesAuthenticationError |
| Existing non-authentication HTTP status | HermesHttpStatusError |
| Existing request, timeout, read, or cleanup transport failure | HermesTransportError |
Identity failures take precedence over required-feature failures. Capability
failures are classified only after both identity discriminators are valid, and
all remaining wire failures use the generic HermesProtocolError fallback.
Authentication HTTP status, other HTTP status, and transport failures remain
HermesAuthenticationError, HermesHttpStatusError, and
HermesTransportError, respectively. Callers should not expect upstream
response bodies, bearer credentials, request payloads, URLs, or raw validation
details in public error text.
Compatibility targets the immutable Hermes Agent tag v2026.7.7.2 at commit
9de9c25f620ff7f1ce0fd5457d596052d5159596. Evidence is derived from a
capability fixture captured from that tagged handler and local
protocol/transport tests; it is not evidence from a live Hermes server or a
moving upstream branch.
Security: Hermes API access exposes the configured agent toolset. Protect the bearer key, restrict network access, and grant the Hermes agent only the tools appropriate for the calling application.
Development
Install the exact locked development environment and the commit hook:
uv sync --locked --all-groups
prek install
prek runs the complete quality gate automatically before every Git commit and
blocks the commit when any check fails.
License
Licensed under the Universal Permissive License 1.0 (UPL-1.0). See LICENSE.
Metadata
Release files for hermes-agent-api-client 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hermes_agent_api_client-0.3.1.tar.gz | 56.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hermes_agent_api_client-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 79.8 kB
Release files / hermes_agent_api_client-0.3.1.tar.gz
| Download URL | hermes_agent_api_client-0.3.1.tar.gz |
|---|---|
| Size | 56.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
882e919efd1ca0c8819acb34232832d2dbc982faadaeb5431ce24ec5b1a188f1
|
|
BLAKE2b-256 checksum How to use checksums |
b9c29dcc0cdaf10ce42a61b860e0e8e1c6534232f7f03606dda946a3058ae996
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 17, 2026.
Transparency logRelease files / hermes_agent_api_client-0.3.1-py3-none-any.whl
| Download URL | hermes_agent_api_client-0.3.1-py3-none-any.whl |
|---|---|
| Size | 23.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c283811c744bc4da9b68dfbf5462bccc805dd6958b71d6f667faba489b692276
|
|
BLAKE2b-256 checksum How to use checksums |
fd1cffc79d9fb413a653247257b876e40fd270a13a04db644cde0c949bd81e37
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 17, 2026.
Transparency log