Skip to content

v0.35.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 02:35

Added

  • Memory Indexer takes a Microsoft.Extensions.AI embedding generator directly. AddMemoryIndexer(...) .WithEmbeddingGenerator(queriesServiceKey) embeds with the container's IEmbeddingGenerator<string, Embedding<float>>
    (OpenAI, Azure OpenAI, Ollama, LMSupply, IronHive) — no IEmbeddingService to write; it replaces any embedding service
    registered before it. A keyed generator embeds recall queries (an asymmetric model's query side); without one the
    documents generator does. EmbeddingGeneratorEmbeddingService is the same thing without DI. The dimension is the
    generator's (EmbeddingGeneratorMetadata.DefaultModelDimensions) or the one passed; neither known, or the two
    disagreeing, throws; batches are one generator call and a short result or a vector of another size throws.
    MemoryIndexer now references Microsoft.Extensions.AI.Abstractions (it was already in the dependency closure).
  • VectorStoreMemoryStore stores memories in any Microsoft.Extensions.VectorData database — Qdrant, PostgreSQL
    (pgvector), Azure AI Search and the other connectors — through the connector's VectorStore, with the embedding
    dimension set in VectorStoreMemoryStoreOptions.Dimensions (also CollectionName, DistanceFunction, PageSize).
    Rows are MemoryRecords (enums by name, times as UTC ticks, metadata as JSON plus key=value entries); every filter runs
    in the database; search scores are the cosine similarity of the stored vectors, as in the other stores. Counts, the user
    list and bulk deletes walk the matching records page by page. The collection is created on first use (a read before
    any write used to fail on Qdrant with «collection not found»). A memory without an embedding is stored with a
    placeholder vector and MemoryRecord.HasEmbedding = false, which every search filters out — Qdrant refuses a point
    without a vector, and the library stores some memories (imports, summaries, backups) before or without embedding them.
    The store contract runs on a real Qdrant (CommunityToolkit.VectorData.Qdrant 1.0.1, Testcontainers).
  • MemoryIndexer:Storage:Type = VectorStore and WithVectorStore(collectionName) register that store on the
    VectorStore the application registered (collection MemoryIndexer:Storage:CollectionName, default memories;
    dimension as for SQLite). Without one, resolving the store fails with a message that names it.

Dependencies

  • Microsoft.Extensions.VectorData.Abstractions 10.6.0 → 10.11.0 (the maintained connectors require 10.8.2 or later).

Documentation

  • docs/QUALITY-BENCHMARKS.md names the public benchmark for recall quality: LongMemEval, retrieval only
    (session- and turn-level Recall@k / NDCG@10, by question type), and what no public benchmark isolates.

Changed

  • Breaking — one vector size: the built-in stores take it from the embedding service. The SQLite and
    VectorData stores used MemoryIndexer:Storage:VectorDimensions (default 768, or Embedding:Dimensions when 0) while the
    embedder reported its own IEmbeddingService.Dimensions — two settings for one number, and a third with a
    Microsoft.Extensions.AI generator; a mismatch failed at the first write. The stores now read
    IEmbeddingService.Dimensions, so a store cannot be resolved without an embedding service. StorageOptions.VectorDimensions
    is removed, and a configuration that still sets MemoryIndexer:Storage:VectorDimensions stops the host at startup
    naming where the size comes from. Embedding:Dimensions stays the size of the built-in embedders (Mock, the MCP
    server's local model). Migration: delete Storage:VectorDimensions; with your own embedding service, its Dimensions
    is the size.
  • MemoryUnit no longer carries Microsoft.Extensions.VectorData attributes ([VectorStoreKey], [VectorStoreData],
    [VectorStoreVector(1024)]). Nothing read them, and the vector attribute fixed a 1024 dimension in code; the schema is now
    MemoryRecord.CreateDefinition(dimensions, …). Code that used MemoryUnit as a VectorData record type maps through
    MemoryRecord.From / ToMemory instead.
  • Breaking — one setting per concept for the Short tier capacity and the Long → Archive gate. The Short tier's
    session cache (IShortTermMemory) had its own capacity (MemoryIndexer:VCM:WorkingMemory:Capacity, default 7) next to the
    tier limit the store enforces (MemoryIndexer:WorkingMemory:Capacity, default 9), and the Long → Archive promoter read
    its confidence/confirmation gate from MemoryIndexer:VCM:SemanticStore while the tier manager read
    MemoryIndexer:ArchivePromotion — setting one left the other path on its default. Now WorkingMemory:Capacity sizes
    both (the cache follows the tier: 9 by default, was 7) and ArchivePromotion is the only gate. A configuration that
    still sets one of the three old keys stops the host at startup naming the new key. Migration: move those keys;
    MemoryIndexer.Services.WorkingMemoryOptions is now WorkingMemoryCacheOptions (no Capacity);
    SemanticStoreOptions loses MinConfirmationCount/MinConfidenceThreshold; ShortTermMemoryService and
    LongTermPromoterService take IOptions<MemoryIndexerOptions> for the values that moved.