hermes - 💡(How to fix) Fix Context compression should integrate memory provider context into the summary [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

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…
GitHub stats
NousResearch/hermes-agent#23367Fetched 2026-05-11 03:29:50
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×4

Error Message

  1. No preservation directives — providers can't tell the compressor "preserve the last file path" or "keep the error resolution exchange"

Code Example

# run_agent.py compression flow (simplified)
# 1. Call memory providers BEFORE compression (good!)
provider_context = self._memory_manager.on_pre_compress(messages)

# 2. Start compression
compressed = self.context_compressor.compress(messages)

# Inside compress():
#   a. _prune_old_tool_results() — cheap removal
#   b. _generate_summary()LLM summary of middle section
#   c. Replace old messages with summary

---

@dataclass
class CompressionContext:
    """Structured context from memory providers for compression."""
    text: str = ""                          # Freeform context (backward compatible)
    preserve_snippets: List[str] = []       # Text snippets that MUST appear in the summary
    preservation_rules: List[str] = []      # Declarative rules ("keep_last_file_path", "keep_last_error")
    key_facts: List[str] = []              # Facts to inject into the final summary verbatim

---

def _generate_summary(self, messages, provider_contexts):
    prompt = "Summarize the conversation, preserving key information.\n\n"

    # Inject provider key facts (must survive compression)
    for provider, ctx in provider_contexts.items():
        if ctx.key_facts:
            prompt += f"[Context from {provider.name} - PRESERVE VERBATIM]\n"
            for fact in ctx.key_facts:
                prompt += f"- {fact}\n"
            prompt += f"[End {provider.name} context]\n\n"

    if ctx.preservation_rules:
        prompt += "Special preservation rules:\n"
        for rule in ctx.preservation_rules:
            prompt += f"- {rule}\n"
        prompt += "\n"

    prompt += "--- Conversation to summarize ---\n"
    prompt += self._format_messages(messages)

---

# Example rules that providers can specify
PRESERVATION_RULES = {
    "keep_last_file_path": lambda msgs: extract_last_pattern(msgs, r'(/[\w./-]+\.\w+)'),
    "keep_last_error": lambda msgs: extract_last_error_resolution(msgs),
    "keep_last_decision": lambda msgs: extract_last_decision(msgs),
    "keep_user_preferences": lambda msgs: extract_preferences(msgs),
}

---

class MuninnMemoryProvider(MemoryProvider):
    def on_pre_compress(self, messages) -> CompressionContext:
        # Extract and save memories externally (already works)
        self._extract_compression_memories(messages)

        # Build structured context for the compressor
        return CompressionContext(
            text=self._build_compression_digest(messages),
            key_facts=[
                f"Active project: {self._detect_project(messages)}",
                f"Last file modified: {self._find_last_path(messages)}",
            ],
            preservation_rules=[
                "keep_last_file_path",
                "keep_user_preferences",
            ],
        )
RAW_BUFFERClick to expand / collapse

Context compression should integrate memory provider context into the summary

Problem Description

Hermes Agent's context compression mechanism has an existing on_pre_compress(messages) hook in the MemoryProvider base class. The hook is correctly called before pruning (from run_agent.py line 10018), and the returned text is included in the compression summary prompt. However, the integration is limited — providers can only append freeform text to the prompt, with no structured way to influence what the compressor preserves.

Current Behavior

# run_agent.py compression flow (simplified)
# 1. Call memory providers BEFORE compression (good!)
provider_context = self._memory_manager.on_pre_compress(messages)

# 2. Start compression
compressed = self.context_compressor.compress(messages)

# Inside compress():
#   a. _prune_old_tool_results() — cheap removal
#   b. _generate_summary() — LLM summary of middle section
#   c. Replace old messages with summary

What works:

  • on_pre_compress() receives full messages before any pruning
  • Providers can extract structured memories and save them externally
  • Returned text is appended to the compression prompt

Limitations:

  1. No structured returnon_pre_compress returns str, limiting providers to freeform text
  2. No preservation directives — providers can't tell the compressor "preserve the last file path" or "keep the error resolution exchange"
  3. No feedback loop — providers don't know what the compressor decided to keep/discard
  4. Text is appended, not integrated — provider context goes into the compression prompt but isn't structurally preserved in the output summary

Proposed Improvements

1. Structured Return Type

Replace the plain str return with a CompressionContext dataclass:

@dataclass
class CompressionContext:
    """Structured context from memory providers for compression."""
    text: str = ""                          # Freeform context (backward compatible)
    preserve_snippets: List[str] = []       # Text snippets that MUST appear in the summary
    preservation_rules: List[str] = []      # Declarative rules ("keep_last_file_path", "keep_last_error")
    key_facts: List[str] = []              # Facts to inject into the final summary verbatim

2. Provider-Injected Context in Compression Prompt

Structure the compression prompt to clearly mark provider-injected context:

def _generate_summary(self, messages, provider_contexts):
    prompt = "Summarize the conversation, preserving key information.\n\n"

    # Inject provider key facts (must survive compression)
    for provider, ctx in provider_contexts.items():
        if ctx.key_facts:
            prompt += f"[Context from {provider.name} - PRESERVE VERBATIM]\n"
            for fact in ctx.key_facts:
                prompt += f"- {fact}\n"
            prompt += f"[End {provider.name} context]\n\n"

    if ctx.preservation_rules:
        prompt += "Special preservation rules:\n"
        for rule in ctx.preservation_rules:
            prompt += f"- {rule}\n"
        prompt += "\n"

    prompt += "--- Conversation to summarize ---\n"
    prompt += self._format_messages(messages)

3. Preservation Rules Engine

Add a simple rules engine that processes provider directives before summary generation:

# Example rules that providers can specify
PRESERVATION_RULES = {
    "keep_last_file_path": lambda msgs: extract_last_pattern(msgs, r'(/[\w./-]+\.\w+)'),
    "keep_last_error": lambda msgs: extract_last_error_resolution(msgs),
    "keep_last_decision": lambda msgs: extract_last_decision(msgs),
    "keep_user_preferences": lambda msgs: extract_preferences(msgs),
}

Use Case: Muninn Memory Plugin

The Muninn plugin extracts structured information from conversations — file paths, URLs, decisions, corrections. With the current str return, it can only append a digest. With CompressionContext, it can:

class MuninnMemoryProvider(MemoryProvider):
    def on_pre_compress(self, messages) -> CompressionContext:
        # Extract and save memories externally (already works)
        self._extract_compression_memories(messages)

        # Build structured context for the compressor
        return CompressionContext(
            text=self._build_compression_digest(messages),
            key_facts=[
                f"Active project: {self._detect_project(messages)}",
                f"Last file modified: {self._find_last_path(messages)}",
            ],
            preservation_rules=[
                "keep_last_file_path",
                "keep_user_preferences",
            ],
        )

Backward Compatibility

  • Providers returning str are auto-wrapped: CompressionContext(text=result)
  • Providers not implementing on_pre_compress get default empty CompressionContext
  • No migration required — all existing providers continue working

Minimal Viable Implementation

For an MVP, just the structured return type + key_facts injection in the prompt. Preservation rules can come later.

Checklist

  • Add CompressionContext dataclass to memory_provider.py
  • Update on_pre_compress() return type (with str backward compat)
  • Update memory_manager.on_pre_compress() to handle CompressionContext
  • Update _generate_summary() prompt to integrate provider key_facts
  • Add backward compatibility wrapper for str returns
  • Add tests
  • Update documentation

Priority: Medium Effort: 1-2 days for MVP Breaking Changes: No Migration Required: No

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 Context compression should integrate memory provider context into the summary [1 participants]