// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.AI; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// /// Provides an abstract base class for storing and managing chat messages associated with agent conversations. /// /// /// /// defines the contract for persistent storage of chat messages in agent conversations. /// Implementations are responsible for managing message persistence, retrieval, and any necessary optimization /// strategies such as truncation, summarization, or archival. /// /// /// Key responsibilities include: /// /// Storing chat messages with proper ordering and metadata preservation /// Retrieving messages in chronological order for agent context /// Managing storage limits through truncation, summarization, or other strategies /// Supporting serialization for thread persistence and migration /// /// /// public abstract class ChatMessageStore { /// /// Called at the start of agent invocation to retrieve all messages from the store that should be provided as context for the next agent invocation. /// /// Contains the request context including the caller provided messages that will be used by the agent for this invocation. /// The to monitor for cancellation requests. The default is . /// /// A task that represents the asynchronous operation. The task result contains a collection of /// instances in ascending chronological order (oldest first). /// /// /// /// Messages are returned in chronological order to maintain proper conversation flow and context for the agent. /// The oldest messages appear first in the collection, followed by more recent messages. /// /// /// If the total message history becomes very large, implementations should apply appropriate strategies to manage /// storage constraints, such as: /// /// Truncating older messages while preserving recent context /// Summarizing message groups to maintain essential context /// Implementing sliding window approaches for message retention /// Archiving old messages while keeping active conversation context /// /// /// /// Each store instance should be associated with a single conversation thread to ensure proper message isolation /// and context management. /// /// public abstract ValueTask> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default); /// /// Called at the end of the agent invocation to add new messages to the store. /// /// Contains the invocation context including request messages, response messages, and any exception that occurred. /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous add operation. /// /// /// Messages should be added in the order they were generated to maintain proper chronological sequence. /// The store is responsible for preserving message ordering and ensuring that subsequent calls to /// return messages in the correct chronological order. /// /// /// Implementations may perform additional processing during message addition, such as: /// /// Validating message content and metadata /// Applying storage optimizations or compression /// Triggering background maintenance operations /// Updating indices or search capabilities /// /// /// /// This method is called regardless of whether the invocation succeeded or failed. /// To check if the invocation was successful, inspect the property. /// /// public abstract ValueTask InvokedAsync(InvokedContext context, CancellationToken cancellationToken = default); /// /// 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 abstract JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null); /// Asks the for an object of the specified type . /// The type of object being requested. /// An optional key that can be used to help identify the target service. /// The found object, otherwise . /// is . /// /// The purpose of this method is to allow for the retrieval of strongly-typed services that might be provided by the , /// including itself or any services it might be wrapping. /// public virtual object? GetService(Type serviceType, object? serviceKey = null) { _ = Throw.IfNull(serviceType); return serviceKey is null && serviceType.IsInstanceOfType(this) ? this : null; } /// Asks the for an object of type . /// The type of the object to be retrieved. /// An optional key that can be used to help identify the target service. /// The found object, otherwise . /// /// The purpose of this method is to allow for the retrieval of strongly typed services that may be provided by the , /// including itself or any services it might be wrapping. /// public TService? GetService(object? serviceKey = null) => this.GetService(typeof(TService), serviceKey) is TService service ? service : default; /// /// Contains the context information provided to . /// /// /// This class provides context about the invocation before the messages are retrieved from the store, /// including the new messages that will be used. Stores can use this information to determine what /// messages should be retrieved for the invocation. /// public sealed class InvokingContext { /// /// Initializes a new instance of the class with the specified request messages. /// /// The new messages to be used by the agent for this invocation. /// is . public InvokingContext(IEnumerable requestMessages) { this.RequestMessages = requestMessages ?? throw new ArgumentNullException(nameof(requestMessages)); } /// /// Gets the caller provided messages that will be used by the agent for this invocation. /// /// /// A collection of instances representing new messages that were provided by the caller. /// public IEnumerable RequestMessages { get; } } /// /// Contains the context information provided to . /// /// /// This class provides context about a completed agent invocation, including both the /// request messages that were used and the response messages that were generated. It also indicates /// whether the invocation succeeded or failed. /// public sealed class InvokedContext { /// /// Initializes a new instance of the class with the specified request messages. /// /// The caller provided messages that were used by the agent for this invocation. /// The messages retrieved from the for this invocation. /// is . public InvokedContext(IEnumerable requestMessages, IEnumerable chatMessageStoreMessages) { this.RequestMessages = Throw.IfNull(requestMessages); this.ChatMessageStoreMessages = chatMessageStoreMessages; } /// /// Gets the caller provided messages that were used by the agent for this invocation. /// /// /// A collection of instances representing new messages that were provided by the caller. /// This does not include any supplied messages. /// public IEnumerable RequestMessages { get; } /// /// Gets the messages retrieved from the for this invocation, if any. /// /// /// A collection of instances that were retrieved from the , /// and were used by the agent as part of the invocation. /// public IEnumerable ChatMessageStoreMessages { get; } /// /// Gets or sets the messages provided by the for this invocation, if any. /// /// /// A collection of instances that were provided by the , /// and were used by the agent as part of the invocation. /// public IEnumerable? AIContextProviderMessages { get; set; } /// /// Gets the collection of response messages generated during this invocation if the invocation succeeded. /// /// /// A collection of instances representing the response, /// or if the invocation failed or did not produce response messages. /// public IEnumerable? ResponseMessages { get; set; } /// /// Gets the that was thrown during the invocation, if the invocation failed. /// /// /// The exception that caused the invocation to fail, or if the invocation succeeded. /// public Exception? InvokeException { get; set; } } }