Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,18 @@ protected override async Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessag
}

// Get existing messages from the store
var invokingContext = new ChatMessageStore.InvokingContext(messages);
var storeMessages = await typedThread.MessageStore.InvokingAsync(invokingContext, cancellationToken);
var invokingContext = new ChatHistoryProvider.InvokingContext(messages);
var storeMessages = await typedThread.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken);

// Clone the input messages and turn them into response messages with upper case text.
List<ChatMessage> responseMessages = CloneAndToUpperCase(messages, this.Name).ToList();

// Notify the thread of the input and output messages.
var invokedContext = new ChatMessageStore.InvokedContext(messages, storeMessages)
var invokedContext = new ChatHistoryProvider.InvokedContext(messages, storeMessages)
{
ResponseMessages = responseMessages
};
await typedThread.MessageStore.InvokedAsync(invokedContext, cancellationToken);
await typedThread.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken);

return new AgentResponse
{
Expand All @@ -77,18 +77,18 @@ protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingA
}

// Get existing messages from the store
var invokingContext = new ChatMessageStore.InvokingContext(messages);
var storeMessages = await typedThread.MessageStore.InvokingAsync(invokingContext, cancellationToken);
var invokingContext = new ChatHistoryProvider.InvokingContext(messages);
var storeMessages = await typedThread.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken);

// Clone the input messages and turn them into response messages with upper case text.
List<ChatMessage> responseMessages = CloneAndToUpperCase(messages, this.Name).ToList();

// Notify the thread of the input and output messages.
var invokedContext = new ChatMessageStore.InvokedContext(messages, storeMessages)
var invokedContext = new ChatHistoryProvider.InvokedContext(messages, storeMessages)
{
ResponseMessages = responseMessages
};
await typedThread.MessageStore.InvokedAsync(invokedContext, cancellationToken);
await typedThread.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken);

foreach (var message in responseMessages)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
// Since we are using ChatCompletion which stores chat history locally, we can also add a message removal policy
// that removes messages produced by the TextSearchProvider before they are added to the chat history, so that
// we don't bloat chat history with all the search result messages.
ChatMessageStoreFactory = (ctx, ct) => new ValueTask<ChatMessageStore>(new InMemoryChatMessageStore(ctx.SerializedState, ctx.JsonSerializerOptions)
ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(new InMemoryChatHistoryProvider(ctx.SerializedState, ctx.JsonSerializerOptions)
.WithAIContextProviderMessageRemoval()),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@
{
ChatOptions = new() { Instructions = "You are good at telling jokes." },
Name = "Joker",
ChatMessageStoreFactory = (ctx, ct) => new ValueTask<ChatMessageStore>(
// Create a new chat message store for this agent that stores the messages in a vector store.
// Each thread must get its own copy of the VectorChatMessageStore, since the store
// also contains the id that the thread is stored under.
new VectorChatMessageStore(vectorStore, ctx.SerializedState, ctx.JsonSerializerOptions))
ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(
// Create a new ChatHistoryProvider for this agent that stores chat history in a vector store.
// Each thread must get its own copy of the VectorChatHistoryProvider, since the provider
// also contains the id that the chat history is stored under.
new VectorChatHistoryProvider(vectorStore, ctx.SerializedState, ctx.JsonSerializerOptions))
});

// Start a new thread for the agent conversation.
AgentThread thread = await agent.GetNewThreadAsync();

// Run the agent with the thread that stores conversation history in the vector store.
// Run the agent with the thread that stores chat history in the vector store.
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread));

// Serialize the thread state, so it can be stored for later use.
Expand All @@ -58,30 +58,30 @@
// Deserialize the thread state after loading from storage.
AgentThread resumedThread = await agent.DeserializeThreadAsync(serializedThread);

// Run the agent with the thread that stores conversation history in the vector store a second time.
// Run the agent with the thread that stores chat history in the vector store a second time.
Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedThread));

