Add archived/hermes-api-server-setup
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: hermes-api-server-setup
|
||||
description: "Set up Hermes Agent built-in OpenAI-compatible API server on a custom port with external access. Includes the critical extra nesting pitfall."
|
||||
version: 1.0
|
||||
category: devops
|
||||
---
|
||||
|
||||
# Hermes API Server Setup
|
||||
|
||||
Hermes has a built-in OpenAI-compatible API server (`gateway/platforms/api_server.py`) that exposes endpoints like `/v1/chat/completions`, `/v1/responses`, `/v1/models`, `/health`, etc. Any OpenAI-compatible frontend (Open WebUI, LobeChat, LibreChat, ChatBox, etc.) can connect.
|
||||
|
||||
## Critical Pitfall - extra Nesting
|
||||
|
||||
**Host, port, key, cors_origins MUST be nested under `extra:` in config.yaml.** If placed directly under the platform name, `PlatformConfig.from_dict()` silently ignores them (it only reads `enabled`, `token`, `api_key`, `home_channel`, `reply_to_mode`, `extra`).
|
||||
|
||||
WRONG - binds to 127.0.0.1 only (silently ignores host/port):
|
||||
```yaml
|
||||
platforms:
|
||||
api_server:
|
||||
enabled: true
|
||||
host: "0.0.0.0"
|
||||
port: 8642
|
||||
key: "my-secret"
|
||||
```
|
||||
|
||||
CORRECT - binds to 0.0.0.0:
|
||||
```yaml
|
||||
platforms:
|
||||
api_server:
|
||||
enabled: true
|
||||
extra:
|
||||
host: "0.0.0.0"
|
||||
port: 8642
|
||||
key: "my-secret"
|
||||
cors_origins:
|
||||
- "*"
|
||||
```
|
||||
|
||||
## Defaults
|
||||
|
||||
- `DEFAULT_HOST`: `127.0.0.1` (localhost only - NOT externally accessible)
|
||||
- `DEFAULT_PORT`: `8642`
|
||||
|
||||
## Setup Steps
|
||||
|
||||
1. Add the `platforms:` block at the TOP LEVEL of `~/.hermes/config.yaml` (not nested under `display:` or `agent:`).
|
||||
|
||||
2. Ensure host is `0.0.0.0` for external access, set a `key` for API auth, and `cors_origins: ["*"]` for browser clients.
|
||||
|
||||
3. Restart the gateway:
|
||||
```bash
|
||||
systemctl --user restart hermes-gateway.service
|
||||
```
|
||||
|
||||
4. Verify:
|
||||
```bash
|
||||
# Check port is listening on 0.0.0.0
|
||||
ss -tlnp | grep 8642
|
||||
# Health check
|
||||
curl http://localhost:8642/health
|
||||
# Model list (auth required)
|
||||
curl http://localhost:8642/v1/models -H "Authorization: Bearer YOUR_KEY"
|
||||
```
|
||||
|
||||
5. If `ss` shows `127.0.0.1:8642` instead of `0.0.0.0:8642`, the `extra:` nesting is wrong - recheck step 1.
|
||||
|
||||
## Client Connection
|
||||
|
||||
| Field | Value |
|
||||
|------------|----------------------------------|
|
||||
| Base URL | `http://YOUR_HOST:8642/v1` |
|
||||
| API Key | Value of `extra.key` in config |
|
||||
| Model ID | `hermes-agent` (or active profile name) |
|
||||
|
||||
## Available Endpoints
|
||||
|
||||
- `GET /health` - Health check
|
||||
- `GET /v1/models` - Model list
|
||||
- `POST /v1/chat/completions` - OpenAI Chat Completions (SSE streaming supported)
|
||||
- `POST /v1/responses` - OpenAI Responses API (stateful)
|
||||
- `GET /v1/responses/{id}` - Retrieve stored response
|
||||
- `DELETE /v1/responses/{id}` - Delete stored response
|
||||
- `POST /v1/runs` - Async run (returns 202 + run_id)
|
||||
- `GET /v1/runs/{id}/events` - SSE stream of run lifecycle events
|
||||
|
||||
### Session Management Endpoints
|
||||
|
||||
- `GET /v1/sessions` - List sessions (query: `limit`, `offset`, `source`, `include_children`, `order_by_last_active`)
|
||||
- `DELETE /v1/sessions/{session_id}` - Delete a session and its messages
|
||||
- `POST /v1/sessions/prune` - Delete ended sessions older than N days (body: `older_than_days`, `source`)
|
||||
- `GET /v1/capabilities` - Advertises all endpoints including sessions (third-party UIs use this for discovery)
|
||||
|
||||
Session IDs are validated (hex + underscore/dash only) to prevent path traversal. All session endpoints require the same `Authorization: Bearer KEY` as other endpoints.
|
||||
|
||||
## Troubleshooting: API Server Unresponsive
|
||||
|
||||
If the API server returns connection refused or times out:
|
||||
|
||||
1. **Check if gateway service is running:**
|
||||
```bash
|
||||
systemctl --user status hermes-gateway.service
|
||||
```
|
||||
|
||||
2. **Check if port is listening:**
|
||||
```bash
|
||||
ss -tlnp | grep 8642
|
||||
```
|
||||
No output = gateway not running or API Server not started.
|
||||
|
||||
3. **Check gateway logs for startup errors or SIGTERM:**
|
||||
```bash
|
||||
cat ~/.hermes/logs/gateway.log
|
||||
```
|
||||
Look for `Received SIGTERM` (systemd or user killed it) or `api_server connected` (started OK).
|
||||
|
||||
4. **Restart gateway:**
|
||||
```bash
|
||||
systemctl --user restart hermes-gateway.service
|
||||
# or: hermes gateway start
|
||||
```
|
||||
|
||||
5. **Verify after restart:**
|
||||
```bash
|
||||
sleep 5 && curl -s http://localhost:8642/health
|
||||
```
|
||||
|
||||
Common cause: Gateway received SIGTERM (e.g. from `hermes gateway stop`, system reboot, or OOM). The API Server is NOT a separate process — it runs inside the gateway. If the gateway dies, API Server dies with it.
|
||||
|
||||
## Environment Variable Alternative
|
||||
|
||||
Instead of config.yaml, you can also use env vars (checked at gateway startup):
|
||||
- `API_SERVER_ENABLED=true`
|
||||
- `API_SERVER_HOST=0.0.0.0`
|
||||
- `API_SERVER_PORT=8642`
|
||||
- `API_SERVER_KEY=my-secret`
|
||||
- `API_SERVER_CORS_ORIGINS=*`
|
||||
- `API_SERVER_MODEL_NAME=custom-name`
|
||||
|
||||
These are read in `gateway/config.py` and take effect if `API_SERVER_ENABLED=true` OR `API_SERVER_KEY` is set.
|
||||
Reference in New Issue
Block a user