// 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; }
}
}