# How ChocolateLMLite Memory Functionality Works for Long Conversations

> Explore how ChocolateLMLite's memory functionality enhances long conversations. Discover its persistent per-persona fact storage and LLM prompt injection for seamless interactions. Learn more.

- Repository: [Segment (gpsnmeajp)/chocolatelmlite](https://github.com/gpsnmeajp/chocolatelmlite)
- Tags: internals
- Published: 2026-03-02

---

**ChocolateLMLite implements persistent long-conversation memory by storing per-persona facts in a local YAML file that the LLM reads and updates through thread-safe file operations, injecting stored entries directly into the system prompt on every request.**

ChocolateLMLite provides a lightweight yet robust mechanism for maintaining context across extended dialogues through its persistent memory functionality. This architecture allows LLM-driven agents to remember facts, preferences, and history beyond the constraints of the active token window. According to the gpsnmeajp/chocolatelmlite source code, the implementation relies on a simple YAML-based storage layer with file-locking concurrency controls and seamless prompt injection.

## Memory Storage Architecture

The memory system persists data in a per-persona YAML file located at `data/persona_{id}/memory.yaml` within the application directory. Each persona maintains its own isolated memory space, ensuring that facts learned during one conversation do not leak into another persona's context.

The underlying data structure uses a `YamlMemory` object containing a list of `YamlMemoryEntry` instances. Every entry tracks four key properties:

- `Id`: Unique integer identifier for the entry
- `Text`: The actual memory content (capped at 500 characters for new entries)
- `CreatedAt`: Timestamp when the entry was first created
- `UpdatedAt`: Timestamp of the most recent modification

### Loading Memory from Disk

When the system needs to retrieve stored facts, `FileManager.GetActivePersonaMemory()` constructs the path to [`memory.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/memory.yaml) and deserializes the contents into a `YamlMemory` instance. If the file does not exist, the method returns an empty memory object with an initialized but empty entry list.

This operation occurs in [`src/FileManager.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/FileManager.cs) at lines 609-618, providing the foundation for both prompt construction and API responses.

## CRUD Operations and Thread Safety

All write operations pass through a centralized locking mechanism to prevent data corruption during concurrent LLM requests.

### Upserting Memory Entries

The `FileManager.UpsertActivePersonaMemory(int id, string newContent)` method handles both creation and updates in [`src/FileManager.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/FileManager.cs) (lines 624-664). The method logic follows these rules:

- When `id` equals 0, the system creates a new entry with an auto-incremented identifier
- When `id` matches an existing entry, the method overwrites the `Text` and `UpdatedAt` fields
- All changes persist immediately to disk via `SaveYaml`

### Deleting Entries

To remove specific facts, `FileManager.RemoveActivePersonaMemory(int id)` filters the entry list by the supplied identifier and rewrites the YAML file without the matching record. This implementation resides at lines 671-686 in [`src/FileManager.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/FileManager.cs).

### File Locking for Concurrency

Concurrent updates pose a risk when multiple LLM tool calls execute simultaneously. The `FileManager.WithMemoryLock(Func<Task<T>> action)` method implements a lock-file pattern using `memory.yaml.lock`:

1. Creates a temporary lock file and waits for existing locks to clear
2. Executes the supplied async lambda containing the read-modify-write operation
3. Deletes the lock file upon completion

This safeguard appears in [`src/FileManager.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/FileManager.cs) at lines 803-867, ensuring atomic memory updates even under high concurrency.

## Integrating Memory into LLM Prompts

Storage alone does not provide long-term context; the system must inject memories into the active prompt. During prompt construction, `SystemPrompt.BuildSystemPrompt` (lines 22-30 in [`src/SystemPrompt.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/SystemPrompt.cs)) retrieves the current memory entries and formats them as XML-style tags:

```xml
<system>
  <memory>ユーザーは猫が好きです</memory>
  <memory>猫は黒と白が好きです</memory>
</system>

```

The concatenated memory entries append directly to the system prompt, giving the model immediate access to persistent facts without consuming the sliding context window.

## API and Tool Interfaces

ChocolateLMLite exposes memory functionality through both REST endpoints and LLM-callable tools.

