rig-memory-v0.1.1

0xPlaygrounds/rigrig-memory-v0.1.1May 13, 2026by github-actions[bot]

AI Summary

Adds advanced memory management traits including compactors, demotion hooks, and heuristic token counting.

Key Highlights

  • Add Compactor trait for summarizing evicted history
  • Add DemotionHook trait for handling truncated messages
  • Add HeuristicTokenCounter for provider-agnostic token estimation
  • Add DemotingPolicyMemory adapter

New Features

  • CompactingMemory adapter
  • DemotionHook trait
  • HeuristicTokenCounter
  • PolicyMemory adapter

Full Release Notes

### Added

- *(memory)* add Compactor trait, CompactingMemory adapter, and TemplateCompactor ([#1748](https://github.com/0xPlaygrounds/rig/pull/1748)) (by @ForeverAngry)
- *(memory)* Rig-managed conversation memory + rig-memory companion crate ([#1702](https://github.com/0xPlaygrounds/rig/pull/1702)) (by @ForeverAngry)

### Other

- Memory adapter cancellation safety and trait-object forwarding ([#1756](https://github.com/0xPlaygrounds/rig/pull/1756)) (by @gold-silver-copper) - #1756
- Add demotion hooks for bounded conversation memory ([#1737](https://github.com/0xPlaygrounds/rig/pull/1737)) (by @ForeverAngry) - #1737

### Contributors

* @gold-silver-copper
* @ForeverAngry

### Added

- `Compactor` trait (re-exported from `rig_core::memory`) +
  `CompactingMemory<M, P, C>` adapter and a `TemplateCompactor`
  reference implementation. Where `DemotingPolicyMemory` only
  *observes* messages a policy truncates out of active history, a
  `Compactor` *substitutes* them: it derives a single `Message`-shaped
  `Artifact` from the evicted prefix (and, optionally, the previous
  summary), and `CompactingMemory` splices that artifact at the front
  of the loaded history. The resulting prompt shape is
  `[summary_message, ...kept_window]`, with the summary rolling
  forward on every load that produces newly-evicted messages — the
  canonical recursive-summary pattern for long-running agents.
  Concurrent loads on the same `conversation_id` are serialised at
  the compaction seam via an in-flight gate: only one load at a time
  invokes the compactor; others observe the gate and immediately
  return the previously-stored summary spliced in front of `kept`,
  without re-running the compactor. Watermarks and the carry-over
  summary are in-process only — `Compactor` implementations with
  durable side effects (LLM calls, vector-store writes) must
  deduplicate. `clear` drops the carry-over so a freshly-populated
  backend re-compacts from scratch. `TemplateCompactor` is a
  zero-dependency, no-LLM rollup useful as a default and for tests;
  it produces a `TextSummary` that converts into a `Message::System`
  with header + previous-summary + per-line `role: text` body. The
  rollup represents out-of-band context about the prior conversation
  rather than a turn from any participant, so the system role is the
  semantically correct framing across providers. `TemplateCompactor`
  exposes `with_max_bytes` so long-running conversations can cap the
  rolled-up text; when exceeded, the oldest portion of the body is
  dropped at a UTF-8 boundary and replaced with a `"[…truncated…]"`
  marker, preserving the most recent context. Note that the spliced
  summary sits **outside** the wrapped `MemoryPolicy`'s budget, so
  pairing `CompactingMemory` with a token-budgeted policy requires a
  bounded compactor (or accepting that the loaded prompt may exceed
  the policy budget by the artifact size).

- `DemotionHook` trait + `DemotingPolicyMemory<M, P, H>` adapter and a
  `NoopDemotionHook` no-op default. The trait itself lives in
  `rig_core::memory` (re-exported here) so any memory backend can
  implement it without taking a `rig-memory` dependency; the composing
  adapter lives in this crate. `DemotingPolicyMemory` calls the hook
  with messages that the policy truncated out of active history,
  turning eviction into demotion. It tracks per-conversation demotion
  watermarks so repeated `load` calls do not replay the same demoted
  messages into append-only long-tail stores. Concurrent loads on the
  same `conversation_id` are serialised at the demotion seam via an
  in-flight gate: only one load at a time delivers to the hook;
  others observe the gate and return the truncated history without
  re-firing. Watermarks are in-process only — `DemotionHook`
  implementations must be idempotent on `(conversation_id, messages)`
  to survive process restarts. Bridges `SlidingWindowMemory` /
  `TokenWindowMemory` to long-tail stores such as `MemvidPersistHook`
  without coupling either crate to the other.
- `DemotingPolicyMemory::forget(conversation_id)` and
  `tracked_conversations()` for explicit watermark-map cleanup and
  leak diagnostics. Both are infallible: a poisoned internal lock
  is treated as "nothing to forget / zero tracked" rather than a
  caller-visible error.
- `MemoryPolicy::apply_with_demoted` companion method that reports
  `(kept, demoted)`. `apply` remains the required method; the default
  `apply_with_demoted` returns `(apply(...)?, Vec::new())` so existing
  policies keep compiling unchanged. `SlidingWindowMemory` and
  `TokenWindowMemory` override it to populate the demoted prefix that
  `DemotingPolicyMemory` hands to the hook.
- `HeuristicTokenCounter` — provider-agnostic, zero-dependency
  `TokenCounter` implementation that approximates token cost from
  UTF-8 byte lengths (`str::len`, O(1)). Ships `default` / `openai` /
  `anthropic` / `gemini` presets so
  `TokenWindowMemory::new(budget, HeuristicTokenCounter::default())`
  works out of the box without a tokenizer dependency. Also handles the
  `Message::System` variant and tool-call argument payloads. The
  configurable ratio is named `bytes_per_token` to match the
  implementation; for ASCII text bytes and characters coincide, and
  for non-ASCII text the counter slightly over-estimates, which is the
  safe direction for a hard budget.
- `PolicyMemory<M, P>` adapter — wrap any `ConversationMemory` with a
  `MemoryPolicy` and propagate policy failures to the caller as
  `MemoryError::Policy`. Hard-fail counterpart to
  `InMemoryConversationMemory::with_filter` + `IntoFilter::into_filter`,
  which degrade to identity on policy error.