// We can access the VectorChatMessageStore via the thread's GetService method if we need to read the key under which threads are stored.
var messageStore = resumedThread.GetService<VectorChatMessageStore>()!;
Console.WriteLine($"\nThread is stored in vector store under key: {messageStore.ThreadDbKey}");
// We can access the VectorChatHistoryProvider via the thread's GetService method if we need to read the key under which chat history is stored.
var chatHistoryProvider = resumedThread.GetService<VectorChatHistoryProvider>()!;
Console.WriteLine($"\nThread is stored in vector store under key: {chatHistoryProvider.ThreadDbKey}");

namespace SampleApp
{
/// <summary>
/// A sample implementation of <see cref="ChatMessageStore"/> that stores chat messages in a vector store.
/// A sample implementation of <see cref="ChatHistoryProvider"/> that stores chat history in a vector store.
/// </summary>
internal sealed class VectorChatMessageStore : ChatMessageStore
internal sealed class VectorChatHistoryProvider : ChatHistoryProvider
{
private readonly VectorStore _vectorStore;

public VectorChatMessageStore(VectorStore vectorStore, JsonElement serializedStoreState, JsonSerializerOptions? jsonSerializerOptions = null)
public VectorChatHistoryProvider(VectorStore vectorStore, JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null)
{
this._vectorStore = vectorStore ?? throw new ArgumentNullException(nameof(vectorStore));

if (serializedStoreState.ValueKind is JsonValueKind.String)
if (serializedState.ValueKind is JsonValueKind.String)
{
// Here we can deserialize the thread id so that we can access the same messages as before the suspension.
this.ThreadDbKey = serializedStoreState.Deserialize<string>();
this.ThreadDbKey = serializedState.Deserialize<string>();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
{
ChatOptions = new() { Instructions = "You are good at telling jokes." },
Name = "Joker",
ChatMessageStoreFactory = (ctx, ct) => new ValueTask<ChatMessageStore>(new InMemoryChatMessageStore(new MessageCountingChatReducer(2), ctx.SerializedState, ctx.JsonSerializerOptions))
ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(new InMemoryChatHistoryProvider(new MessageCountingChatReducer(2), ctx.SerializedState, ctx.JsonSerializerOptions))
});

AgentThread thread = await agent.GetNewThreadAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ You are a helpful personal assistant.
You manage a TODO list for the user. When the user has completed one of the tasks it can be removed from the TODO list. Only provide the list of TODO items if asked.
You remind users of upcoming calendar events when the user interacts with you.
""" },
ChatMessageStoreFactory = (ctx, ct) => new ValueTask<ChatMessageStore>(new InMemoryChatMessageStore()
ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(new InMemoryChatHistoryProvider()
// Use WithAIContextProviderMessageRemoval, so that we don't store the messages from the AI context provider in the chat history.
// You may want to store these messages, depending on their content and your requirements.
.WithAIContextProviderMessageRemoval()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private static JsonSerializerOptions CreateDefaultOptions()
[JsonSerializable(typeof(AgentResponseUpdate[]))]
[JsonSerializable(typeof(ServiceIdAgentThread.ServiceIdAgentThreadState))]
[JsonSerializable(typeof(InMemoryAgentThread.InMemoryAgentThreadState))]
[JsonSerializable(typeof(InMemoryChatMessageStore.StoreState))]
[JsonSerializable(typeof(InMemoryChatHistoryProvider.State))]

[ExcludeFromCodeCoverage]
private sealed partial class JsonContext : JsonSerializerContext;
Expand Down
2 changes: 1 addition & 1 deletion dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public virtual JsonElement Serialize(JsonSerializerOptions? jsonSerializerOption
/// <exception cref="ArgumentNullException"><paramref name="serviceType"/> is <see langword="null"/>.</exception>
/// <remarks>
/// The purpose of this method is to allow for the retrieval of strongly-typed services that might be provided by the <see cref="AgentThread"/>,
/// including itself or any services it might be wrapping. For example, to access a <see cref="ChatMessageStore"/> if available for the instance,
/// including itself or any services it might be wrapping. For example, to access a <see cref="ChatHistoryProvider"/> if available for the instance,
/// <see cref="GetService"/> may be used to request it.
/// </remarks>
public virtual object? GetService(Type serviceType, object? serviceKey = null)
Expand Down
Loading
Loading