All docs

Memory

Munin has two memory layers. They solve different problems and are both local.

Vector memory

MemoryService stores semantically searchable snippets from chat messages. The production implementation is FlutterGemmaMemoryService.

The implementation uses:

  • Gecko embeddings through flutter_gemma.
  • SqliteVectorStore from flutter_gemma_rag_sqlite.
  • A local database named munin_memory.db in the application documents directory.

Every remembered message stores:

  • The message id as document id.
  • The message text as document content.
  • The embedding vector.
  • Metadata containing conversation id and sender flag.

Initialization

Memory initializes lazily through _ensureReady. Startup also calls warmUp() after the first frame so the first real turn does not pay the full embedder and store setup cost.

If the embedder or vector store cannot initialize, memory marks itself unavailable and all methods become safe no-ops. Chat continues without memory.

Remembering messages

ChatController calls memory.remember for user messages after they are saved. It also remembers assistant replies after finalization.

The remember path:

  1. Trim text.
  2. Ensure embedder and vector store are ready.
  3. Generate an embedding.
  4. Add a document to the vector store.
  5. Ignore individual failures so a failed embed does not break chat.

Recalling messages

Before a model reply, ChatController calls memory.recall(query) with a timeout. The service embeds the query and searches similar vectors with:

  • topK: default 6.
  • threshold: 0.35.

The controller injects up to three recalled snippets into the prompt, skipping the current user message and duplicate content.

Profile memory

MemoryProfileService maintains durable user facts. These facts are stored in SharedPreferences under StorageKeys.memoryFacts.

Profile memory exists because vector search alone can miss direct identity questions. For example, a query like "what is my name" may not be close enough to the original message where the user introduced themselves. Profile facts are always injected into every turn.

Learning profile facts

After a successful assistant reply, the controller calls MemoryProfileService.learnFrom asynchronously.

The utility model receives one exchange and extracts lasting facts only:

  • Name.
  • Location.
  • Job or role.
  • Stable preferences.
  • Ongoing projects.
  • Goals.
  • Important relationships.

Transient task details are ignored. The model must return a JSON array of short third-person strings.

Merging facts

The profile service deduplicates and updates facts:

  • Single-valued attributes such as name, age, location, and job replace older facts about the same attribute.
  • Near-duplicate facts collapse using significant-word overlap.
  • Multi-valued preferences and projects can accumulate.
  • The list is capped at 40 facts.

User management

The memory settings page lets users:

  • Search saved facts.
  • Add a fact manually.
  • Remove one fact.
  • Forget all saved facts.

Wiping profile memory does not delete conversations. Clearing chats is handled separately.

Forgetting vector memory

The MemoryService contract supports:

  • forgetMessages for messages deleted with a conversation.
  • forgetAll for full memory wipe.

These operations are defensive. If the vector store is unavailable, they return without crashing.