I started BlazorMemory in January because I wanted an AI chat assistant in Blazor that remembered things. I looked for a .NET library that did this. There wasn't one. Everything in the space was Python, and everything assumed you had a server and a vector database.
Blazor WASM has neither. So I built it.
v1.0.0 shipped this week. Here is what is in it, and more usefully, what I would do differently.
What 1.0 includes
Four storage backends. IndexedDB runs in the browser with no backend at all, which is the case I originally built this for. EF Core covers SQL Server and SQLite. pgvector handles PostgreSQL with native vector search and an HNSW index. InMemory is for tests.
Four AI providers. OpenAI, Anthropic, Azure OpenAI, Ollama. Each one works for both embeddings and fact extraction, and they mix freely. You can run Ollama embeddings with a Claude extractor if you want.
The Ollama combination is the one I like most:
builder.Services
.AddBlazorMemory()
.UseIndexedDbStorage()
.UseOllamaEmbeddings()
.UseOllamaExtractor();
No API key. No cost per request. Everything local. For development this removes the last reason not to try the library.
A drop-in UI component. One line of markup gives you a panel with the stored memories, delete and clear, export and import, thumbs up and down feedback, and a mode toggle:
<MemoryPanel UserId="@userId" IsOpen="true" />
A memory graph. Memories render as nodes, connected by edges where the cosine similarity crosses a threshold. Vanilla JS force simulation, no D3, no external libraries:
<MemoryGraph UserId="@userId" SimilarityThreshold="0.65f" />
Semantic Kernel integration. BlazorMemory implements SK's IMemoryStore, so it drops into an existing SK app without changing anything else.
Multi-agent shared memory. Several agents share one memory pool. Each agent writes to its own namespace but reads across all of them, so agents see what other agents learned.
Three things I got wrong
The storage interface changed three times. First when I added namespaces, then again when I added importance scores. Each change meant touching every adapter, every test, every extension method. If I had used an options object from the start instead of positional parameters, those would have been additive changes instead of breaking ones.
Consolidation was harder than everything else combined. Storing memories is easy. Not storing forty near-duplicate memories is not. Early versions produced "User works in software," "User is a developer," and "User writes code" as three separate entries.
The fix was a priority order in the consolidation prompt: NONE first, then UPDATE, then DELETE, then ADD. The model has to justify adding something new rather than defaulting to it. That single reordering did more for memory quality than any amount of prompt polish.
I trusted local models to return clean JSON. The Ollama extractor worked in testing and failed constantly in practice, because llama3.2 wraps its JSON in markdown fences roughly half the time and sometimes adds a sentence of commentary first. The parse failed silently and extraction returned nothing.
Now the extractor strips code fences, hunts for the first bracket and its match, and retries once with a stricter prompt if the parse fails. Obvious in hindsight. Cost me a version to notice.
What I would tell someone starting this
Ship small and often. Fourteen packages sounds like a lot but no single release was large. v0.2 added namespaces. v0.3 added export and import. v0.5 added thumbs up and down. Each one took a day or two and each one gave me something to write about.
The writing mattered more than I expected. Downloads spiked after every article, not after every release. The releases without a post attached barely moved the numbers.
Getting it
dotnet add package BlazorMemory
dotnet add package BlazorMemory.Components
dotnet add package BlazorMemory.Storage.IndexedDb
dotnet add package BlazorMemory.Embeddings.Ollama
dotnet add package BlazorMemory.Extractor.Ollama
14 packages, 132 tests, MIT licensed.
Top comments (0)