hermes - 💡(How to fix) Fix Bug: get_auxiliary_extra_body() ignores auxiliary.<task>.extra_body from config.yaml

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

get_auxiliary_extra_body() in agent/auxiliary_client.py silently drops any extra_body configured in auxiliary.<task>.extra_body from config.yaml. The config field exists, the schema accepts it, _get_auxiliary_task_config() returns it — but the function that delivers it to the API call never reads it.

Error Message

def get_auxiliary_extra_body(task: str = "") -> dict: """Return extra_body kwargs for auxiliary API calls.""" result = _nous_extra_body() if auxiliary_is_nous else {}

if task:
    try:
        task_cfg = _get_auxiliary_task_config(task)
        task_extra = task_cfg.get("extra_body", {}) or {}
        if isinstance(task_extra, dict):
            result.update(task_extra)
    except Exception:
        pass

return result

Root Cause

flowchart TD
    A["config.yaml: auxiliary.&lt;task&gt;.extra_body"]
    B["_get_auxiliary_task_config(task)"]
    C["get_auxiliary_extra_body()"]
    D["_nous_extra_body()"]
    E["HTTP request body"]
    
    A --> B
    B -->|"has extra_body ✓"| E
    
    C -->|"only checks auxiliary_is_nous"| D
    D -->|"returns {} for non-Nous"| C
    C -->|"returns {}"| E
    
    style C fill:#f88,stroke:#c00
    style E fill:#f88,stroke:#c00

The function at line 4134:

def get_auxiliary_extra_body() -> dict:
    return _nous_extra_body() if auxiliary_is_nous else {}

Returns {} for every non-Nous provider. The task-specific extra_body that the user configured in auxiliary.&lt;task&gt;.extra_body is never consulted. Callers then do get_auxiliary_extra_body() or None{} is falsy — so None goes into the API call, and no extra_body parameter is sent.

Meanwhile _get_auxiliary_task_config(task) (line 4589) correctly reads auxiliary.&lt;task&gt; from the resolved config, including its extra_body field. The data flow is:

User config → load_config() → _get_auxiliary_task_config(task) → has extra_body ✓
get_auxiliary_extra_body() → {}  (ignores task config)                   ✗
HTTP POST to /chat/completions — no extra_body in request body          ✗

Fix Action

Fix / Workaround

Confirmed fixed locally: 5/5 consecutive hermes profile describe researcher --auto calls succeed after applying the patch. Before: ~60% failure rate with "LLM returned an empty response".

Code Example

flowchart TD
    A["config.yaml: auxiliary.&lt;task&gt;.extra_body"]
    B["_get_auxiliary_task_config(task)"]
    C["get_auxiliary_extra_body()"]
    D["_nous_extra_body()"]
    E["HTTP request body"]
    
    A --> B
    B -->|"has extra_body ✓"| E
    
    C -->|"only checks auxiliary_is_nous"| D
    D -->|"returns {} for non-Nous"| C
    C -->|"returns {}"| E
    
    style C fill:#f88,stroke:#c00
    style E fill:#f88,stroke:#c00

---

def get_auxiliary_extra_body() -> dict:
    return _nous_extra_body() if auxiliary_is_nous else {}

---

User config → load_config()_get_auxiliary_task_config(task) → has extra_body ✓
get_auxiliary_extra_body(){}  (ignores task config)HTTP POST to /chat/completions — no extra_body in request body          ✗

---

auxiliary:
  profile_describer:
    extra_body:
      chat_template_kwargs:
        enable_thinking: false

---

def get_auxiliary_extra_body(task: str = "") -> dict:
    """Return extra_body kwargs for auxiliary API calls."""
    result = _nous_extra_body() if auxiliary_is_nous else {}
    
    if task:
        try:
            task_cfg = _get_auxiliary_task_config(task)
            task_extra = task_cfg.get("extra_body", {}) or {}
            if isinstance(task_extra, dict):
                result.update(task_extra)
        except Exception:
            pass
    
    return result

---

extra_body=get_auxiliary_extra_body(task="profile_describer") or None,
RAW_BUFFERClick to expand / collapse

Summary

