Skip to content

feat(mem): config-driven allocator trim with telemetry and lifecycle coverage - #66355

Closed
aider4ryder wants to merge 1 commit into
NousResearch:mainfrom
aider4ryder:feat/mem-trim-p1
Closed

feat(mem): config-driven allocator trim with telemetry and lifecycle coverage#66355
aider4ryder wants to merge 1 commit into
NousResearch:mainfrom
aider4ryder:feat/mem-trim-p1

Conversation

@aider4ryder

Copy link
Copy Markdown

Summary

Config-driven glibc malloc_trim for long-lived Hermes processes (gateway, TUI gateway, slash_worker).

Changes

  • hermes_cli/mem_trim.py: trim_memory() with configurable cooldown, RSS snapshot telemetry, and forced-trim INFO logging
  • gateway/run.py: periodic trim in gateway housekeeping loop
  • tui_gateway/server.py: trim in idle reaper (~every 5 min)
  • tui_gateway/slash_worker.py: trim on turn boundary
  • run_agent.py: force trim on agent close
  • hermes_cli/config.py: context.memory_trim config section

Config

context:
  memory_trim:
    enabled: true
    cooldown_seconds: 60.0
    log_every_n: 1
    info_log_min_delta_mb: 0.0

Unsupported platforms (non-Linux/non-glibc) are safe no-ops.

Testing

  • 12 unit tests
  • CSA tier-4 reviewed (4 rounds; 0 HIGH/MEDIUM/CRITICAL)
  • Deployed and smoke-tested on production machine

Supersedes #63708 + #64591.

…coverage

Add config-driven glibc malloc_trim for long-lived Hermes processes:
- hermes_cli/mem_trim.py: trim_memory() with configurable cooldown,
  RSS snapshot telemetry, and forced-trim INFO logging
- gateway/run.py: periodic trim in gateway housekeeping loop
- tui_gateway/server.py: trim in idle reaper (~every 5 min)
- tui_gateway/slash_worker.py: trim on turn boundary
- run_agent.py: force trim on agent close
- hermes_cli/config.py: context.memory_trim config section
  (enabled, cooldown_seconds, log_every_n, info_log_min_delta_mb)

CSA tier-4 reviewed (4 rounds, 0 HIGH/MEDIUM/CRITICAL remaining).

Supersedes PR NousResearch#63708 + NousResearch#64591 with enhanced telemetry and gateway/slash_worker coverage.
@alt-glitch alt-glitch added type/perf Performance improvement or optimization P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery comp/cli CLI entry point, hermes_cli/, setup wizard comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 17, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #63708 and #64591 cover allocator-trim infrastructure and the gateway reaper. This PR adds broader lifecycle hooks; please choose a consolidation path rather than treating it as a duplicate.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment

Memory subsystem change touching 9 files with 644 additions. The PR adds config-driven allocator trim with telemetry and lifecycle coverage. While the description sounds reasonable, the broad scope across the memory subsystem warrants human review to confirm no unintended interactions with existing memory management logic.


Reviewed by Hermes Agent

@teknium1 teknium1 added the sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit label Jul 18, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for consolidating the allocator-trim work and retaining the config.yaml mechanism from the related PRs.

Problems

  • run_agent.py:3627 adds trim_memory(force=True), but AIAgent.close() recursively calls child.close() for every active child (run_agent.py:3590-3599). The helper's force path bypasses its cooldown, so a teardown with N children performs N+1 full collections/trims. Please retain rate limiting for nested closes or designate one outer teardown trim.
  • The new context.memory_trim controls are not documented. website/docs/user-guide/configuration.md:812-830 currently documents only context.engine, while this PR changes no docs file.
  • The tests do not cover the new AIAgent.close() or slash-worker lifecycle hooks.

Suggested changes

  • Add lifecycle tests for those two hooks, including nested-child cooldown behavior.
  • Add configuration documentation for defaults and Linux/glibc-only behavior.

Automated hermes-sweeper review.

Comment thread run_agent.py
# mark until exit. This helper is a safe no-op on other allocators.
try:
from hermes_cli.mem_trim import trim_memory
trim_memory(force=True, reason="agent close")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

