.NET: [BREAKING] Change GetNewThread and DeserializeThread to async (#3152)

* Change GetNewThread and DeserializeThread plus ChatMessageStore and AIContextProvider Factories to async

* Merge fixes
This commit is contained in:
westey
2026-01-12 11:25:51 +00:00
committed by GitHub
parent 6e3bc219e0
commit bb6ecd9c71
113 changed files with 538 additions and 477 deletions
@@ -34,7 +34,7 @@ AIAgent agent = new AzureOpenAIClient(
{
ChatOptions = new() { Instructions = "You are good at telling jokes." },
Name = "Joker",
AIContextProviderFactory = (ctx) => new ChatHistoryMemoryProvider(
AIContextProviderFactory = (ctx, ct) => new ValueTask<AIContextProvider>(new ChatHistoryMemoryProvider(
vectorStore,
collectionName: "chathistory",
vectorDimensions: 3072,
@@ -43,18 +43,18 @@ AIAgent agent = new AzureOpenAIClient(
storageScope: new() { UserId = "UID1", ThreadId = new Guid().ToString() },
// Configure the scope which would be used to search for relevant prior messages.
// In this case, we are searching for any messages for the user across all threads.
searchScope: new() { UserId = "UID1" })
searchScope: new() { UserId = "UID1" }))
});
// Start a new thread for the agent conversation.
AgentThread thread = agent.GetNewThread();
AgentThread thread = await agent.GetNewThreadAsync();
// Run the agent with the thread that stores conversation history in the vector store.
Console.WriteLine(await agent.RunAsync("I like jokes about Pirates. Tell me a joke about a pirate.", thread));
// Start a second thread. Since we configured the search scope to be across all threads for the user,
// the agent should remember that the user likes pirate jokes.
AgentThread thread2 = agent.GetNewThread();
AgentThread thread2 = await agent.GetNewThreadAsync();
// Run the agent with the second thread.
Console.WriteLine(await agent.RunAsync("Tell me a joke that I might like.", thread2));
@@ -31,16 +31,16 @@ AIAgent agent = new AzureOpenAIClient(
.CreateAIAgent(new ChatClientAgentOptions()
{
ChatOptions = new() { Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details." },
AIContextProviderFactory = ctx => ctx.SerializedState.ValueKind is not JsonValueKind.Null and not JsonValueKind.Undefined
AIContextProviderFactory = (ctx, ct) => new ValueTask<AIContextProvider>(ctx.SerializedState.ValueKind is not JsonValueKind.Null and not JsonValueKind.Undefined
// If each thread should have its own Mem0 scope, you can create a new id per thread here:
// ? new Mem0Provider(mem0HttpClient, new Mem0ProviderScope() { ThreadId = Guid.NewGuid().ToString() })
// In this case we are storing memories scoped by application and user instead so that memories are retained across threads.
? new Mem0Provider(mem0HttpClient, new Mem0ProviderScope() { ApplicationId = "getting-started-agents", UserId = "sample-user" })
// For cases where we are restoring from serialized state:
: new Mem0Provider(mem0HttpClient, ctx.SerializedState, ctx.JsonSerializerOptions)
: new Mem0Provider(mem0HttpClient, ctx.SerializedState, ctx.JsonSerializerOptions))
});
AgentThread thread = agent.GetNewThread();
AgentThread thread = await agent.GetNewThreadAsync();
// Clear any existing memories for this scope to demonstrate fresh behavior.
Mem0Provider mem0Provider = thread.GetService<Mem0Provider>()!;
@@ -56,9 +56,9 @@ Console.WriteLine(await agent.RunAsync("What do you already know about my upcomi
Console.WriteLine("\n>> Serialize and deserialize the thread to demonstrate persisted state\n");
JsonElement serializedThread = thread.Serialize();
AgentThread restoredThread = agent.DeserializeThread(serializedThread);
AgentThread restoredThread = await agent.DeserializeThreadAsync(serializedThread);
Console.WriteLine(await agent.RunAsync("Can you recap the personal details you remember?", restoredThread));
Console.WriteLine("\n>> Start a new thread that shares the same Mem0 scope\n");
AgentThread newThread = agent.GetNewThread();
AgentThread newThread = await agent.GetNewThreadAsync();
Console.WriteLine(await agent.RunAsync("Summarize what you already know about me.", newThread));
@@ -33,11 +33,11 @@ ChatClient chatClient = new AzureOpenAIClient(
AIAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions()
{
ChatOptions = new() { Instructions = "You are a friendly assistant. Always address the user by their name." },
AIContextProviderFactory = ctx => new UserInfoMemory(chatClient.AsIChatClient(), ctx.SerializedState, ctx.JsonSerializerOptions)
AIContextProviderFactory = (ctx, ct) => new ValueTask<AIContextProvider>(new UserInfoMemory(chatClient.AsIChatClient(), ctx.SerializedState, ctx.JsonSerializerOptions))
});
// Create a new thread for the conversation.
AgentThread thread = agent.GetNewThread();
AgentThread thread = await agent.GetNewThreadAsync();
Console.WriteLine(">> Use thread with blank memory\n");
@@ -52,7 +52,7 @@ var threadElement = thread.Serialize();
Console.WriteLine("\n>> Use deserialized thread with previously created memories\n");
// Later we can deserialize the thread and continue the conversation with the previous memory component state.
var deserializedThread = agent.DeserializeThread(threadElement);
var deserializedThread = await agent.DeserializeThreadAsync(threadElement);
Console.WriteLine(await agent.RunAsync("What is my name and age?", deserializedThread));
Console.WriteLine("\n>> Read memories from memory component\n");
@@ -68,7 +68,7 @@ Console.WriteLine("\n>> Use new thread with previously created memories\n");
// It is also possible to set the memories in a memory component on an individual thread.
// This is useful if we want to start a new thread, but have it share the same memories as a previous thread.
var newThread = agent.GetNewThread();
var newThread = await agent.GetNewThreadAsync();
if (userInfo is not null && newThread.GetService<UserInfoMemory>() is UserInfoMemory newThreadMemory)
{
newThreadMemory.UserInfo = userInfo;