Progress.Memory (Core)
The Memory .NET SDK is split into three NuGet packages so you take only the dependencies you need:
| Package | Main purpose | When to use |
|---|---|---|
| Progress.Memory | Core client for add/recall/forget memory operations. | You want direct programmatic control of memory calls. |
| Progress.Memory.Extensions.AI | Integration with Microsoft.Extensions.AI (IChatClient). | You are building with IChatClient and want middleware or tool-based memory. |
| Progress.Memory.SemanticKernel | Integration with Semantic Kernel (IKernelBuilder / IChatCompletionService). | You are using Semantic Kernel plugins, planners, or chat wrappers. |
Before configuring the SDK, request early access and prepare the Agent Memory prerequisites.
Installation
Install the core Memory package:
dotnet add package Progress.Memory
Configuration
Create a ProgressMemoryConfig record with the backend URL and SDK API key:
using Progress.Memory;
var config = new ProgressMemoryConfig(
BackendBaseUrl: "https://memory-api.observability.progress.com",
SdkApiKey: "pmem_d_...");
Environment Variables
If you register via IConfiguration, the SDK reads:
| Variable | Purpose |
|---|---|
PROGRESS_MEMORY_BACKEND_URL | Base URL of the memory-api service |
PROGRESS_MEMORY_SDK_API_KEY | SDK API key (for example, pmem_d_*) |
Dependency Injection
With explicit config:
using Progress.Memory;
using Progress.Memory.Extensions;
services.AddProgressMemory(new ProgressMemoryConfig(
BackendBaseUrl: "https://memory-api.observability.progress.com",
SdkApiKey: "pmem_d_..."));
From IConfiguration / environment variables:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Progress.Memory;
using Progress.Memory.Extensions;
var builder = Host.CreateApplicationBuilder(args);
// Set PROGRESS_MEMORY_BACKEND_URL and PROGRESS_MEMORY_SDK_API_KEY in your environment
builder.Services.AddProgressMemory(builder.Configuration);
var host = builder.Build();
var memory = host.Services.GetRequiredService<IProgressMemoryClient>();
API Reference
IProgressMemoryClient exposes three methods.
AddMemoryAsync
Extracts atomic memories from one or more conversation messages and saves them to the knowledge base.
Task<AddMemoryResult> AddMemoryAsync(AddMemoryRequest request, CancellationToken ct = default);
AddMemoryRequest
| Property | Type | Purpose |
|---|---|---|
Messages | List<MemoryMessage>? | Ordered conversation messages for memory extraction. At least one "user" message with text is required. Preferred over Text. |
Text | string | Single-text input. When Messages is not set, the SDK sends this as a single "user" message. |
ConversationId | string? | Optional conversation identifier to continue an existing thread. If omitted, the backend creates one. |
UserId | string | Identifies the user whose memory this belongs to. |
AgentId | string | Identifies the agent (scopes memory per agent per user). |
Metadata | Dictionary<string, string>? | Optional key/value pairs forwarded to the backend. |
MemoryMessage
| Property | Type | Purpose |
|---|---|---|
Text | string | Message text. |
Type | string | Message role: "user" or "assistant". |
AddMemoryResult
| Property | Type | Purpose |
|---|---|---|
OriginalTextId | string | Record ID for the submitted text. |
MemoriesCount | int | Number of atomic memories extracted and stored. |
ConversationId | string? | Conversation identifier returned by the backend for threading follow-up turns. |
Memories | IReadOnlyList<StoredMemory> | The individual extracted memories. |
StoredMemory
| Property | Type | Purpose |
|---|---|---|
MemoryId | string | Unique identifier for the memory. Pass to ForgetMemoryAsync to delete it. |
Text | string | The extracted atomic memory text. |
Category | string | Category assigned by the extraction pipeline (for example, FACT, PREFERENCE, or MEMORY). |
RecallMemoryAsync
Performs a search over stored memories for a given user/agent pair.
Task<IReadOnlyList<RecallResult>> RecallMemoryAsync(RecallMemoryRequest request, CancellationToken ct = default);
RecallMemoryRequest
| Property | Type | Purpose |
|---|---|---|
Query | string | Natural-language query describing what to look for. |
UserId | string | Scope results to this user. |
AgentId | string | Scope results to this agent. |
topK | int | Maximum number of results to return (default 10). |
IncludeOutdated | bool | Optional. Set to true to return both active and outdated memories. If omitted, defaults to false (active memories only). |
RecallResult
| Property | Type | Purpose |
|---|---|---|
MemoryId | string | Unique identifier for the memory. Pass to ForgetMemoryAsync to delete it. |
Text | string | The stored atomic memory text. |
Score | double | Semantic similarity score (0–1). Higher is more relevant. |
Category | string? | Category label assigned during extraction. |
OriginalTextId | string? | ID of the original text record this memory was derived from. |
Status | string? | Memory status: active or outdated. |
OutdatedById | string? | ID of the replacement memory when this memory is outdated. |
Metadata | Dictionary<string, object> | Additional metadata from the backend resource. |
ForgetMemoryAsync
Deletes a specific extracted memory by its ID. The original conversation text is not affected. userId must match the owner of the memory — passing a different user ID returns a 403 error.
Task ForgetMemoryAsync(string memoryId, string userId, CancellationToken ct = default);
ForgetMemoryAsync is idempotent — calling it for a memory that no longer exists returns successfully.
Standalone Usage Example
The following example creates a client, stores a conversation exchange, recalls relevant memories, and deletes a recalled memory:
using Progress.Memory;
using Progress.Memory.Models;
var config = new ProgressMemoryConfig(
BackendBaseUrl: "https://memory-api.observability.progress.com",
SdkApiKey: "pmem_d_...");
var memory = ProgressMemoryClient.Create(config);
// Save a conversation exchange for memory extraction
var result = await memory.AddMemoryAsync(new AddMemoryRequest
{
UserId = "user-123",
AgentId = "support-bot",
Messages =
[
new MemoryMessage { Type = "user", Text = "I prefer dark mode and metric units." },
new MemoryMessage { Type = "assistant", Text = "Noted — I'll remember your display and unit preferences." },
],
});
Console.WriteLine($"Extracted {result.MemoriesCount} memory(s):");
foreach (var item in result.Memories)
Console.WriteLine($" [{item.Category}] {item.Text} (id: {item.MemoryId})");
// Recall relevant memories before answering the next question
var memories = await memory.RecallMemoryAsync(new RecallMemoryRequest
{
Query = "display preferences",
UserId = "user-123",
AgentId = "support-bot",
topK = 5,
IncludeOutdated = false, // Optional; omit for active-only retrieval, or set to true to include outdated memories.
});
foreach (var m in memories)
Console.WriteLine($"[{m.Score:F2}] [{m.Category}] {m.Text}");
// Forget a specific memory
if (memories.Count > 0)
await memory.ForgetMemoryAsync(memories[0].MemoryId, "user-123");