initial commit

This commit is contained in:
ray
2026-07-12 10:17:17 -04:00
commit dab5a4ebc6
1424 changed files with 330463 additions and 0 deletions
@@ -0,0 +1,104 @@
# Fixing "AIAgent not available" in Hermes Web UI
The Web UI cannot find the Hermes Agent source code and starts in reduced-functionality mode. Symptoms in `docker logs hermes-webui`:
```
!! WARNING: hermes-agent source not found.
!! Looked in: /home/hermeswebui/.hermes/hermes-agent
ImportError: AIAgent not available -- check that hermes-agent is on sys.path
agent dir : NOT FOUND [XX]
```
## Root Cause
The container needs the Hermes Agent Python source at `/home/hermeswebui/.hermes/hermes-agent` (or `/opt/hermes` as fallback). This is used by the init script to:
1. Install dependencies from the agent's `pyproject.toml`
2. Make `AIAgent` importable from `run_agent.py`
When the source is missing, the server starts without any agent features.
## Fix (agent source is on host but not in the container)
If your Hermes home is bind-mounted and the agent source exists on the host but wasn't mounted:
### Option A: Copy source into the mounted data directory (no restart needed)
```bash
# Copy host's hermes-agent source into the directory already mounted to the container
cp -r /path/to/host/.hermes/hermes-agent /path/to/mounted-data/hermes-agent
# Install deps inside the container
docker exec hermes-webui bash -c "source /app/venv/bin/activate && pip install -e /home/hermeswebui/.hermes/hermes-agent"
# Remove the fast-restart marker and restart to force full setup
docker exec hermes-webui rm -f /app/venv/.deps_installed
docker restart hermes-webui
# If permission errors occur (root-owned pycache from prior pip install):
docker exec hermes-webui bash -c "find /app/venv -user root -delete"
docker restart hermes-webui
```
### Option B: Add a proper bind mount (cleaner, Docker Compose)
Add to the webui service in your docker-compose.yml:
```yaml
services:
hermes-webui:
image: ghcr.io/nesquena/hermes-webui:latest
volumes:
- ~/.hermes:/home/hermeswebui/.hermes:ro
- ~/.hermes/hermes-agent:/home/hermeswebui/.hermes/hermes-agent:ro # <-- add this
- /path/to/workspace:/workspace
environment:
- HERMES_WEBUI_PASSWORD=***
ports:
- "8787:8787"
```
Then recreate:
```bash
docker compose up -d
```
## Verifying the fix
```bash
# Check startup logs for success
docker logs hermes-webui | grep "agent dir"
# Expected:
# agent dir : /home/hermeswebui/.hermes/hermes-agent [ok]
# Also confirm no AIAgent import errors:
docker logs hermes-webui | grep -i "AIAgent"
# Should return nothing (no errors)
```
## Known edge cases
### Fast restart skipping agent install
The init script checks for `/app/venv/.deps_installed`. If the agent source was added after the container's first startup (when the marker was created), the init script skips reinstalling and the import still fails even though the source is present.
**Fix:** Remove the marker and restart:
```bash
docker exec hermes-webui rm -f /app/venv/.deps_installed
docker restart hermes-webui
```
### Permission denied during reinstall ("os error 13")
A prior `pip install -e` or manual install running as root leaves root-owned `.pyc` files in `/app/venv/lib/python3.12/site-packages/__pycache__/`. When the init script later runs as a non-root user, it can't remove them.
**Fix:** Clean root files from the venv:
```bash
docker exec hermes-webui bash -c "find /app/venv -user root -delete"
docker restart hermes-webui
```
@@ -0,0 +1,9 @@
# Firecrawl + Reddit Limitations
Firecrawl (firecrawl.dev, `firecrawl-py` SDK) explicitly blocks Reddit:
```
HTTP 403: "We apologize for the inconvenience but we do not support this site."
```
Applies to all Reddit URLs. See `devops/hermes-dashboard/references/firecrawl-reddit-limitations.md` for full details including the WSB RSS workaround.
@@ -0,0 +1,146 @@
# Provider Credentials & Session State Diagnosis
Diagnosis flow for HTTP 401 errors and "No LLM provider configured" when the web UI has the agent source installed (agent dir shows `[ok]` in logs).
## Diagnosis Flow
### Step 1 — Check what provider the web UI is actually trying to use
```bash
docker exec hermes-webui cat /home/hermeswebui/.hermes/config.yaml
```
The `model.provider` field is the provider the web UI will attempt to use. If this says `openrouter` but your API keys are for `deepseek`, that's the problem.
### Step 2 — Check that the .env has the right API key
```bash
docker exec hermes-webui cat /home/hermeswebui/.hermes/.env
```
The env var name must match what the provider expects. Common mappings:
| provider in config.yaml | env var |
|---|---|
| `deepseek` | `DEEPSEEK_API_KEY` |
| `openrouter` | `OPENROUTER_API_KEY` |
| `anthropic` | `ANTHROPIC_API_KEY` |
| `openai` / `openai-api` | `OPENAI_API_KEY` |
| `gemini` | `GOOGLE_API_KEY` or `GEMINI_API_KEY` |
### Step 3 — Check if the error is from a stale cached session
The web UI caches session metadata (model, provider, workspace) on disk. When you switch providers, old sessions keep the old provider.
```bash
# Find the most recent session file
ls -t /home/hermeswebui/.hermes/webui/sessions/*.json 2>/dev/null | head -3
# Check its model/provider
python3 -c "
import json, glob
for f in sorted(glob.glob('/home/hermeswebui/.hermes/webui/sessions/*.json')):
with open(f) as fh:
d = json.load(fh)
print(f'{f}: model={d.get(\"model\")} provider={d.get(\"model_provider\")}')
"
```
Expected: `model=deepseek-v4-flash provider=deepseek` (or whatever your current provider is).
If the session shows a different provider, update it:
```bash
python3 -c "
import json, glob
for f in glob.glob('/home/hermeswebui/.hermes/webui/sessions/*.json'):
with open(f) as fh:
d = json.load(fh)
d['model'] = 'deepseek-v4-flash' # <-- replace with your model
d['model_provider'] = 'deepseek' # <-- replace with your provider
with open(f, 'w') as fh:
json.dump(d, fh)
"
```
### Step 4 — Check settings.json
The web UI stores `default_model_provider` in settings.json:
```bash
python3 -c "
import json
with open('/home/hermeswebui/.hermes/webui/settings.json') as f:
d = json.load(f)
print('default_model_provider:', d.get('default_model_provider'))
"
```
If this doesn't match your current provider:
```bash
python3 -c "
import json
with open('/home/hermeswebui/.hermes/webui/settings.json') as f:
d = json.load(f)
d['default_model_provider'] = 'deepseek' # <-- replace with your provider
with open('/home/hermeswebui/.hermes/webui/settings.json', 'w') as f:
json.dump(d, f, indent=2)
"
```
### Step 5 — Verify provider resolution works
Test what the web UI resolves at runtime:
```bash
python3 -c "
import os
os.environ['HERMES_HOME'] = '/home/hermeswebui/.hermes'
from api.config import get_config, resolve_model_provider, model_with_provider_context
cfg = get_config()
print('Config model section:', cfg.get('model', {}))
# Simulate sending a message without specifying a model
model_with_ctx = model_with_provider_context('')
resolved = resolve_model_provider(model_with_ctx)
print('Resolved (empty model):', resolved)
# Simulate sending a message with your default model
model_with_ctx = model_with_provider_context('deepseek-v4-flash')
resolved = resolve_model_provider(model_with_ctx)
print('Resolved (default model):', resolved)
# Check runtime provider can find the API key
from api.oauth import resolve_runtime_provider_with_anthropic_env_lock
from hermes_cli.runtime_provider import resolve_runtime_provider
_rt = resolve_runtime_provider_with_anthropic_env_lock(
resolve_runtime_provider,
requested=resolved[1], # the resolved provider name
)
print('API key found:', bool(_rt.get('api_key')))
"
```
If the key is found and provider/base_url are correct, the config is right and the issue is in the session cache.
## Common Patterns
### Pattern: OpenRouter → DeepSeek switch
After switching from OpenRouter to DeepSeek, three things need updating:
1. `config.yaml` — set `model.provider: deepseek`, `model.default: deepseek-v4-flash`, `model.base_url: https://api.deepseek.com/v1`
2. `.env` — set `DEEPSEEK_API_KEY=<your-key>`
3. Session cache — update session JSON files to use `model_provider: deepseek` instead of `openrouter`
4. `settings.json` — update `default_model_provider` to `deepseek`
### Pattern: Copying host config to web UI
The host's `~/.hermes/` is NOT mounted into the web UI container. The web UI uses whatever is in the bind-mounted data directory (e.g. `/home/ray/docker/hermes/data/`). To sync:
```bash
cp ~/.hermes/config.yaml /path/to/webui/data/config.yaml
grep -E '^(DEEPSEEK|OPENROUTER|ANTHROPIC|OPENAI|GEMINI|GOOGLE)_API_KEY' ~/.hermes/.env >> /path/to/webui/data/.env
```