### REST API Endpoint

The web server provides read-only access via `GET /api/persona/active/memory`, implemented in [`src/WebServer.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/WebServer.cs) at lines 525-529. This endpoint returns the raw `YamlMemory` JSON, enabling external dashboards or debugging tools to inspect the current knowledge base:

```bash
curl http://localhost:8010/api/persona/active/memory

```

### LLM Tool Interface

The `UpdatePersonaMemory` tool in [`src/Tools.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/Tools.cs) (lines 15-42) serves as the primary interface for the LLM to modify its own memory. The tool accepts two parameters:

- `id`: 0 for new entries, or existing ID for updates
- `newContent`: The text to store (enforced 500-character limit for new entries)

When invoked, the tool delegates to `WithMemoryLock` and returns a confirmation string ("メモリを更新しました。") that the LLM can relay to users.

## Practical Implementation Examples

The following patterns demonstrate common memory operations using the ChocolateLMLite API.

**Retrieving current memory:**

```csharp
var memory = fileManager.GetActivePersonaMemory();
// memory.MemoryEntries is List<YamlMemoryEntry>

```

**Adding a new fact with concurrency protection:**

```csharp
await fileManager.WithMemoryLock(async () =>
{
    // id = 0 creates new entry
    return fileManager.UpsertActivePersonaMemory(0, "ユーザーは猫が好きです");
}, Program.cts.Token);

```

**Updating existing knowledge:**

```csharp
await fileManager.WithMemoryLock(async () =>
{
    return fileManager.UpsertActivePersonaMemory(3, "猫は黒と白が好きです");
}, Program.cts.Token);

```

**Removing outdated information:**

```csharp
await fileManager.WithMemoryLock(async () =>
{
    return fileManager.RemoveActivePersonaMemory(2);
}, Program.cts.Token);

```

**Tool call format for LLM integration:**

```json
{
  "function": "UpdatePersonaMemory",
  "arguments": {
    "id": 0,
    "newContent": "ユーザーは来週旅行に行く予定です"
  }
}

```

## Summary

- ChocolateLMLite stores long-conversation memory in per-persona YAML files (`data/persona_{id}/memory.yaml`) with `Id`, `Text`, `CreatedAt`, and `UpdatedAt` fields
- The `FileManager` class provides thread-safe CRUD operations through `GetActivePersonaMemory`, `UpsertActivePersonaMemory`, and `RemoveActivePersonaMemory`
- Concurrency protection uses file locking via `WithMemoryLock` to prevent race conditions during simultaneous updates
- Memories inject into the active prompt as XML `<memory>` tags through `SystemPrompt.BuildSystemPrompt`, ensuring the LLM retains context across turns
- External access is available via the REST endpoint `GET /api/persona/active/memory`, while LLM agents modify memory through the `UpdatePersonaMemory` tool with a 500-character content limit

## Frequently Asked Questions

### Where does ChocolateLMLite store conversation memory?

ChocolateLMLite persists memory in a YAML file named [`memory.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/memory.yaml) located inside the active persona's data directory at `data/persona_{id}/`. This file stores a serialized `YamlMemory` object containing a list of entries, ensuring data survives server restarts and remains isolated per persona.

### How does the system prevent memory corruption during concurrent updates?

The implementation uses `FileManager.WithMemoryLock`, which creates a temporary lock file (`memory.yaml.lock`) before executing any read-modify-write operations. This mechanism queues concurrent requests and processes them sequentially, preventing race conditions when multiple LLM tool calls attempt simultaneous modifications.

### What is the maximum length for a memory entry?

The `UpdatePersonaMemory` tool enforces a 500-character limit on new content to prevent the system prompt from growing excessively large. This constraint appears in [`src/Tools.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/Tools.cs) and ensures that injected `<memory>` tags do not consume the majority of the model's available context window.

### How does the LLM access stored memories during a conversation?

During prompt construction, `SystemPrompt.BuildSystemPrompt` retrieves all current entries via `GetActivePersonaMemory` and formats them as XML-style `<memory>` tags appended to the system prompt. This injection happens on every request, giving the model immediate access to persistent facts without requiring explicit retrieval tool calls.