Skip to main content

Overview

CompositeMemory chains multiple MemoryProvider implementations together. This is useful for combining fast local memory with slow persistent memory. Best for: multi-tier memory architectures (L1 cache + L2 persistent storage).

Constructor

import { CompositeMemory } from '@agentlib/memory'

const memory = new CompositeMemory(config)

Configuration

providers
MemoryProvider[]
required
Providers chained in order.
  • read() queries them in order and returns the first non-empty result (or merges all)
  • write() fans out to ALL providers
readStrategy
'first-hit' | 'merge'
default:"first-hit"
Read strategy:
  • 'first-hit': Return the first provider that has data (default)
  • 'merge': Merge results from all providers (deduplicates by content)

Methods

read()

Retrieve conversation history using the configured strategy.
async read(options: MemoryReadOptions): Promise<ModelMessage[]>
Parameters:
  • options.sessionId - Session identifier
  • Other MemoryReadOptions fields passed to underlying providers
Returns: Array of messages based on the readStrategy: first-hit strategy:
  • Queries providers in order
  • Returns the first non-empty result
  • Returns empty array if all providers are empty
merge strategy:
  • Queries all providers in parallel
  • Combines results and deduplicates by content fingerprint
  • Returns merged array

write()

Persist messages to ALL providers.
async write(messages: ModelMessage[], options: MemoryWriteOptions): Promise<void>
Parameters:
  • messages - Array of messages to store
  • options - Write options passed to all providers
Behavior:
  • Fans out write to all providers in parallel
  • All providers receive the same messages and options

clear()

Remove stored memory from all providers.
async clear(sessionId?: string): Promise<void>
Parameters:
  • sessionId - If provided, clears only that session. Otherwise clears all sessions.
Behavior:
  • Calls clear() on all providers in parallel
  • Providers without clear() are skipped

entries()

Retrieve raw memory entries from all providers.
async entries(sessionId?: string): Promise<MemoryEntry[]>
Parameters:
  • sessionId - If provided, returns only that session’s entries
Returns: Flattened array of entries from all providers.

Usage Examples

Two-Tier Memory Architecture

import { CompositeMemory, BufferMemory } from '@agentlib/memory'
import { RedisMemory } from '@agentlib/redis'

const memory = new CompositeMemory({
  providers: [
    new BufferMemory({ maxMessages: 20 }),   // Fast L1 cache
    new RedisMemory({ url: process.env.REDIS_URL }), // Persistent L2
  ],
})

const agent = new Agent({
  name: 'assistant',
  memory,
})
Behavior:
  • Reads check BufferMemory first (fast), fall back to Redis (slow)
  • Writes go to both providers
  • In-memory cache warms up over time

Merge Strategy

const memory = new CompositeMemory({
  providers: [
    new BufferMemory(),
    new VectorMemory(), // Semantic memory
  ],
  readStrategy: 'merge', // Combine results from both
})
Behavior:
  • Reads from both providers and merges results
  • Deduplicates by message content
  • Useful for combining different memory types

Multiple Storage Backends

const memory = new CompositeMemory({
  providers: [
    new BufferMemory({ maxMessages: 10 }),     // Recent cache
    new RedisMemory({ url: redisUrl }),        // Short-term storage
    new PostgresMemory({ connectionString }), // Long-term storage
  ],
})
Behavior:
  • Reads try BufferMemory → Redis → Postgres
  • Writes replicate to all three
  • Different tiers can have different retention policies

Inspecting All Providers

const entries = await memory.entries('session-1')

// Entries from all providers
for (const entry of entries) {
  console.log(`Provider: ${entry.metadata.agentName}`)
  console.log(`Messages: ${entry.messages.length}`)
}

Read Strategies

first-hit (default)

const memory = new CompositeMemory({
  providers: [fastMemory, slowMemory],
  readStrategy: 'first-hit',
})
  • Queries providers sequentially in order
  • Returns immediately when a provider has data
  • Faster for typical cache-hit scenarios
  • Best for L1/L2 cache patterns

merge

const memory = new CompositeMemory({
  providers: [recentMemory, semanticMemory],
  readStrategy: 'merge',
})
  • Queries all providers in parallel
  • Combines and deduplicates results
  • Useful for heterogeneous memory types
  • Best for semantic + chronological memory

Deduplication

When using readStrategy: 'merge', messages are deduplicated by fingerprint:
const key = `${msg.role}:${msg.content.slice(0, 100)}`
Messages with identical role and first 100 characters are considered duplicates.

Error Handling

try {
  const memory = new CompositeMemory({ providers: [] })
} catch (err) {
  // Error: [CompositeMemory] At least one provider required.
}
At least one provider must be configured.

Type Reference

Source: /packages/memory/src/composite.ts:9-23
interface CompositeMemoryConfig {
  providers: MemoryProvider[]
  readStrategy?: 'first-hit' | 'merge'
}

Build docs developers (and LLMs) love