Skip to content

Consolidation

intermediate

Memories strengthen over time through sleep cycles. Learn how consolidation works, when it triggers, and what you observe as a user.

Memories that improve with age

In biological brains, sleep isn't idle time — it's when memories consolidate. Important experiences strengthen, irrelevant ones fade, and related patterns merge into coherent knowledge.

Engramma works the same way. Consolidation is the process where your memory space actively improves itself — without you re-indexing, re-embedding, or re-uploading anything.

What consolidation does

During a consolidation cycle, Engramma performs four operations:

OperationWhat it doesEffect you observe
StrengtheningFrequently-accessed memories get higher base confidenceImportant memories surface faster
EvictionRarely-accessed, low-importance memories are removedStorage stays clean, noise decreases
MergingNear-duplicate memories combine into one stronger memoryLess redundancy, clearer results
Link reinforcementCausal links that proved useful strengthenBetter causal reasoning over time

When consolidation happens

Consolidation can trigger in two ways:

Automatic (background)

Engramma runs lightweight consolidation in the background based on activity patterns. You don't need to do anything — it happens automatically when:

  • A threshold number of new memories have been stored (typically 50-100)
  • A significant time period has passed since the last cycle
  • The engine detects increasing redundancy in stored patterns

Manual (on-demand)

You can trigger consolidation explicitly when you want immediate optimization. The sleep endpoint is the full consolidation operation — it performs eviction, strengthening, and reorganization in a single cycle:

import requests

API_BASE = "https://api.engramma-memory.com"
HEADERS = {
    "X-API-Key": "your-api-key",
    "Content-Type": "application/json"
}

# Trigger a full consolidation cycle
response = requests.post(
    f"{API_BASE}/v1/memory/consolidation/sleep",
    headers=HEADERS,
    json={"mode": "full"}
)
result = response.json()

print(f"Status: {result['status']}")
print(f"Evicted: {result['evicted']}")
print(f"Strengthened: {result['strengthened']}")
print(f"Duration: {result['duration_ms']}ms")

Expected response:

{
  "status": "completed",
  "evicted": 3,
  "strengthened": 12,
  "duration_ms": 2340
}

For a lighter operation that only deduplicates without full eviction/strengthening, use the merge-duplicates endpoint:

# Merge only near-duplicate patterns (lighter than full sleep)
response = requests.post(
    f"{API_BASE}/v1/memory/consolidation/merge-duplicates",
    headers=HEADERS,
    json={"threshold": 0.85}
)
result = response.json()

print(f"Merged: {result['merged']}")
print(f"Patterns before: {result['patterns_before']}")
print(f"Patterns after: {result['patterns_after']}")

Expected response:

{
  "merged": 5,
  "patterns_before": 142,
  "patterns_after": 137
}

Sleep modes

The /v1/memory/consolidation/sleep endpoint accepts a mode parameter:

ModeWhat it doesWhen to use
"full"Complete consolidation: eviction + strengthening + reorganizationAfter bulk imports, scheduled maintenance
"light"Lighter pass with less aggressive evictionFrequent runs, incremental cleanup

You can also protect specific categories from eviction during a sleep cycle:

# Run full consolidation but protect identity and health memories
response = requests.post(
    f"{API_BASE}/v1/memory/consolidation/sleep",
    headers=HEADERS,
    json={
        "mode": "full",
        "protect_categories": ["identity", "health"]
    }
)
result = response.json()
print(f"Evicted: {result['evicted']}, Strengthened: {result['strengthened']}")

The consolidation lifecycle

Here's what happens to a memory over time:

1

Storage

New memory enters with a base importance score. It's immediately retrievable but hasn't been validated by time.
2

First accesses

Each time the memory is retrieved, its access counter increments. Frequently-accessed memories are marked as important.
3

Causal integration

If the memory participates in causal chains (Level 2/3 queries), its links to other memories strengthen.
4

Consolidation cycle

During the next sleep cycle, the memory's importance is recalculated. High-access, causally-linked memories get strengthened. Isolated, never-accessed memories are evicted.
5

Maturity

After several consolidation cycles, the memory reaches a stable importance level. It's now part of the 'long-term' memory and resistant to eviction.

Protecting memories from eviction

Sometimes you want certain categories of memories to persist regardless of access frequency. You can protect them during consolidation using the protect_categories parameter in the sleep request, or view currently protected patterns:

# View currently protected patterns
response = requests.get(
    f"{API_BASE}/v1/memory/consolidation/protected",
    headers=HEADERS
)
protected = response.json()
print(f"Protected patterns: {protected}")

