initial commit
This commit is contained in:
+146
@@ -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
|
||||
```
|
||||
Reference in New Issue
Block a user