get_auxiliary_extra_body() in agent/auxiliary_client.py silently drops any extra_body configured in auxiliary.<task>.extra_body from config.yaml. The config field exists, the schema accepts it, _get_auxiliary_task_config() returns it — but the function that delivers it to the API call never reads it.

Impact

Every auxiliary task that calls get_auxiliary_extra_body() and configures extra_body in config.yaml has that configuration silently ignored. Affected callers:

  • profile_describer.py — cannot pass enable_thinking: false to Qwen3 models running locally
  • kanban_specify.py — cannot pass provider-specific parameters
  • kanban_decompose.py — same
  • goals.py — same

Related

This is distinct from #7209 (model.extra_body passthrough for the main agent loop). #7209 covers the main AIAgent request path; this bug is in the auxiliary task client at agent/auxiliary_client.py, which has a separate config layer (auxiliary.<task>.extra_body) and a separate delivery function (get_auxiliary_extra_body()). Both need fixing, but they're independent code paths.

Root Cause

flowchart TD
    A["config.yaml: auxiliary.&lt;task&gt;.extra_body"]
    B["_get_auxiliary_task_config(task)"]
    C["get_auxiliary_extra_body()"]
    D["_nous_extra_body()"]
    E["HTTP request body"]
    
    A --> B
    B -->|"has extra_body ✓"| E
    
    C -->|"only checks auxiliary_is_nous"| D
    D -->|"returns {} for non-Nous"| C
    C -->|"returns {}"| E
    
    style C fill:#f88,stroke:#c00
    style E fill:#f88,stroke:#c00

The function at line 4134:

def get_auxiliary_extra_body() -> dict:
    return _nous_extra_body() if auxiliary_is_nous else {}

Returns {} for every non-Nous provider. The task-specific extra_body that the user configured in auxiliary.&lt;task&gt;.extra_body is never consulted. Callers then do get_auxiliary_extra_body() or None{} is falsy — so None goes into the API call, and no extra_body parameter is sent.

Meanwhile _get_auxiliary_task_config(task) (line 4589) correctly reads auxiliary.&lt;task&gt; from the resolved config, including its extra_body field. The data flow is:

User config → load_config() → _get_auxiliary_task_config(task) → has extra_body ✓
get_auxiliary_extra_body() → {}  (ignores task config)                   ✗
HTTP POST to /chat/completions — no extra_body in request body          ✗

Reproduction

  1. Set auxiliary.profile_describer.extra_body in config.yaml:
auxiliary:
  profile_describer:
    extra_body:
      chat_template_kwargs:
        enable_thinking: false
  1. Run hermes profile describe researcher --auto

  2. Observe the HTTP request (visible in agent logs at DEBUG level) contains no extra_body key — only messages, model, max_tokens, temperature.

For Qwen3 models running locally with thinking mode, this causes the model to burn all tokens on reasoning, return content: null, and the describer reports "LLM returned an empty response".

Proposed Fix

The function needs to read extra_body from the current task's config and merge it with Nous extras:

def get_auxiliary_extra_body(task: str = "") -> dict:
    """Return extra_body kwargs for auxiliary API calls."""
    result = _nous_extra_body() if auxiliary_is_nous else {}
    
    if task:
        try:
            task_cfg = _get_auxiliary_task_config(task)
            task_extra = task_cfg.get("extra_body", {}) or {}
            if isinstance(task_extra, dict):
                result.update(task_extra)
        except Exception:
            pass
    
    return result

Then each caller passes its task name, e.g.:

extra_body=get_auxiliary_extra_body(task="profile_describer") or None,

This preserves backward compatibility — callers that don't pass task get the current behavior (Nous Portal tags only).

May also want to bump profile_describer.py's max_tokens from 400 to 600 since JSON + 1-2 sentence description is tight at 400 when the context contains 55+ skill names.

Verification

Confirmed fixed locally: 5/5 consecutive hermes profile describe researcher --auto calls succeed after applying the patch. Before: ~60% failure rate with "LLM returned an empty response".


Filed by Jasper (AI agent on behalf of Magnus Hedemark)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix Bug: get_auxiliary_extra_body() ignores auxiliary.<task>.extra_body from config.yaml