// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.AI.Agents;
///
/// A base class for agent threads that operate entirely in memory without external storage.
///
public abstract class InMemoryAgentThread : AgentThread
{
///
/// Initializes a new instance of the class.
///
/// An optional to use for storing chat messages. If null, a new instance will be created.
protected InMemoryAgentThread(InMemoryChatMessageStore? messageStore = null)
{
this.MessageStore = messageStore ?? [];
}
///
/// Initializes a new instance of the class with the specified initial messages.
///
/// The messages to initialize the thread with.
protected InMemoryAgentThread(IEnumerable messages)
{
this.MessageStore = [.. messages];
}
///
/// Initializes a new instance of the class from serialized state.
///
/// A representing the serialized state of the thread.
/// Optional settings for customizing the JSON deserialization process.
/// A factory function to create the from its serialized state.
/// The is not a JSON object.
/// The is invalid or cannot be deserialized to the expected type.
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.
/// The to monitor for cancellation requests. The default is .
/// A representation of the object's state.
public override async Task SerializeAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
var storeState = await this.MessageStore.SerializeStateAsync(jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
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);
///
protected internal override Task MessagesReceivedAsync(IEnumerable newMessages, CancellationToken cancellationToken = default)
=> this.MessageStore.AddMessagesAsync(newMessages, cancellationToken);
internal sealed class InMemoryAgentThreadState
{
public JsonElement? StoreState { get; set; }
}
}