// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Text.Json; using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; /// /// Provides an abstract base class for agent threads that maintain all conversation state in local memory. /// /// /// /// is designed for scenarios where conversation state should be stored locally /// rather than in external services or databases. This approach provides high performance and simplicity while /// maintaining full control over the conversation data. /// /// /// In-memory threads do not persist conversation data across application restarts /// unless explicitly serialized and restored. /// /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public abstract class InMemoryAgentThread : AgentThread { /// /// Initializes a new instance of the class. /// /// /// An optional instance to use for storing chat messages. /// If , a new empty message store will be created. /// /// /// This constructor allows sharing of message stores between threads or providing pre-configured /// message stores with specific reduction or processing logic. /// protected InMemoryAgentThread(InMemoryChatMessageStore? messageStore = null) { this.MessageStore = messageStore ?? []; } /// /// Initializes a new instance of the class. /// /// The initial messages to populate the conversation history. /// is . /// /// This constructor is useful for initializing threads with existing conversation history or /// for migrating conversations from other storage systems. /// protected InMemoryAgentThread(IEnumerable messages) { this.MessageStore = [.. messages]; } /// /// Initializes a new instance of the class from previously serialized state. /// /// A representing the serialized state of the thread. /// Optional settings for customizing the JSON deserialization process. /// /// Optional factory function to create the from its serialized state. /// If not provided, a default factory will be used that creates a basic in-memory store. /// /// The is not a JSON object. /// The is invalid or cannot be deserialized to the expected type. /// /// This constructor enables restoration of in-memory threads from previously saved state, allowing /// conversations to be resumed across application restarts or migrated between different instances. /// protected InMemoryAgentThread( JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null, Func? messageStoreFactory = null) { if (serializedThreadState.ValueKind != JsonValueKind.Object) { throw new ArgumentException("The serialized thread state must be a JSON object.", nameof(serializedThreadState)); } var state = serializedThreadState.Deserialize( AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(InMemoryAgentThreadState))) as InMemoryAgentThreadState; this.MessageStore = messageStoreFactory?.Invoke(state?.StoreState ?? default, jsonSerializerOptions) ?? new InMemoryChatMessageStore(state?.StoreState ?? default, jsonSerializerOptions); } /// /// Gets or sets the used by this thread. /// public InMemoryChatMessageStore MessageStore { get; } /// /// Serializes the current object's state to a using the specified serialization options. /// /// The JSON serialization options to use. /// A representation of the object's state. public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { var storeState = this.MessageStore.Serialize(jsonSerializerOptions); var state = new InMemoryAgentThreadState { StoreState = storeState, }; return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(InMemoryAgentThreadState))); } /// public override object? GetService(Type serviceType, object? serviceKey = null) => base.GetService(serviceType, serviceKey) ?? this.MessageStore?.GetService(serviceType, serviceKey); [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => $"Count = {this.MessageStore.Count}"; internal sealed class InMemoryAgentThreadState { public JsonElement? StoreState { get; set; } } }