diff --git a/src/Microsoft.AI.Agents.Orleans/MemoryBank.cs b/src/Microsoft.AI.Agents.Orleans/MemoryBank.cs new file mode 100644 index 00000000..89744f5f --- /dev/null +++ b/src/Microsoft.AI.Agents.Orleans/MemoryBank.cs @@ -0,0 +1,82 @@ +using Microsoft.AI.Agents.Abstractions; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using Orleans.Runtime; +using Orleans.Streams; + +namespace Microsoft.AI.Agents.Orleans; + +public abstract class MemoryBank : Grain, IGrainWithStringKey, IMemoryBank +{ + protected virtual string Namespace { get; set; } + protected readonly Kernel _kernel; + public MemoryBank( + [PersistentState("memoryBanks", "memoryBanks")] IPersistentState memory, + Kernel kernel) + { + Memory = memory; + _kernel = kernel; + } + public string Name { get; set; } + public IPersistentState Memory { get; set; } + public abstract Task ConsolidateMemory(); + + public abstract Task ExtractMemory(Event item); + + public async Task HandleEvent(Event item) + { + await ProcessEvent(item); + } + + public async Task ProcessEvent(Event item) + { + var memory = await ExtractMemory(item); + if (memory != default) + { + if (Memory.State.Events == null) Memory.State.Events = new List(); + if (Memory.State.Memories == null) Memory.State.Memories = new List(); + Memory.State.Events.Add(item); + Memory.State.Memories.Add(memory); + var summary = await ConsolidateMemory(); + Memory.State.Summary = summary; + await Memory.WriteStateAsync(); + } + } + + public async Task PublishEvent(string ns, string id, Event item) + { + var streamProvider = this.GetStreamProvider("StreamProvider"); + var streamId = StreamId.Create(ns, id); + var stream = streamProvider.GetStream(streamId); + await stream.OnNextAsync(item); + } + private async Task HandleEvent(Event item, StreamSequenceToken? token) + { + await HandleEvent(item); + } + + public async override Task OnActivateAsync(CancellationToken cancellationToken) + { + var streamProvider = this.GetStreamProvider("StreamProvider"); + var streamId = StreamId.Create(Namespace, this.GetPrimaryKeyString()); + var stream = streamProvider.GetStream(streamId); + await stream.SubscribeAsync(HandleEvent); + } + + protected async Task CallFunction(string template, KernelArguments arguments, OpenAIPromptExecutionSettings? settings = null) + { + var propmptSettings = settings ?? new OpenAIPromptExecutionSettings { MaxTokens = 4096, Temperature = 0.5, TopP = 0.8 }; + var function = _kernel.CreateFunctionFromPrompt(template, propmptSettings); + var result = (await _kernel.InvokeAsync(function, arguments)).ToString(); + return result; + } + + public virtual Task Recall() + { + return Task.FromResult(Memory.State.Summary); + } + public Task IntrospectMemory() + { + return Task.FromResult(Memory.State); + } +} diff --git a/src/Microsoft.AI.Agents.Orleans/MemorySurrogate.cs b/src/Microsoft.AI.Agents.Orleans/MemorySurrogate.cs new file mode 100644 index 00000000..ffbdaefc --- /dev/null +++ b/src/Microsoft.AI.Agents.Orleans/MemorySurrogate.cs @@ -0,0 +1,34 @@ +using Microsoft.AI.Agents.Abstractions; + +namespace Microsoft.AI.Agents.Orleans; + + +[GenerateSerializer] +public struct MemoryItemSurrogate +{ + [Id(0)] + public Guid Id { get; set; } + [Id(1)] + public string Memory { get; set; } + [Id(2)] + public List Tags { get; set; } +} + +[RegisterConverter] +public sealed class MemoryItemSurrogateConverter : + IConverter +{ + public MemoryItem ConvertFromSurrogate( + in MemoryItemSurrogate surrogate) => new MemoryItem { + Id = surrogate.Id, + Memory = surrogate.Memory, + Tags = surrogate.Tags + }; + + public MemoryItemSurrogate ConvertToSurrogate(in MemoryItem value) => + new MemoryItemSurrogate { + Id = value.Id, + Memory = value.Memory, + Tags = value.Tags + }; +} diff --git a/src/Microsoft.AI.Agents/Abstractions/IMemoryBank.cs b/src/Microsoft.AI.Agents/Abstractions/IMemoryBank.cs new file mode 100644 index 00000000..cfe3f21c --- /dev/null +++ b/src/Microsoft.AI.Agents/Abstractions/IMemoryBank.cs @@ -0,0 +1,11 @@ +namespace Microsoft.AI.Agents.Abstractions; + +public interface IMemoryBank : IAgent +{ + public Task ProcessEvent(Event item); + public Task ExtractMemory(Event item); + public Task ConsolidateMemory(); + public Task Recall(); + public Task IntrospectMemory(); + +} diff --git a/src/Microsoft.AI.Agents/Abstractions/MemoryBankState.cs b/src/Microsoft.AI.Agents/Abstractions/MemoryBankState.cs new file mode 100644 index 00000000..ed068e2b --- /dev/null +++ b/src/Microsoft.AI.Agents/Abstractions/MemoryBankState.cs @@ -0,0 +1,8 @@ +namespace Microsoft.AI.Agents.Abstractions; + +public class MemoryBankState +{ + public List Memories { get; set; } + public List Events { get; set; } + public string Summary { get; set; } +} \ No newline at end of file diff --git a/src/Microsoft.AI.Agents/Abstractions/MemoryItem.cs b/src/Microsoft.AI.Agents/Abstractions/MemoryItem.cs new file mode 100644 index 00000000..3bd0b1e4 --- /dev/null +++ b/src/Microsoft.AI.Agents/Abstractions/MemoryItem.cs @@ -0,0 +1,8 @@ +namespace Microsoft.AI.Agents.Abstractions; + +public class MemoryItem +{ + public Guid Id { get; set; } + public string Memory { get; set; } + public List Tags { get; set; } +}