Files
hermes-skills/hermes-config-internals/SKILL.md
Hermes Agent b0d790be34 Add all 104 active skills from all 16 Hermes profiles
12 unversioned skills now versioned at 1.0.0:
agent-communication, ascii-video, external-reasoning-augmentation,
jotty-notes-api, minecraft-modpack-server, obsidian, pokemon-player,
powerpoint, social-search, songwriting-and-ai-music,
workspace-context-organization, youtube-content

Total repo: 141 skills across all profile scopes
2026-07-04 11:44:04 -05:00

147 lines
8.1 KiB
Markdown

---
name: hermes-config-internals
description: "Hermes Agent config internals — what parameters the agent actually sends to providers vs what users expect. Covers temperature, generation params, provider profiles, request_overrides, and the gap between documented/expected behavior and source-code reality."
version: 1.0.0
author: agent
metadata:
hermes:
tags: [hermes, config, providers, internals, debugging]
---
# Hermes Config Internals
When a user asks whether Hermes supports a config option (temperature, top_p, generation params, etc.), do NOT trust external sources (Grok, ChatGPT, blog posts). Always verify against the source code. Many plausible-sounding config blocks do not exist.
## Core Finding: No `generation:` Config Block
Hermes Agent does **not** have a `generation:` config section. Temperature, top_p, top_k, seed, etc. are not user-facing config knobs for the main agent loop. Any external source claiming otherwise is wrong.
## Temperature — What Actually Happens
### Main agent loop
The `build_kwargs` method in `agent/transports/chat_completions.py` never sets a `temperature` key. For registered providers, `_build_kwargs_from_profile` checks `profile.fixed_temperature` — most profiles (including Ollama Cloud and Custom/Ollama) inherit `None` from `ProviderProfile`, so temperature is omitted entirely. The provider/server uses its own default.
### Auxiliary tasks (hardcoded 0.3)
- Compression summaries: 0.3
- Title generation: 0.3
- One-shot helpers: 0.3
### Model-specific overrides
`_fixed_temperature_for_model()` in `agent/auxiliary_client.py` only overrides for:
- Kimi/Moonshot models → `OMIT_TEMPERATURE` (strip entirely, server-managed)
- Arcee Trinity Thinking → force 0.5
- Everything else → `None` (no opinion)
### Claude Opus 4.7+
`_forbids_sampling_params()` in `agent/anthropic_adapter.py` returns True for Opus 4.7+ — these models 400 on any non-default temperature/top_p/top_k. Hermes strips them.
### MoA (Mixture of Agents)
The only user-configurable temperature in Hermes is for MoA presets:
- `moa.presets.<name>.reference_temperature` (default 0.6)
- `moa.presets.<name>.aggregator_temperature` (default 0.4)
## Provider Profiles — What They Actually Control
### Ollama Cloud (`plugins/model-providers/ollama-cloud/__init__.py`)
- Only handles `reasoning_effort` (maps xhigh→max)
- No temperature, no num_ctx, no options
- `fixed_temperature` is `None` (inherited from `ProviderProfile`)
### Custom/Ollama Local (`plugins/model-providers/custom/__init__.py`)
- `ollama_num_ctx``extra_body.options.num_ctx`
- `reasoning_config` disabled → `extra_body.think = False`
- No temperature
- `fixed_temperature` is `None`
## `request_overrides` — The Injection Point
The transport merges `request_overrides` into `api_kwargs` at the end of `build_kwargs`. If `{"temperature": 0.3}` were in `request_overrides`, it would land as a top-level API parameter. However, `request_overrides` comes from turn routing (gateway/CLI runtime resolution), not from static config.
## Working Method: `extra_body` on `custom_providers`
**This works.** Adding `extra_body` to a `custom_providers` entry injects parameters into the API call. The OpenAI SDK flattens `extra_body` keys into top-level JSON fields in the HTTP request.
### Code path
1. `_custom_provider_request_overrides()` in `hermes_cli/runtime_provider.py` (line 899): reads `custom_provider["extra_body"]` → returns `{"extra_body": {...}}`
2. `_merge_custom_provider_extra_body()` in `agent/agent_init.py` (line 147): merges into `agent.request_overrides["extra_body"]`
3. Transport `_build_kwargs_from_profile()` in `agent/transports/chat_completions.py` (line 564): iterates `request_overrides``extra_body` keys are merged into the `extra_body` dict
4. OpenAI SDK sends `extra_body` keys as top-level JSON fields in the HTTP request body
### Config example
```yaml
custom_providers:
- name: Ollama
base_url: http://localhost:11434/v1
api_key: m
extra_body:
temperature: 0.3
models:
deepseek-v4-pro:cloud: {}
```
### Per-model variant
```yaml
custom_providers:
- name: Ollama
base_url: http://localhost:11434/v1
api_key: m
models:
deepseek-v4-pro:cloud:
extra_body:
temperature: 0.3
glm-5.2:cloud:
extra_body:
temperature: 0.7
```
`_custom_provider_extra_body_for_agent()` (line 95) matches by provider name + base_url, then checks `_custom_provider_model_matches()` for per-model `extra_body`.
### Verified: 2026-07-02
Tested on general profile with DeepSeek V4 Pro via Ollama (localhost:11434/v1). The `extra_body` injection mechanism works — confirmed by temperature=4 causing HTTP 400 (Ollama rejects out-of-range). However, DeepSeek V4 Pro's Thinking Mode explicitly does NOT support temperature. Official DeepSeek API docs (api-docs.deepseek.com/guides/thinking_mode): "Thinking mode does not support the `temperature`, `top_p`, `presence_penalty`, or `frequency_penalty` parameters. Please note that, for compatibility with existing software, setting these parameters will not trigger an error but will also have no effect." Default temperature is 1.0 (api-docs.deepseek.com/quick_start/parameter_settings). Together AI docs warn: "Lower temperatures can collapse the reasoning trace and degrade answer quality." The parameter is silently accepted but has zero effect — this is by design, not an Ollama quirk. GLM-5.2 confirmed to respect temperature via direct API test. See `references/temperature-test-results.md`.
### Verification workflow for parameter testing
When testing whether a parameter reaches the API and is respected:
1. **Test the model directly against the API first** — use `curl` to confirm the model actually respects the parameter before testing through Hermes
2. **Use the model the user specifies** — do NOT suggest alternative models. If the user says "test with DeepSeek," test with DeepSeek even if you suspect it won't work
3. **Get test prompts from the web** — when the user says "get test questions from the web," use SearXNG MCP to find validated test prompts. Do NOT reuse the same prompts or invent your own
4. **Test at multiple temperature values** — 0, 1.0, and an out-of-range value (e.g. 4) to confirm the parameter reaches the API (out-of-range → 400 proves injection)
5. **Run each test 3-5 times** — temperature effects are statistical; single runs are inconclusive
6. **Use creative prompts for differentiation** — "write a poem about X" shows variation better than "name a random number"
### Matching logic
`_custom_provider_extra_body_for_agent()` matches by:
1. Provider name (`custom:ollama` → filter by `name: Ollama` or `provider_key: ollama`)
2. Base URL (normalized: strip trailing `/`)
3. Optionally per-model via `_custom_provider_model_matches()`
The runtime resolution for `custom:<name>` picks up the matching `custom_providers` entry's `base_url`, overriding the profile's `model.base_url`. So the agent's actual base_url at runtime matches the entry.
## Verification Workflow
When investigating whether Hermes supports a parameter:
1. Search the source for the parameter name: `search_files(pattern="temperature", path="~/.hermes/hermes-agent/agent")`
2. Trace `build_kwargs` in `agent/transports/chat_completions.py` — this is where API kwargs are assembled
3. Check the relevant provider profile in `plugins/model-providers/<name>/__init__.py`
4. Check `agent/agent_init.py` for how config values flow into the agent
5. Check `hermes_cli/runtime_provider.py` for how custom provider config maps to runtime
## Key Source Files
| File | Role |
|------|------|
| `agent/transports/chat_completions.py` | API kwargs assembly (build_kwargs, _build_kwargs_from_profile) |
| `agent/auxiliary_client.py` | _fixed_temperature_for_model, _build_call_kwargs |
| `agent/agent_init.py` | Config → agent attribute mapping |
| `agent/anthropic_adapter.py` | _forbids_sampling_params |
| `providers/base.py` | ProviderProfile base class (fixed_temperature, build_extra_body) |
| `plugins/model-providers/*/__init__.py` | Per-provider profiles |
| `hermes_cli/runtime_provider.py` | Custom provider → runtime resolution, request_overrides |