force=True bypasses the helper cooldown, while close() recursively calls child.close() for every active child. A parent teardown can therefore run a full collection/trim once per child plus once for the parent. Please preserve rate limiting for nested closes or arrange for only the outer teardown to force a trim.

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Thanks @aider4ryder — this is exactly the comprehensive trim design the earlier per-site attempts needed (it already superseded #63708/#64591, which were closed pointing here). The base had drifted (DEFAULT_CONFIG moved to config_defaults.py, housekeeping/close/reaper anchors all shifted), so I re-anchored it onto current main and salvaged it into #76905 with your authorship preserved via cherry-pick; all 5 lifecycle hooks and every test carried over (505/505 gateway-server tests green). Closing in favor of the salvage.

kshitijk4poor added a commit to kshitijk4poor/hermes-agent that referenced this pull request Aug 2, 2026
…test

Simplify-pass follow-up on the NousResearch#66355 salvage:

1. _config_settings runs on EVERY trim attempt (before the cooldown
   check) and only reads — swap load_config for load_config_readonly.
   Deep-copying the whole config per attempt generates exactly the
   allocator garbage this module exists to release. Tests re-seamed.

2. Trim-failure logs demoted warning->debug at all 3 periodic sites
   (gateway housekeeping, idle reaper, slash worker): sibling failure
   branches in the same loops log at debug, and a persistent failure
   (e.g. broken import after a partial update) would otherwise warn
   every 60s forever.

3. The frame-inspection test now asserts the expected locals exist
   before reading them — a rename in _run_prompt_submit fails the test
   loudly instead of vacuously passing on None.
kshitijk4poor added a commit to kshitijk4poor/hermes-agent that referenced this pull request Aug 2, 2026
Efficiency-pass follow-up on the NousResearch#66355 salvage: force=True bypassed the
cooldown entirely, and AIAgent.close() fires a forced trim for EVERY
in-process child subagent close (delegate_tool child.close(), parent
close step 5). A delegate batch of N children closing back-to-back in
the gateway process stacked N+1 uncooled full gc.collect()+malloc_trim
passes (50-500ms each with a large live heap). Forced trims now honor a
5s floor — bursts coalesce, the parent's final close-trim still fires.
Guard test mutation-checked (floor zeroed -> test fails).
kshitijk4poor added a commit that referenced this pull request Aug 2, 2026
…test

Simplify-pass follow-up on the #66355 salvage:

1. _config_settings runs on EVERY trim attempt (before the cooldown
   check) and only reads — swap load_config for load_config_readonly.
   Deep-copying the whole config per attempt generates exactly the
   allocator garbage this module exists to release. Tests re-seamed.

2. Trim-failure logs demoted warning->debug at all 3 periodic sites
   (gateway housekeeping, idle reaper, slash worker): sibling failure
   branches in the same loops log at debug, and a persistent failure
   (e.g. broken import after a partial update) would otherwise warn
   every 60s forever.

3. The frame-inspection test now asserts the expected locals exist
   before reading them — a rename in _run_prompt_submit fails the test
   loudly instead of vacuously passing on None.
kshitijk4poor added a commit that referenced this pull request Aug 2, 2026
Efficiency-pass follow-up on the #66355 salvage: force=True bypassed the
cooldown entirely, and AIAgent.close() fires a forced trim for EVERY
in-process child subagent close (delegate_tool child.close(), parent
close step 5). A delegate batch of N children closing back-to-back in
the gateway process stacked N+1 uncooled full gc.collect()+malloc_trim
passes (50-500ms each with a large live heap). Forced trims now honor a
5s floor — bursts coalesce, the parent's final close-trim still fires.
Guard test mutation-checked (floor zeroed -> test fails).
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…test

Simplify-pass follow-up on the NousResearch#66355 salvage:

1. _config_settings runs on EVERY trim attempt (before the cooldown
   check) and only reads — swap load_config for load_config_readonly.
   Deep-copying the whole config per attempt generates exactly the
   allocator garbage this module exists to release. Tests re-seamed.

2. Trim-failure logs demoted warning->debug at all 3 periodic sites
   (gateway housekeeping, idle reaper, slash worker): sibling failure
   branches in the same loops log at debug, and a persistent failure
   (e.g. broken import after a partial update) would otherwise warn
   every 60s forever.

3. The frame-inspection test now asserts the expected locals exist
   before reading them — a rename in _run_prompt_submit fails the test
   loudly instead of vacuously passing on None.
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
Efficiency-pass follow-up on the NousResearch#66355 salvage: force=True bypassed the
cooldown entirely, and AIAgent.close() fires a forced trim for EVERY
in-process child subagent close (delegate_tool child.close(), parent
close step 5). A delegate batch of N children closing back-to-back in
the gateway process stacked N+1 uncooled full gc.collect()+malloc_trim
passes (50-500ms each with a large live heap). Forced trims now honor a
5s floor — bursts coalesce, the parent's final close-trim still fires.
Guard test mutation-checked (floor zeroed -> test fails).
@aider4ryder

Copy link
Copy Markdown
Author

FYI — this already landed upstream. It was salvaged into official main as PR #76905 (da43a8527b47 feat(mem): config-driven allocator trim with telemetry and lifecycle coverage), with follow-ups 3fad8fdc45b9 and c575351d9ac5, touching hermes_cli/mem_trim.py, gateway/run.py and tests/gateway/test_memory_trim_housekeeping.py. Superseded by upstream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants