OpenPay (1 crate, 3751 LOC, 83 tests)
op-crypto-extra Extended crypto rails. Stablecoin catalog (USDC, USDT,
PYUSD, DAI, RLUSD, EURC, FDUSD — 26 deployments). L2
catalog (12 chains incl. Optimism, Arbitrum, Base,
zkSync Era, Polygon zkEVM, Linea, Scroll, Starknet).
CCTP v2 BurnMessage encode/decode + domain ids +
attestation. ERC-4337 UserOperation v0.7 +
PackedUserOperation. EIP-7702 set-EOA-code with RLP
signing preimage. Uniswap Permit2 single + batch
transfer EIP-712 hashes. EIP-2612 ERC-20 permit.
BOLT-11 bech32 invoices + LNURL decode. Bitcoin
PSBT v2 (BIP-370). Atomic-swap HTLC. Solana SPL /
Aleo / Aztec confidential-transfer sketches.
Smart Byte (1 crate, 2304 LOC, 30 tests)
smart-byte-acdc Authentic Chained Data Containers (KERI ecosystem).
Acdc + AcdcBuilder (targeted + untargeted) with SAID
derivation. JSON Schema 2020-12 validator with
oneOf partial-disclosure. TEL (RIP/VRT/ISS/REV)
with hash-chained verification. IPEX (apply/offer/
agree/grant/admit/spurn) chained by SAID. Edge
records (I2I/NI2I/DI2I) + EdgeGraph BFS traversal
with cycle detection. Selective disclosure with
full/none/subset plans + compact packing.
CredentialRegistry trait + InMemoryRegistry over
TEL. Composes with smart-byte-core + smart-byte-keri-witness.
EOC (1 crate, 1764 LOC, 16 tests)
eoc-memory Long-term memory primitives. Memory trait + MemoryKind +
EpisodeId. Episodic event log with TimeIndex range
queries. RDF-style Triple store with S/P/O indices.
Bounded attention-budget Scratchpad with
salience-weighted eviction. Rolling SummaryBuffer +
FIFO SlidingWindow. Dream-cycle consolidate()
(episodic → semantic). Ebbinghaus forgetting curve
with reinforcement. Retriever blending cosine
similarity + recency + saturating frequency.
MemGpt three-tier shape (Main/Recall/Archival).
Co-Authored-By: Claude Opus 4.7 <[email protected]>
119 lines
3.5 KiB
Rust
119 lines
3.5 KiB
Rust
//! MemGPT-style hierarchical memory.
|
|
//!
|
|
//! Packer et al. 2023 ("MemGPT: Towards LLMs as Operating Systems")
|
|
//! split agent memory into three tiers:
|
|
//!
|
|
//! 1. **Main context** — always-resident; small, hot, in-prompt.
|
|
//! 2. **Recall** — recent overflow; paged in by reference.
|
|
//! 3. **Archival** — cold, large, retrievable by query.
|
|
//!
|
|
//! This module ties together [`Scratchpad`], [`SlidingWindow`] and
|
|
//! [`EpisodicLog`] into that three-tier shape.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::episodic::{Episode, EpisodicLog};
|
|
use crate::error::MemoryResult;
|
|
use crate::memory::EpisodeId;
|
|
use crate::window::{SlidingWindow, Turn};
|
|
use crate::working::Scratchpad;
|
|
|
|
/// MemGPT tier discriminator.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum Tier {
|
|
/// In-prompt working set.
|
|
Main,
|
|
/// Recent overflow.
|
|
Recall,
|
|
/// Cold long-term store.
|
|
Archival,
|
|
}
|
|
|
|
/// Configuration knobs for a [`MemGpt`] instance.
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct MemGptConfig {
|
|
/// Token budget for the main context scratchpad.
|
|
pub main_tokens: u32,
|
|
/// Turn capacity of the recall sliding window.
|
|
pub recall_turns: usize,
|
|
}
|
|
|
|
impl Default for MemGptConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
main_tokens: 2048,
|
|
recall_turns: 32,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Three-tier hierarchical memory bundle.
|
|
pub struct MemGpt {
|
|
/// Main-context scratchpad.
|
|
pub main: Scratchpad,
|
|
/// Recall sliding window.
|
|
pub recall: SlidingWindow,
|
|
/// Archival episodic log.
|
|
pub archival: EpisodicLog,
|
|
}
|
|
|
|
impl MemGpt {
|
|
/// Build a new MemGpt with the supplied config.
|
|
pub fn new(cfg: MemGptConfig) -> MemoryResult<Self> {
|
|
Ok(Self {
|
|
main: Scratchpad::new(cfg.main_tokens)?,
|
|
recall: SlidingWindow::new(cfg.recall_turns)?,
|
|
archival: EpisodicLog::new(),
|
|
})
|
|
}
|
|
|
|
/// Record a fresh conversation turn:
|
|
/// * `recall` gains the turn (oldest evicted on overflow).
|
|
/// * The evicted-out turn (if any) is appended to `archival`.
|
|
pub fn observe(&mut self, turn: Turn) -> MemoryResult<Option<EpisodeId>> {
|
|
let actor = turn.speaker.clone();
|
|
let text = turn.text.clone();
|
|
let ts = turn.timestamp_ms;
|
|
let evicted = self.recall.push(turn);
|
|
if let Some(old) = evicted {
|
|
let ep = Episode::new(old.timestamp_ms, old.speaker, old.text);
|
|
let id = self.archival.append(ep)?;
|
|
// The just-pushed turn metadata is also retained for the
|
|
// caller to inspect.
|
|
let _ = (actor, text, ts);
|
|
return Ok(Some(id));
|
|
}
|
|
Ok(None)
|
|
}
|
|
|
|
/// Manually promote a turn into the archival log (e.g. when the
|
|
/// agent calls `archive`).
|
|
pub fn archive(
|
|
&mut self,
|
|
speaker: impl Into<String>,
|
|
text: impl Into<String>,
|
|
timestamp_ms: u64,
|
|
) -> MemoryResult<EpisodeId> {
|
|
self.archival
|
|
.append(Episode::new(timestamp_ms, speaker, text))
|
|
}
|
|
|
|
/// Which tier currently holds an episode id, if any.
|
|
#[must_use]
|
|
pub fn locate(&self, id: &EpisodeId) -> Option<Tier> {
|
|
if self.archival.get(id).is_some() {
|
|
return Some(Tier::Archival);
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Lengths of each tier, useful for instrumentation.
|
|
#[must_use]
|
|
pub fn sizes(&self) -> (usize, usize, usize) {
|
|
(
|
|
self.main.slots().len(),
|
|
self.recall.turns().len(),
|
|
self.archival.all().len(),
|
|
)
|
|
}
|
|
}
|