// 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 an that maintain all chat history in local memory.
///
///
///
/// is designed for scenarios where chat history 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 will be created.
///
///
/// This constructor allows sharing of between threads or providing pre-configured
/// with specific reduction or processing logic.
///
protected InMemoryAgentThread(InMemoryChatHistoryProvider? chatHistoryProvider = null)
{
this.ChatHistoryProvider = chatHistoryProvider ?? [];
}
///
/// 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.ChatHistoryProvider = [.. 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 .
///
/// 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? chatHistoryProviderFactory = 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.ChatHistoryProvider =
chatHistoryProviderFactory?.Invoke(state?.ChatHistoryProviderState ?? default, jsonSerializerOptions) ??
new InMemoryChatHistoryProvider(state?.ChatHistoryProviderState ?? default, jsonSerializerOptions);
}
///
/// Gets or sets the used by this thread.
///
public InMemoryChatHistoryProvider ChatHistoryProvider { 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 chatHistoryProviderState = this.ChatHistoryProvider.Serialize(jsonSerializerOptions);
var state = new InMemoryAgentThreadState
{
ChatHistoryProviderState = chatHistoryProviderState,
};
return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(InMemoryAgentThreadState)));
}
///
public override object? GetService(Type serviceType, object? serviceKey = null) =>
base.GetService(serviceType, serviceKey) ?? this.ChatHistoryProvider?.GetService(serviceType, serviceKey);
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay => $"Count = {this.ChatHistoryProvider.Count}";
internal sealed class InMemoryAgentThreadState
{
public JsonElement? ChatHistoryProviderState { get; set; }
}
}