# Run consolidation with category protection
response = requests.post(
    f"{API_BASE}/v1/memory/consolidation/sleep",
    headers=HEADERS,
    json={
        "mode": "full",
        "protect_categories": ["identity", "health"]
    }
)
# Memories in protected categories won't be evicted
# They can still be strengthened and merged
Warning

Don't over-protect. If you protect every category, consolidation can't clean up noise. Reserve protection for foundational categories that define your application's core knowledge (e.g., identity, health).

Monitoring consolidation health

Check your consolidation status and history with these endpoints:

# Check consolidation status
response = requests.get(
    f"{API_BASE}/v1/memory/consolidation/status",
    headers=HEADERS
)
status = response.json()
print(f"Status: {status['status']}")
print(f"Last run: {status['last_run']}")
print(f"Next scheduled: {status['next_scheduled']}")

# View sleep cycle statistics
response = requests.get(
    f"{API_BASE}/v1/memory/consolidation/sleep-stats",
    headers=HEADERS
)
stats = response.json()
print(f"Total cycles: {stats['total_cycles']}")
print(f"Last cycle duration: {stats['last_cycle']['duration_ms']}ms")
print(f"Last cycle evicted: {stats['last_cycle']['evicted']}")
print(f"Last cycle strengthened: {stats['last_cycle']['strengthened']}")

# Check importance rankings
response = requests.get(
    f"{API_BASE}/v1/memory/consolidation/importance",
    headers=HEADERS
)
importance = response.json()
print(f"Importance rankings: {importance}")

Example status response:

{
  "status": "idle",
  "last_run": "2026-01-14T03:00:00Z",
  "next_scheduled": null
}

Example sleep-stats response:

{
  "total_cycles": 42,
  "last_cycle": {
    "started_at": "2026-01-14T03:00:00Z",
    "duration_ms": 4200,
    "evicted": 3,
    "strengthened": 12
  }
}

Waking and resuming

After consolidation completes, the memory space is in a "sleeping" state briefly. You can explicitly wake it (though this happens automatically):

# Wake the consolidation system
response = requests.post(
    f"{API_BASE}/v1/memory/consolidation/wake",
    headers=HEADERS
)
result = response.json()
print(f"Status: {result['status']}")  # "awake"

Best practices

  1. Let automatic consolidation work — For most applications, the default background cycles are sufficient.

  2. Trigger manual consolidation after bulk imports — If you batch-store hundreds of memories at once, trigger a full sleep cycle to let the engine organize them.

  3. Monitor sleep-stats — Track evicted and strengthened counts over time to understand how aggressively consolidation is cleaning up.

  4. Don't consolidate too frequently — Each full cycle takes 2-5 seconds. Running it after every single store operation defeats the purpose. Let memories accumulate between cycles.

  5. Use merge-duplicates for lightweight cleanup — If you only need deduplication without full eviction/strengthening, use the merge-duplicates endpoint with an appropriate similarity threshold.

  6. Use consolidation previews — Before triggering a full sleep, preview what would happen:

# Preview consolidation without executing
response = requests.get(
    f"{API_BASE}/v1/memory/consolidation/preview",
    headers=HEADERS
)
preview = response.json()

print(f"Merge candidates: {preview['merge_candidates']}")
print(f"Estimated patterns after: {preview['estimated_patterns_after']}")

# Check specific merge candidates
response = requests.get(
    f"{API_BASE}/v1/memory/consolidation/merge-candidates",
    headers=HEADERS
)
candidates = response.json()

for c in candidates['candidates']:
    print(f"{c['pattern_a']} <-> {c['pattern_b']} (similarity: {c['similarity']})")

# Decide whether to proceed
if preview['merge_candidates'] > 10:
    requests.post(
        f"{API_BASE}/v1/memory/consolidation/sleep",
        headers=HEADERS,
        json={"mode": "full"}
    )

Example preview response:

{
  "merge_candidates": 12,
  "estimated_patterns_after": 130
}

Example merge-candidates response:

{
  "candidates": [
    {"pattern_a": "pat_abc", "pattern_b": "pat_def", "similarity": 0.94},
    {"pattern_a": "pat_ghi", "pattern_b": "pat_jkl", "similarity": 0.91}
  ]
}

Analogy: inbox vs. archive

Think of consolidation like email management:

  • New memories = unread emails (recent, uncertain importance)
  • Consolidation = your weekly inbox cleanup (archive what matters, delete noise, group related threads)
  • Mature memories = your organized archive (searchable, categorized, persistent)

The difference: Engramma does this cleanup automatically, and it gets smarter about what matters based on how you use it.

Next steps