diff --git a/dotnet/samples/A2AClientServer/A2AClient/Program.cs b/dotnet/samples/A2AClientServer/A2AClient/Program.cs index b701ea7441..bdf942a0cc 100644 --- a/dotnet/samples/A2AClientServer/A2AClient/Program.cs +++ b/dotnet/samples/A2AClientServer/A2AClient/Program.cs @@ -42,7 +42,7 @@ public static class Program // Create the Host agent var hostAgent = new HostClientAgent(loggerFactory); await hostAgent.InitializeAgentAsync(modelId, apiKey, agentUrls!.Split(";")); - AgentThread thread = await hostAgent.Agent!.GetNewThreadAsync(cancellationToken); + AgentSession session = await hostAgent.Agent!.GetNewSessionAsync(cancellationToken); try { while (true) @@ -61,7 +61,7 @@ public static class Program break; } - var agentResponse = await hostAgent.Agent!.RunAsync(message, thread, cancellationToken: cancellationToken); + var agentResponse = await hostAgent.Agent!.RunAsync(message, session, cancellationToken: cancellationToken); foreach (var chatMessage in agentResponse.Messages) { Console.ForegroundColor = ConsoleColor.Cyan; diff --git a/dotnet/samples/AGUIClientServer/AGUIClient/Program.cs b/dotnet/samples/AGUIClientServer/AGUIClient/Program.cs index 1906b4d913..e6be9472da 100644 --- a/dotnet/samples/AGUIClientServer/AGUIClient/Program.cs +++ b/dotnet/samples/AGUIClientServer/AGUIClient/Program.cs @@ -88,7 +88,7 @@ public static class Program description: "AG-UI Client Agent", tools: [changeBackground, readClientClimateSensors]); - AgentThread thread = await agent.GetNewThreadAsync(cancellationToken); + AgentSession session = await agent.GetNewSessionAsync(cancellationToken); List messages = [new(ChatRole.System, "You are a helpful assistant.")]; try { @@ -112,23 +112,23 @@ public static class Program // Call RunStreamingAsync to get streaming updates bool isFirstUpdate = true; - string? threadId = null; + string? sessionId = null; var updates = new List(); - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread, cancellationToken: cancellationToken)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session, cancellationToken: cancellationToken)) { // Use AsChatResponseUpdate to access ChatResponseUpdate properties ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate(); updates.Add(chatUpdate); if (chatUpdate.ConversationId != null) { - threadId = chatUpdate.ConversationId; + sessionId = chatUpdate.ConversationId; } // Display run started information from the first update - if (isFirstUpdate && threadId != null && update.ResponseId != null) + if (isFirstUpdate && sessionId != null && update.ResponseId != null) { Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"\n[Run Started - Thread: {threadId}, Run: {update.ResponseId}]"); + Console.WriteLine($"\n[Run Started - Session: {sessionId}, Run: {update.ResponseId}]"); Console.ResetColor(); isFirstUpdate = false; } @@ -177,7 +177,7 @@ public static class Program var lastUpdate = updates[^1]; Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(); - Console.WriteLine($"[Run Ended - Thread: {threadId}, Run: {lastUpdate.ResponseId}]"); + Console.WriteLine($"[Run Ended - Session: {sessionId}, Run: {lastUpdate.ResponseId}]"); Console.ResetColor(); } messages.Clear(); diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs b/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs index da082483db..f1981e19be 100644 --- a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs +++ b/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs @@ -19,21 +19,21 @@ internal sealed class AgenticUIAgent : DelegatingAIAgent this._jsonSerializerOptions = jsonSerializerOptions; } - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { // Track function calls that should trigger state events var trackedFunctionCalls = new Dictionary(); - await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { // Process contents: track function calls and emit state events for results List stateEventsToEmit = new(); diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs b/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs index 2e994d8e29..a06aed4176 100644 --- a/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs +++ b/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs @@ -20,21 +20,21 @@ internal sealed class PredictiveStateUpdatesAgent : DelegatingAIAgent this._jsonSerializerOptions = jsonSerializerOptions; } - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { // Track the last emitted document state to avoid duplicates string? lastEmittedDocument = null; - await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { // Check if we're seeing a write_document tool call and emit predictive state bool hasToolCall = false; diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs b/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs index 36a629dd56..51f4791272 100644 --- a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs +++ b/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs @@ -19,21 +19,21 @@ internal sealed class SharedStateAgent : DelegatingAIAgent this._jsonSerializerOptions = jsonSerializerOptions; } - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (options is not ChatClientAgentRunOptions { ChatOptions.AdditionalProperties: { } properties } chatRunOptions || !properties.TryGetValue("ag_ui_state", out JsonElement state)) { - await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { yield return update; } @@ -64,7 +64,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent var firstRunMessages = messages.Append(stateUpdateMessage); var allUpdates = new List(); - await foreach (var update in this.InnerAgent.RunStreamingAsync(firstRunMessages, thread, firstRunOptions, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(firstRunMessages, session, firstRunOptions, cancellationToken).ConfigureAwait(false)) { allUpdates.Add(update); @@ -98,7 +98,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent ChatRole.System, [new TextContent("Please provide a concise summary of the state changes in at most two sentences.")])); - await foreach (var update in this.InnerAgent.RunStreamingAsync(secondRunMessages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(secondRunMessages, session, options, cancellationToken).ConfigureAwait(false)) { yield return update; } diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs index 7447c54aa1..e7262345d7 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs @@ -36,7 +36,7 @@ var pirateAgentBuilder = builder.AddAIAgent( chatClientServiceKey: "chat-model") .WithAITool(new CustomAITool()) .WithAITool(new CustomFunctionTool()) - .WithInMemoryThreadStore(); + .WithInMemorySessionStore(); var knightsKnavesAgentBuilder = builder.AddAIAgent("knights-and-knaves", (sp, key) => { diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs index 1f87597122..f790ec0daa 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs @@ -28,13 +28,13 @@ internal sealed class A2AAgentClient : AgentClientBase public override async IAsyncEnumerable RunStreamingAsync( string agentName, IList messages, - string? threadId = null, + string? sessionId = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { this._logger.LogInformation("Running agent {AgentName} with {MessageCount} messages via A2A", agentName, messages.Count); var (a2aClient, _) = this.ResolveClient(agentName); - var contextId = threadId ?? Guid.NewGuid().ToString("N"); + var contextId = sessionId ?? Guid.NewGuid().ToString("N"); // Convert and send messages via A2A without try-catch in yield method var results = new List(); diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/IAgentClient.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/IAgentClient.cs index 2d22413f0d..4eda916d01 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/IAgentClient.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/IAgentClient.cs @@ -16,13 +16,13 @@ internal abstract class AgentClientBase /// /// The name of the agent to run. /// The messages to send to the agent. - /// Optional thread identifier for conversation continuity. + /// Optional session identifier for conversation continuity. /// Cancellation token. /// An asynchronous enumerable of agent response updates. public abstract IAsyncEnumerable RunStreamingAsync( string agentName, IList messages, - string? threadId = null, + string? sessionId = null, CancellationToken cancellationToken = default); /// @@ -34,16 +34,3 @@ internal abstract class AgentClientBase public virtual Task GetAgentCardAsync(string agentName, CancellationToken cancellationToken = default) => Task.FromResult(null); } - -/// -/// Helper class to create a thread-like wrapper for agent clients. -/// -public class AgentClientThread -{ - public string ThreadId { get; } - - public AgentClientThread(string? threadId = null) - { - this.ThreadId = threadId ?? Guid.NewGuid().ToString("N"); - } -} diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs index a5b522a892..8939ca785a 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs @@ -19,7 +19,7 @@ internal sealed class OpenAIChatCompletionsAgentClient(HttpClient httpClient) : public override async IAsyncEnumerable RunStreamingAsync( string agentName, IList messages, - string? threadId = null, + string? sessionId = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { OpenAIClientOptions options = new() diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs index 7594468398..0d2470762e 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs @@ -18,7 +18,7 @@ internal sealed class OpenAIResponsesAgentClient(HttpClient httpClient) : AgentC public override async IAsyncEnumerable RunStreamingAsync( string agentName, IList messages, - string? threadId = null, + string? sessionId = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { OpenAIClientOptions options = new() @@ -30,7 +30,7 @@ internal sealed class OpenAIResponsesAgentClient(HttpClient httpClient) : AgentC var openAiClient = new ResponsesClient(model: agentName, credential: new ApiKeyCredential("dummy-key"), options: options).AsIChatClient(); var chatOptions = new ChatOptions() { - ConversationId = threadId + ConversationId = sessionId }; await foreach (var update in openAiClient.GetStreamingResponseAsync(messages, chatOptions, cancellationToken: cancellationToken)) diff --git a/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs b/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs index 8ac9ea8d51..02f8cceced 100644 --- a/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs +++ b/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs @@ -19,15 +19,15 @@ public static class FunctionTriggers public static async Task RunOrchestrationAsync([OrchestrationTrigger] TaskOrchestrationContext context) { DurableAIAgent writer = context.GetAgent("WriterAgent"); - AgentThread writerThread = await writer.GetNewThreadAsync(); + AgentSession writerSession = await writer.GetNewSessionAsync(); AgentResponse initial = await writer.RunAsync( message: "Write a concise inspirational sentence about learning.", - thread: writerThread); + session: writerSession); AgentResponse refined = await writer.RunAsync( message: $"Improve this further while keeping it under 25 words: {initial.Result.Text}", - thread: writerThread); + session: writerSession); return refined.Result.Text; } diff --git a/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs b/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs index ba16578935..d906714c5c 100644 --- a/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs +++ b/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs @@ -21,7 +21,7 @@ AzureOpenAIClient client = !string.IsNullOrEmpty(azureOpenAiKey) ? new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(azureOpenAiKey)) : new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()); -// Single agent used by the orchestration to demonstrate sequential calls on the same thread. +// Single agent used by the orchestration to demonstrate sequential calls on the same session. const string WriterName = "WriterAgent"; const string WriterInstructions = """ diff --git a/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/README.md b/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/README.md index e98885eced..8fb86a4f52 100644 --- a/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/README.md +++ b/dotnet/samples/AzureFunctions/02_AgentOrchestration_Chaining/README.md @@ -1,11 +1,11 @@ # Single Agent Orchestration Sample -This sample demonstrates how to use the Durable Agent Framework (DAFx) to create a simple Azure Functions app that orchestrates sequential calls to a single AI agent using the same conversation thread for context continuity. +This sample demonstrates how to use the Durable Agent Framework (DAFx) to create a simple Azure Functions app that orchestrates sequential calls to a single AI agent using the same session for context continuity. ## Key Concepts Demonstrated - Orchestrating multiple interactions with the same agent in a deterministic order -- Using the same `AgentThread` across multiple calls to maintain conversational context +- Using the same `AgentSession` across multiple calls to maintain conversational context - Durable orchestration with automatic checkpointing and resumption from failures - HTTP API integration for starting and monitoring orchestrations diff --git a/dotnet/samples/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs b/dotnet/samples/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs index f09579978f..fcb0952394 100644 --- a/dotnet/samples/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs +++ b/dotnet/samples/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs @@ -21,7 +21,7 @@ public static class FunctionTriggers // Get the spam detection agent DurableAIAgent spamDetectionAgent = context.GetAgent("SpamDetectionAgent"); - AgentThread spamThread = await spamDetectionAgent.GetNewThreadAsync(); + AgentSession spamSession = await spamDetectionAgent.GetNewSessionAsync(); // Step 1: Check if the email is spam AgentResponse spamDetectionResponse = await spamDetectionAgent.RunAsync( @@ -31,7 +31,7 @@ public static class FunctionTriggers Email ID: {email.EmailId} Content: {email.EmailContent} """, - thread: spamThread); + session: spamSession); DetectionResult result = spamDetectionResponse.Result; // Step 2: Conditional logic based on spam detection result @@ -43,7 +43,7 @@ public static class FunctionTriggers // Generate and send response for legitimate email DurableAIAgent emailAssistantAgent = context.GetAgent("EmailAssistantAgent"); - AgentThread emailThread = await emailAssistantAgent.GetNewThreadAsync(); + AgentSession emailSession = await emailAssistantAgent.GetNewSessionAsync(); AgentResponse emailAssistantResponse = await emailAssistantAgent.RunAsync( message: @@ -53,7 +53,7 @@ public static class FunctionTriggers Email ID: {email.EmailId} Content: {email.EmailContent} """, - thread: emailThread); + session: emailSession); EmailResponse emailResponse = emailAssistantResponse.Result; diff --git a/dotnet/samples/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs b/dotnet/samples/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs index 6dcbb50c01..b5cd6c43db 100644 --- a/dotnet/samples/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs +++ b/dotnet/samples/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs @@ -24,7 +24,7 @@ public static class FunctionTriggers // Get the writer agent DurableAIAgent writerAgent = context.GetAgent("WriterAgent"); - AgentThread writerThread = await writerAgent.GetNewThreadAsync(); + AgentSession writerSession = await writerAgent.GetNewSessionAsync(); // Set initial status context.SetCustomStatus($"Starting content generation for topic: {input.Topic}"); @@ -32,7 +32,7 @@ public static class FunctionTriggers // Step 1: Generate initial content AgentResponse writerResponse = await writerAgent.RunAsync( message: $"Write a short article about '{input.Topic}'.", - thread: writerThread); + session: writerSession); GeneratedContent content = writerResponse.Result; // Human-in-the-loop iteration - we set a maximum number of attempts to avoid infinite loops @@ -81,7 +81,7 @@ public static class FunctionTriggers Human Feedback: {humanResponse.Feedback} """, - thread: writerThread); + session: writerSession); content = writerResponse.Result; } diff --git a/dotnet/samples/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs b/dotnet/samples/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs index 9f73cff18e..3f85749880 100644 --- a/dotnet/samples/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs +++ b/dotnet/samples/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs @@ -20,7 +20,7 @@ public static class FunctionTriggers // Get the writer agent DurableAIAgent writerAgent = context.GetAgent("Writer"); - AgentThread writerThread = await writerAgent.GetNewThreadAsync(); + AgentSession writerSession = await writerAgent.GetNewSessionAsync(); // Set initial status context.SetCustomStatus($"Starting content generation for topic: {input.Topic}"); @@ -28,7 +28,7 @@ public static class FunctionTriggers // Step 1: Generate initial content AgentResponse writerResponse = await writerAgent.RunAsync( message: $"Write a short article about '{input.Topic}'.", - thread: writerThread); + session: writerSession); GeneratedContent content = writerResponse.Result; // Human-in-the-loop iteration - we set a maximum number of attempts to avoid infinite loops @@ -102,7 +102,7 @@ public static class FunctionTriggers Human Feedback: {humanResponse.Feedback} """, - thread: writerThread); + session: writerSession); content = writerResponse.Result; } diff --git a/dotnet/samples/AzureFunctions/06_LongRunningTools/Tools.cs b/dotnet/samples/AzureFunctions/06_LongRunningTools/Tools.cs index c2602e659e..0694c8ea58 100644 --- a/dotnet/samples/AzureFunctions/06_LongRunningTools/Tools.cs +++ b/dotnet/samples/AzureFunctions/06_LongRunningTools/Tools.cs @@ -47,7 +47,7 @@ internal sealed class Tools(ILogger logger) { this._logger.LogInformation("Getting status for workflow instance: {InstanceId}", instanceId); - // Get the current agent context using the thread-static property + // Get the current agent context using the session-static property OrchestrationMetadata? status = await DurableAgentContext.Current.GetOrchestrationStatusAsync( instanceId, includeDetails); diff --git a/dotnet/samples/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs b/dotnet/samples/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs index 94905f8156..f4fb251726 100644 --- a/dotnet/samples/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs +++ b/dotnet/samples/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs @@ -94,15 +94,15 @@ public sealed class FunctionTriggers AIAgent agentProxy = durableClient.AsDurableAgentProxy(context, "TravelPlanner"); - // Create a new agent thread - AgentThread thread = await agentProxy.GetNewThreadAsync(cancellationToken); - string agentSessionId = thread.GetService().ToString(); + // Create a new agent session + AgentSession session = await agentProxy.GetNewSessionAsync(cancellationToken); + string agentSessionId = session.GetService().ToString(); this._logger.LogInformation("Creating new agent session: {AgentSessionId}", agentSessionId); // Run the agent in the background (fire-and-forget) DurableAgentRunOptions options = new() { IsFireAndForget = true }; - await agentProxy.RunAsync(prompt, thread, options, cancellationToken); + await agentProxy.RunAsync(prompt, session, options, cancellationToken); this._logger.LogInformation("Agent run started for session: {AgentSessionId}", agentSessionId); diff --git a/dotnet/samples/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs b/dotnet/samples/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs index e13c685a08..eae0820cbf 100644 --- a/dotnet/samples/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs +++ b/dotnet/samples/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs @@ -65,9 +65,9 @@ public sealed class RedisStreamResponseHandler : IAgentResponseHandler "DurableAgentContext.Current is not set. This handler must be used within a durable agent context."); } - // Get session ID from the current thread context, which is only available in the context of + // Get session ID from the current session context, which is only available in the context of // a durable agent execution. - string agentSessionId = context.CurrentThread.GetService().ToString(); + string agentSessionId = context.CurrentSession.GetService().ToString(); string streamKey = GetStreamKey(agentSessionId); IDatabase db = this._redis.GetDatabase(); diff --git a/dotnet/samples/DurableAgents/ConsoleApps/01_SingleAgent/Program.cs b/dotnet/samples/DurableAgents/ConsoleApps/01_SingleAgent/Program.cs index 9d0fe3359b..8fa004470c 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/01_SingleAgent/Program.cs +++ b/dotnet/samples/DurableAgents/ConsoleApps/01_SingleAgent/Program.cs @@ -60,8 +60,8 @@ Console.ResetColor(); Console.WriteLine("Enter a message for the Joker agent (or 'exit' to quit):"); Console.WriteLine(); -// Create a thread for the conversation -AgentThread thread = await agentProxy.GetNewThreadAsync(); +// Create a session for the conversation +AgentSession session = await agentProxy.GetNewSessionAsync(); while (true) { @@ -85,7 +85,7 @@ while (true) { AgentResponse agentResponse = await agentProxy.RunAsync( message: input, - thread: thread, + session: session, cancellationToken: CancellationToken.None); Console.WriteLine(agentResponse.Text); diff --git a/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs b/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs index 74c299ae0c..e7b736a068 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs +++ b/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs @@ -33,7 +33,7 @@ AzureOpenAIClient client = !string.IsNullOrEmpty(azureOpenAiKey) ? new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(azureOpenAiKey)) : new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()); -// Single agent used by the orchestration to demonstrate sequential calls on the same thread. +// Single agent used by the orchestration to demonstrate sequential calls on the same session. const string WriterName = "WriterAgent"; const string WriterInstructions = """ @@ -47,15 +47,15 @@ AIAgent writerAgent = client.GetChatClient(deploymentName).AsAIAgent(WriterInstr static async Task RunOrchestratorAsync(TaskOrchestrationContext context) { DurableAIAgent writer = context.GetAgent("WriterAgent"); - AgentThread writerThread = await writer.GetNewThreadAsync(); + AgentSession writerSession = await writer.GetNewSessionAsync(); AgentResponse initial = await writer.RunAsync( message: "Write a concise inspirational sentence about learning.", - thread: writerThread); + session: writerSession); AgentResponse refined = await writer.RunAsync( message: $"Improve this further while keeping it under 25 words: {initial.Result.Text}", - thread: writerThread); + session: writerSession); return refined.Result.Text; } diff --git a/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/README.md b/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/README.md index 715a72ada0..504f036dae 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/README.md +++ b/dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/README.md @@ -1,11 +1,11 @@ # Single Agent Orchestration Sample -This sample demonstrates how to use the durable agents extension to create a simple console app that orchestrates sequential calls to a single AI agent using the same conversation thread for context continuity. +This sample demonstrates how to use the durable agents extension to create a simple console app that orchestrates sequential calls to a single AI agent using the same session for context continuity. ## Key Concepts Demonstrated - Orchestrating multiple interactions with the same agent in a deterministic order -- Using the same `AgentThread` across multiple calls to maintain conversational context +- Using the same `AgentSession` across multiple calls to maintain conversational context - Durable orchestration with automatic checkpointing and resumption from failures - Waiting for orchestration completion using `WaitForInstanceCompletionAsync` diff --git a/dotnet/samples/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs b/dotnet/samples/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs index 9e12f9192f..e9e3fca3a1 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs +++ b/dotnet/samples/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs @@ -56,7 +56,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, { // Get the spam detection agent DurableAIAgent spamDetectionAgent = context.GetAgent(SpamDetectionAgentName); - AgentThread spamThread = await spamDetectionAgent.GetNewThreadAsync(); + AgentSession spamSession = await spamDetectionAgent.GetNewSessionAsync(); // Step 1: Check if the email is spam AgentResponse spamDetectionResponse = await spamDetectionAgent.RunAsync( @@ -66,7 +66,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, Email ID: {email.EmailId} Content: {email.EmailContent} """, - thread: spamThread); + session: spamSession); DetectionResult result = spamDetectionResponse.Result; // Step 2: Conditional logic based on spam detection result @@ -78,7 +78,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, // Generate and send response for legitimate email DurableAIAgent emailAssistantAgent = context.GetAgent(EmailAssistantAgentName); - AgentThread emailThread = await emailAssistantAgent.GetNewThreadAsync(); + AgentSession emailSession = await emailAssistantAgent.GetNewSessionAsync(); AgentResponse emailAssistantResponse = await emailAssistantAgent.RunAsync( message: @@ -88,7 +88,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, Email ID: {email.EmailId} Content: {email.EmailContent} """, - thread: emailThread); + session: emailSession); EmailResponse emailResponse = emailAssistantResponse.Result; diff --git a/dotnet/samples/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs b/dotnet/samples/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs index 2369e6a364..8dc2186571 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs +++ b/dotnet/samples/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs @@ -48,7 +48,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, { // Get the writer agent DurableAIAgent writerAgent = context.GetAgent("WriterAgent"); - AgentThread writerThread = await writerAgent.GetNewThreadAsync(); + AgentSession writerSession = await writerAgent.GetNewSessionAsync(); // Set initial status context.SetCustomStatus($"Starting content generation for topic: {input.Topic}"); @@ -56,7 +56,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, // Step 1: Generate initial content AgentResponse writerResponse = await writerAgent.RunAsync( message: $"Write a short article about '{input.Topic}' in less than 300 words.", - thread: writerThread); + session: writerSession); GeneratedContent content = writerResponse.Result; // Human-in-the-loop iteration - we set a maximum number of attempts to avoid infinite loops @@ -105,7 +105,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, Human Feedback: {humanResponse.Feedback} """, - thread: writerThread); + session: writerSession); content = writerResponse.Result; } diff --git a/dotnet/samples/DurableAgents/ConsoleApps/06_LongRunningTools/Program.cs b/dotnet/samples/DurableAgents/ConsoleApps/06_LongRunningTools/Program.cs index e429d9ca9b..0e493a0ffc 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/06_LongRunningTools/Program.cs +++ b/dotnet/samples/DurableAgents/ConsoleApps/06_LongRunningTools/Program.cs @@ -59,7 +59,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, { // Get the writer agent DurableAIAgent writerAgent = context.GetAgent(WriterAgentName); - AgentThread writerThread = await writerAgent.GetNewThreadAsync(); + AgentSession writerSession = await writerAgent.GetNewSessionAsync(); // Set initial status context.SetCustomStatus($"Starting content generation for topic: {input.Topic}"); @@ -67,7 +67,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, // Step 1: Generate initial content AgentResponse writerResponse = await writerAgent.RunAsync( message: $"Write a short article about '{input.Topic}'.", - thread: writerThread); + session: writerSession); GeneratedContent content = writerResponse.Result; // Human-in-the-loop iteration - we set a maximum number of attempts to avoid infinite loops @@ -141,7 +141,7 @@ static async Task RunOrchestratorAsync(TaskOrchestrationContext context, Human Feedback: {humanResponse.Feedback} """, - thread: writerThread); + session: writerSession); content = writerResponse.Result; } @@ -203,7 +203,7 @@ static async Task GetWorkflowStatusAsync( [Description("The instance ID of the workflow to check")] string instanceId, [Description("Whether to include detailed information")] bool includeDetails = true) { - // Get the current agent context using the thread-static property + // Get the current agent context using the session-static property OrchestrationMetadata? status = await DurableAgentContext.Current.GetOrchestrationStatusAsync( instanceId, includeDetails); @@ -298,8 +298,8 @@ Console.ResetColor(); Console.WriteLine("Enter a topic for the Publisher agent to write about (or 'exit' to quit):"); Console.WriteLine(); -// Create a thread for the conversation -AgentThread thread = await agentProxy.GetNewThreadAsync(); +// Create a session for the conversation +AgentSession session = await agentProxy.GetNewSessionAsync(); using CancellationTokenSource cts = new(); Console.CancelKeyPress += (sender, e) => @@ -330,7 +330,7 @@ while (!cts.Token.IsCancellationRequested) { AgentResponse agentResponse = await agentProxy.RunAsync( message: input, - thread: thread, + session: session, cancellationToken: cts.Token); Console.WriteLine(agentResponse.Text); diff --git a/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/Program.cs b/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/Program.cs index afd00cc200..720e2d7030 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/Program.cs +++ b/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/Program.cs @@ -304,9 +304,9 @@ if (string.IsNullOrWhiteSpace(prompt) || prompt.Equals("exit", StringComparison. return; } -// Create a new agent thread -AgentThread thread = await agentProxy.GetNewThreadAsync(); -AgentSessionId sessionId = thread.GetService(); +// Create a new agent session +AgentSession session = await agentProxy.GetNewSessionAsync(); +AgentSessionId sessionId = session.GetService(); string conversationId = sessionId.ToString(); Console.ForegroundColor = ConsoleColor.Green; @@ -316,7 +316,7 @@ Console.ResetColor(); // Run the agent in the background DurableAgentRunOptions options = new() { IsFireAndForget = true }; -await agentProxy.RunAsync(prompt, thread, options, CancellationToken.None); +await agentProxy.RunAsync(prompt, session, options, CancellationToken.None); bool streamCompleted = false; while (!streamCompleted) diff --git a/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs b/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs index 6838583342..3ba08a98b1 100644 --- a/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs +++ b/dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs @@ -61,12 +61,12 @@ public sealed class RedisStreamResponseHandler : IAgentResponseHandler DurableAgentContext context = DurableAgentContext.Current ?? throw new InvalidOperationException("DurableAgentContext.Current is not set. This handler must be used within a durable agent context."); - // Get conversation ID from the current thread context, which is only available in the context of + // Get conversation ID from the current session context, which is only available in the context of // a durable agent execution. - string conversationId = context.CurrentThread.GetService().ToString(); + string conversationId = context.CurrentSession.GetService().ToString(); if (string.IsNullOrEmpty(conversationId)) { - throw new InvalidOperationException("Unable to determine conversation ID from the current thread."); + throw new InvalidOperationException("Unable to determine conversation ID from the current session."); } string streamKey = GetStreamKey(conversationId); diff --git a/dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/Program.cs b/dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/Program.cs index de5cc79ebf..ddc330f321 100644 --- a/dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/Program.cs +++ b/dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/Program.cs @@ -16,10 +16,10 @@ AgentCard agentCard = await agentCardResolver.GetAgentCardAsync(); // Create an instance of the AIAgent for an existing A2A agent specified by the agent card. AIAgent agent = agentCard.AsAIAgent(); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); // Start the initial run with a long-running task. -AgentResponse response = await agent.RunAsync("Conduct a comprehensive analysis of quantum computing applications in cryptography, including recent breakthroughs, implementation challenges, and future roadmap. Please include diagrams and visual representations to illustrate complex concepts.", thread); +AgentResponse response = await agent.RunAsync("Conduct a comprehensive analysis of quantum computing applications in cryptography, including recent breakthroughs, implementation challenges, and future roadmap. Please include diagrams and visual representations to illustrate complex concepts.", session); // Poll until the response is complete. while (response.ContinuationToken is { } token) @@ -28,7 +28,7 @@ while (response.ContinuationToken is { } token) await Task.Delay(TimeSpan.FromSeconds(2)); // Continue with the token. - response = await agent.RunAsync(thread, options: new AgentRunOptions { ContinuationToken = token }); + response = await agent.RunAsync(session, options: new AgentRunOptions { ContinuationToken = token }); } // Display the result diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Program.cs b/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Program.cs index b3e74e7efd..56d289ce7b 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Program.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Program.cs @@ -20,7 +20,7 @@ AIAgent agent = chatClient.AsAIAgent( name: "agui-client", description: "AG-UI Client Agent"); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); List messages = [ new(ChatRole.System, "You are a helpful assistant.") @@ -49,18 +49,18 @@ try // Stream the response bool isFirstUpdate = true; - string? threadId = null; + string? sessionId = null; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session)) { ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate(); // First update indicates run started if (isFirstUpdate) { - threadId = chatUpdate.ConversationId; + sessionId = chatUpdate.ConversationId; Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"\n[Run Started - Thread: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); + Console.WriteLine($"\n[Run Started - Session: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); Console.ResetColor(); isFirstUpdate = false; } @@ -84,7 +84,7 @@ try } Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\n[Run Finished - Thread: {threadId}]"); + Console.WriteLine($"\n[Run Finished - Session: {sessionId}]"); Console.ResetColor(); } } diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Program.cs b/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Program.cs index 9544d4286b..8ab9d11b76 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Program.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Program.cs @@ -20,7 +20,7 @@ AIAgent agent = chatClient.AsAIAgent( name: "agui-client", description: "AG-UI Client Agent"); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); List messages = [ new(ChatRole.System, "You are a helpful assistant.") @@ -49,18 +49,18 @@ try // Stream the response bool isFirstUpdate = true; - string? threadId = null; + string? sessionId = null; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session)) { ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate(); // First update indicates run started if (isFirstUpdate) { - threadId = chatUpdate.ConversationId; + sessionId = chatUpdate.ConversationId; Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"\n[Run Started - Thread: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); + Console.WriteLine($"\n[Run Started - Session: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); Console.ResetColor(); isFirstUpdate = false; } @@ -116,7 +116,7 @@ try } Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\n[Run Finished - Thread: {threadId}]"); + Console.WriteLine($"\n[Run Finished - Session: {sessionId}]"); Console.ResetColor(); } } diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Program.cs b/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Program.cs index fa760e9a6e..06ce9e8b55 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Program.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Program.cs @@ -33,7 +33,7 @@ AIAgent agent = chatClient.AsAIAgent( description: "AG-UI Client Agent", tools: frontendTools); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); List messages = [ new(ChatRole.System, "You are a helpful assistant.") @@ -62,18 +62,18 @@ try // Stream the response bool isFirstUpdate = true; - string? threadId = null; + string? sessionId = null; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session)) { ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate(); // First update indicates run started if (isFirstUpdate) { - threadId = chatUpdate.ConversationId; + sessionId = chatUpdate.ConversationId; Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"\n[Run Started - Thread: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); + Console.WriteLine($"\n[Run Started - Session: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); Console.ResetColor(); isFirstUpdate = false; } @@ -109,7 +109,7 @@ try } Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\n[Run Finished - Thread: {threadId}]"); + Console.WriteLine($"\n[Run Finished - Session: {sessionId}]"); Console.ResetColor(); } } diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Program.cs b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Program.cs index e66087bad7..fafe9ccf83 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Program.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Program.cs @@ -27,7 +27,7 @@ JsonSerializerOptions jsonSerializerOptions = JsonSerializerOptions.Default; ServerFunctionApprovalClientAgent agent = new(baseAgent, jsonSerializerOptions); List messages = []; -AgentThread? thread = null; +AgentSession? session = null; Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Ask a question (or type 'exit' to quit):"); @@ -52,7 +52,7 @@ while ((input = Console.ReadLine()) != null && !input.Equals("exit", StringCompa approvalResponses.Clear(); List chatResponseUpdates = []; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread, cancellationToken: default)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session, cancellationToken: default)) { chatResponseUpdates.Add(update); foreach (AIContent content in update.Contents) diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs index ef84b85281..ee0191fd98 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs @@ -24,17 +24,17 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken) .ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -43,7 +43,7 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent // Run the inner agent and intercept any approval requests await foreach (var update in this.InnerAgent.RunStreamingAsync( - processedMessages, thread, options, cancellationToken).ConfigureAwait(false)) + processedMessages, session, options, cancellationToken).ConfigureAwait(false)) { yield return ProcessIncomingServerApprovalRequests(update, this._jsonSerializerOptions); } diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs index 01649084ac..62209792f6 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs @@ -24,17 +24,17 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken) .ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -43,7 +43,7 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent // Run the inner agent and intercept any approval requests await foreach (var update in this.InnerAgent.RunStreamingAsync( - processedMessages, thread, options, cancellationToken).ConfigureAwait(false)) + processedMessages, session, options, cancellationToken).ConfigureAwait(false)) { yield return ProcessOutgoingApprovalRequests(update, this._jsonSerializerOptions); } diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Program.cs b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Program.cs index 0072f62845..8db46493d2 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Program.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Program.cs @@ -30,7 +30,7 @@ JsonSerializerOptions jsonOptions = new(JsonSerializerDefaults.Web) }; StatefulAgent agent = new(baseAgent, jsonOptions, new AgentState()); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); List messages = [ new(ChatRole.System, "You are a helpful recipe assistant.") @@ -65,21 +65,21 @@ try // Stream the response bool isFirstUpdate = true; - string? threadId = null; + string? sessionId = null; bool stateReceived = false; Console.WriteLine(); - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session)) { ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate(); // First update indicates run started if (isFirstUpdate) { - threadId = chatUpdate.ConversationId; + sessionId = chatUpdate.ConversationId; Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"[Run Started - Thread: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); + Console.WriteLine($"[Run Started - Session: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]"); Console.ResetColor(); isFirstUpdate = false; } @@ -113,7 +113,7 @@ try } Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\n[Run Finished - Thread: {threadId}]"); + Console.WriteLine($"\n[Run Finished - Session: {sessionId}]"); Console.ResetColor(); // Display final state if received diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs index 8eca890e60..41c94d5686 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs @@ -37,18 +37,18 @@ internal sealed class StatefulAgent : DelegatingAIAgent /// protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken) .ToAgentResponseAsync(cancellationToken); } /// protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -64,7 +64,7 @@ internal sealed class StatefulAgent : DelegatingAIAgent messagesWithState.Add(stateMessage); // Stream the response and update state when received - await foreach (AgentResponseUpdate update in this.InnerAgent.RunStreamingAsync(messagesWithState, thread, options, cancellationToken)) + await foreach (AgentResponseUpdate update in this.InnerAgent.RunStreamingAsync(messagesWithState, session, options, cancellationToken)) { // Check if this update contains a state snapshot foreach (AIContent content in update.Contents) diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs index 1ac21adfce..66e8da6eed 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs @@ -19,17 +19,17 @@ internal sealed class SharedStateAgent : DelegatingAIAgent protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken) .ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -40,7 +40,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent state.ValueKind != JsonValueKind.Object) { // No state management requested, pass through to inner agent - await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { yield return update; } @@ -58,7 +58,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent if (!hasProperties) { // Empty state - treat as no state - await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { yield return update; } @@ -92,7 +92,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent // Collect all updates from first run var allUpdates = new List(); - await foreach (var update in this.InnerAgent.RunStreamingAsync(firstRunMessages, thread, firstRunOptions, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(firstRunMessages, session, firstRunOptions, cancellationToken).ConfigureAwait(false)) { allUpdates.Add(update); @@ -129,7 +129,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent ChatRole.System, [new TextContent("Please provide a concise summary of the state changes in at most two sentences.")])); - await foreach (var update in this.InnerAgent.RunStreamingAsync(secondRunMessages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(secondRunMessages, session, options, cancellationToken).ConfigureAwait(false)) { yield return update; } diff --git a/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs b/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs index abef6ee30f..1b09b43588 100644 --- a/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs +++ b/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs @@ -128,7 +128,7 @@ var agent = new ChatClientAgent(instrumentedChatClient, .UseOpenTelemetry(SourceName, configure: (cfg) => cfg.EnableSensitiveData = true) // enable telemetry at the agent level .Build(); -var thread = await agent.GetNewThreadAsync(); +var session = await agent.GetNewSessionAsync(); appLogger.LogInformation("Agent created successfully with ID: {AgentId}", agent.Id); @@ -176,7 +176,7 @@ using (appLogger.BeginScope(new Dictionary { ["SessionId"] = ses Console.Write("Agent: "); // Run the agent (this will create its own internal telemetry spans) - await foreach (var update in agent.RunStreamingAsync(userInput, thread)) + await foreach (var update in agent.RunStreamingAsync(userInput, session)) { Console.Write(update.Text); } diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs index e3d37a39d7..38836b90d3 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs @@ -31,8 +31,8 @@ AIAgent agent2 = await persistentAgentsClient.CreateAIAgentAsync( instructions: JokerInstructions); // You can then invoke the agent like any other AIAgent. -AgentThread thread = await agent1.GetNewThreadAsync(); -Console.WriteLine(await agent1.RunAsync("Tell me a joke about a pirate.", thread)); +AgentSession session = await agent1.GetNewSessionAsync(); +Console.WriteLine(await agent1.RunAsync("Tell me a joke about a pirate.", session)); // Cleanup for sample purposes. await persistentAgentsClient.Administration.DeleteAgentAsync(agent1.Id); diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Program.cs index ba51c8c0e7..d4dd7e7d3a 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Program.cs @@ -40,11 +40,11 @@ var latestAgentVersion = jokerAgentLatest.GetService()!; Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}"); // Once you have the AIAgent, you can invoke it like any other AIAgent. -AgentThread thread = await jokerAgentLatest.GetNewThreadAsync(); -Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate.", thread)); +AgentSession session = await jokerAgentLatest.GetNewSessionAsync(); +Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate.", session)); -// This will use the same thread to continue the conversation. -Console.WriteLine(await jokerAgentLatest.RunAsync("Now tell me a joke about a cat and a dog using last joke as the anchor.", thread)); +// This will use the same session to continue the conversation. +Console.WriteLine(await jokerAgentLatest.RunAsync("Now tell me a joke about a cat and a dog using last joke as the anchor.", session)); // Cleanup by agent name removes both agent versions created. aiProjectClient.Agents.DeleteAgent(existingJokerAgent.Name); diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs index 52efda2cf6..980a4eda40 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs @@ -28,35 +28,35 @@ namespace SampleApp { public override string? Name => "UpperCaseParrotAgent"; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) - => new(new CustomAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) + => new(new CustomAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new CustomAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new CustomAgentSession(serializedSession, jsonSerializerOptions)); - protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - // Create a thread if the user didn't supply one. - thread ??= await this.GetNewThreadAsync(cancellationToken); + // Create a session if the user didn't supply one. + session ??= await this.GetNewSessionAsync(cancellationToken); - if (thread is not CustomAgentThread typedThread) + if (session is not CustomAgentSession typedSession) { - throw new ArgumentException($"The provided thread is not of type {nameof(CustomAgentThread)}.", nameof(thread)); + throw new ArgumentException($"The provided session is not of type {nameof(CustomAgentSession)}.", nameof(session)); } // Get existing messages from the store var invokingContext = new ChatHistoryProvider.InvokingContext(messages); - var storeMessages = await typedThread.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken); + var storeMessages = await typedSession.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken); // Clone the input messages and turn them into response messages with upper case text. List responseMessages = CloneAndToUpperCase(messages, this.Name).ToList(); - // Notify the thread of the input and output messages. + // Notify the session of the input and output messages. var invokedContext = new ChatHistoryProvider.InvokedContext(messages, storeMessages) { ResponseMessages = responseMessages }; - await typedThread.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken); + await typedSession.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken); return new AgentResponse { @@ -66,29 +66,29 @@ namespace SampleApp }; } - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - // Create a thread if the user didn't supply one. - thread ??= await this.GetNewThreadAsync(cancellationToken); + // Create a session if the user didn't supply one. + session ??= await this.GetNewSessionAsync(cancellationToken); - if (thread is not CustomAgentThread typedThread) + if (session is not CustomAgentSession typedSession) { - throw new ArgumentException($"The provided thread is not of type {nameof(CustomAgentThread)}.", nameof(thread)); + throw new ArgumentException($"The provided session is not of type {nameof(CustomAgentSession)}.", nameof(session)); } // Get existing messages from the store var invokingContext = new ChatHistoryProvider.InvokingContext(messages); - var storeMessages = await typedThread.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken); + var storeMessages = await typedSession.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken); // Clone the input messages and turn them into response messages with upper case text. List responseMessages = CloneAndToUpperCase(messages, this.Name).ToList(); - // Notify the thread of the input and output messages. + // Notify the session of the input and output messages. var invokedContext = new ChatHistoryProvider.InvokedContext(messages, storeMessages) { ResponseMessages = responseMessages }; - await typedThread.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken); + await typedSession.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken); foreach (var message in responseMessages) { @@ -128,14 +128,14 @@ namespace SampleApp }); /// - /// A thread type for our custom agent that only supports in memory storage of messages. + /// A session type for our custom agent that only supports in memory storage of messages. /// - internal sealed class CustomAgentThread : InMemoryAgentThread + internal sealed class CustomAgentSession : InMemoryAgentSession { - internal CustomAgentThread() { } + internal CustomAgentSession() { } - internal CustomAgentThread(JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null) - : base(serializedThreadState, jsonSerializerOptions) { } + internal CustomAgentSession(JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedSessionState, jsonSerializerOptions) { } } } } diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Program.cs index eb194badfe..69bd31d971 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Program.cs @@ -33,8 +33,8 @@ AIAgent agent2 = await assistantClient.CreateAIAgentAsync( instructions: JokerInstructions); // You can invoke the agent like any other AIAgent. -AgentThread thread = await agent1.GetNewThreadAsync(); -Console.WriteLine(await agent1.RunAsync("Tell me a joke about a pirate.", thread)); +AgentSession session = await agent1.GetNewSessionAsync(); +Console.WriteLine(await agent1.RunAsync("Tell me a joke about a pirate.", session)); // Cleanup for sample purposes. await assistantClient.DeleteAssistantAsync(agent1.Id); diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs index 4253e8819f..f9634d9212 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs @@ -26,12 +26,12 @@ AIAgent agent = new AnthropicClient { APIKey = apiKey } .AsAIAgent(model: model, instructions: AssistantInstructions, name: AssistantName, tools: [tool]); // Non-streaming agent interaction with function tools. -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", thread)); +AgentSession session = await agent.GetNewSessionAsync(); +Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", session)); // Streaming agent interaction with function tools. -thread = await agent.GetNewThreadAsync(); -await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("What is the weather like in Amsterdam?", thread)) +session = await agent.GetNewSessionAsync(); +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("What is the weather like in Amsterdam?", session)) { Console.WriteLine(update); } diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs index b8fe566dc9..6299f6381a 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs @@ -39,22 +39,22 @@ AIAgent agent = new AzureOpenAIClient( collectionName: "chathistory", vectorDimensions: 3072, // Configure the scope values under which chat messages will be stored. - // In this case, we are using a fixed user ID and a unique thread ID for each new thread. - storageScope: new() { UserId = "UID1", ThreadId = new Guid().ToString() }, + // In this case, we are using a fixed user ID and a unique session ID for each new session. + storageScope: new() { UserId = "UID1", SessionId = Guid.NewGuid().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. + // In this case, we are searching for any messages for the user across all sessions. searchScope: new() { UserId = "UID1" })) }); -// Start a new thread for the agent conversation. -AgentThread thread = await agent.GetNewThreadAsync(); +// Start a new session for the agent conversation. +AgentSession session = await agent.GetNewSessionAsync(); -// 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)); +// Run the agent with the session 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.", session)); -// Start a second thread. Since we configured the search scope to be across all threads for the user, +// Start a second session. Since we configured the search scope to be across all sessions for the user, // the agent should remember that the user likes pirate jokes. -AgentThread thread2 = await agent.GetNewThreadAsync(); +AgentSession? session2 = await agent.GetNewSessionAsync(); -// Run the agent with the second thread. -Console.WriteLine(await agent.RunAsync("Tell me a joke that I might like.", thread2)); +// Run the agent with the second session. +Console.WriteLine(await agent.RunAsync("Tell me a joke that I might like.", session2)); diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs index da0e816448..ab0f1735c0 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs @@ -2,7 +2,7 @@ // This sample shows how to use the Mem0Provider to persist and recall memories for an agent. // The sample stores conversation messages in a Mem0 service and retrieves relevant memories -// for subsequent invocations, even across new threads. +// for subsequent invocations, even across new sessions. using System.Net.Http.Headers; using System.Text.Json; @@ -32,7 +32,7 @@ AIAgent agent = new AzureOpenAIClient( { ChatOptions = new() { Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details." }, AIContextProviderFactory = (ctx, ct) => new ValueTask(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: + // If each session should have its own Mem0 scope, you can create a new id per session 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" }) @@ -40,25 +40,25 @@ AIAgent agent = new AzureOpenAIClient( : new Mem0Provider(mem0HttpClient, ctx.SerializedState, ctx.JsonSerializerOptions)) }); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); // Clear any existing memories for this scope to demonstrate fresh behavior. -Mem0Provider mem0Provider = thread.GetService()!; +Mem0Provider mem0Provider = session.GetService()!; await mem0Provider.ClearStoredMemoriesAsync(); -Console.WriteLine(await agent.RunAsync("Hi there! My name is Taylor and I'm planning a hiking trip to Patagonia in November.", thread)); -Console.WriteLine(await agent.RunAsync("I'm travelling with my sister and we love finding scenic viewpoints.", thread)); +Console.WriteLine(await agent.RunAsync("Hi there! My name is Taylor and I'm planning a hiking trip to Patagonia in November.", session)); +Console.WriteLine(await agent.RunAsync("I'm travelling with my sister and we love finding scenic viewpoints.", session)); Console.WriteLine("\nWaiting briefly for Mem0 to index the new memories...\n"); await Task.Delay(TimeSpan.FromSeconds(2)); -Console.WriteLine(await agent.RunAsync("What do you already know about my upcoming trip?", thread)); +Console.WriteLine(await agent.RunAsync("What do you already know about my upcoming trip?", session)); -Console.WriteLine("\n>> Serialize and deserialize the thread to demonstrate persisted state\n"); -JsonElement serializedThread = thread.Serialize(); -AgentThread restoredThread = await agent.DeserializeThreadAsync(serializedThread); -Console.WriteLine(await agent.RunAsync("Can you recap the personal details you remember?", restoredThread)); +Console.WriteLine("\n>> Serialize and deserialize the session to demonstrate persisted state\n"); +JsonElement serializedSession = session.Serialize(); +AgentSession restoredSession = await agent.DeserializeSessionAsync(serializedSession); +Console.WriteLine(await agent.RunAsync("Can you recap the personal details you remember?", restoredSession)); -Console.WriteLine("\n>> Start a new thread that shares the same Mem0 scope\n"); -AgentThread newThread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("Summarize what you already know about me.", newThread)); +Console.WriteLine("\n>> Start a new session that shares the same Mem0 scope\n"); +AgentSession newSession = await agent.GetNewSessionAsync(); +Console.WriteLine(await agent.RunAsync("Summarize what you already know about me.", newSession)); diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs index 4e84a4b53c..cf8e0dd943 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs @@ -24,10 +24,10 @@ ChatClient chatClient = new AzureOpenAIClient( .GetChatClient(deploymentName); // Create the agent and provide a factory to add our custom memory component to -// all threads created by the agent. Here each new memory component will have its own -// user info object, so each thread will have its own memory. +// all sessions created by the agent. Here each new memory component will have its own +// user info object, so each session will have its own memory. // In real world applications/services, where the user info would be persisted in a database, -// and preferably shared between multiple threads used by the same user, ensure that the +// and preferably shared between multiple sessions used by the same user, ensure that the // factory reads the user id from the current context and scopes the memory component // and its storage to that user id. AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions() @@ -36,47 +36,47 @@ AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions() AIContextProviderFactory = (ctx, ct) => new ValueTask(new UserInfoMemory(chatClient.AsIChatClient(), ctx.SerializedState, ctx.JsonSerializerOptions)) }); -// Create a new thread for the conversation. -AgentThread thread = await agent.GetNewThreadAsync(); +// Create a new session for the conversation. +AgentSession session = await agent.GetNewSessionAsync(); -Console.WriteLine(">> Use thread with blank memory\n"); +Console.WriteLine(">> Use session with blank memory\n"); // Invoke the agent and output the text result. -Console.WriteLine(await agent.RunAsync("Hello, what is the square root of 9?", thread)); -Console.WriteLine(await agent.RunAsync("My name is Ruaidhrí", thread)); -Console.WriteLine(await agent.RunAsync("I am 20 years old", thread)); +Console.WriteLine(await agent.RunAsync("Hello, what is the square root of 9?", session)); +Console.WriteLine(await agent.RunAsync("My name is Ruaidhrí", session)); +Console.WriteLine(await agent.RunAsync("I am 20 years old", session)); -// We can serialize the thread. The serialized state will include the state of the memory component. -var threadElement = thread.Serialize(); +// We can serialize the session. The serialized state will include the state of the memory component. +var sesionElement = session.Serialize(); -Console.WriteLine("\n>> Use deserialized thread with previously created memories\n"); +Console.WriteLine("\n>> Use deserialized session with previously created memories\n"); -// Later we can deserialize the thread and continue the conversation with the previous memory component state. -var deserializedThread = await agent.DeserializeThreadAsync(threadElement); -Console.WriteLine(await agent.RunAsync("What is my name and age?", deserializedThread)); +// Later we can deserialize the session and continue the conversation with the previous memory component state. +var deserializedSession = await agent.DeserializeSessionAsync(sesionElement); +Console.WriteLine(await agent.RunAsync("What is my name and age?", deserializedSession)); Console.WriteLine("\n>> Read memories from memory component\n"); -// It's possible to access the memory component via the thread's GetService method. -var userInfo = deserializedThread.GetService()?.UserInfo; +// It's possible to access the memory component via the session's GetService method. +var userInfo = deserializedSession.GetService()?.UserInfo; // Output the user info that was captured by the memory component. Console.WriteLine($"MEMORY - User Name: {userInfo?.UserName}"); Console.WriteLine($"MEMORY - User Age: {userInfo?.UserAge}"); -Console.WriteLine("\n>> Use new thread with previously created memories\n"); +Console.WriteLine("\n>> Use new session 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 = await agent.GetNewThreadAsync(); -if (userInfo is not null && newThread.GetService() is UserInfoMemory newThreadMemory) +// It is also possible to set the memories in a memory component on an individual session. +// This is useful if we want to start a new session, but have it share the same memories as a previous session. +var newSession = await agent.GetNewSessionAsync(); +if (userInfo is not null && newSession.GetService() is UserInfoMemory newSessionMemory) { - newThreadMemory.UserInfo = userInfo; + newSessionMemory.UserInfo = userInfo; } // Invoke the agent and output the text result. // This time the agent should remember the user's name and use it in the response. -Console.WriteLine(await agent.RunAsync("What is my name and age?", newThread)); +Console.WriteLine(await agent.RunAsync("What is my name and age?", newSession)); namespace SampleApp { diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs index 3694f70b11..51d822a749 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs @@ -52,17 +52,17 @@ public class OpenAIChatClientAgent : DelegatingAIAgent /// Run the agent with the provided message and arguments. /// /// The messages to pass to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided messages and agent response. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// A containing the list of items. public virtual async Task RunAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - var response = await this.RunAsync(messages.AsChatMessages(), thread, options, cancellationToken).ConfigureAwait(false); + var response = await this.RunAsync(messages.AsChatMessages(), session, options, cancellationToken).ConfigureAwait(false); return response.AsOpenAIChatCompletion(); } @@ -71,26 +71,26 @@ public class OpenAIChatClientAgent : DelegatingAIAgent /// Run the agent streaming with the provided message and arguments. /// /// The messages to pass to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided messages and agent response. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// A containing the list of items. public virtual IAsyncEnumerable RunStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - var response = this.RunStreamingAsync(messages.AsChatMessages(), thread, options, cancellationToken); + var response = this.RunStreamingAsync(messages.AsChatMessages(), session, options, cancellationToken); return response.AsChatResponseUpdatesAsync().AsOpenAIStreamingChatCompletionUpdatesAsync(cancellationToken); } /// - protected sealed override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunCoreAsync(messages, thread, options, cancellationToken); + protected sealed override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreAsync(messages, session, options, cancellationToken); /// - protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunCoreStreamingAsync(messages, thread, options, cancellationToken); + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreStreamingAsync(messages, session, options, cancellationToken); } diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs index e0c4f36356..196bd64922 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs @@ -52,17 +52,17 @@ public class OpenAIResponseClientAgent : DelegatingAIAgent /// Run the agent with the provided message and arguments. /// /// The messages to pass to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided messages and agent response. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// A containing the list of items. public virtual async Task RunAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - var response = await this.RunAsync(messages.AsChatMessages(), thread, options, cancellationToken).ConfigureAwait(false); + var response = await this.RunAsync(messages.AsChatMessages(), session, options, cancellationToken).ConfigureAwait(false); return response.AsOpenAIResponse(); } @@ -71,17 +71,17 @@ public class OpenAIResponseClientAgent : DelegatingAIAgent /// Run the agent streaming with the provided message and arguments. /// /// The messages to pass to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided messages and agent response. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// A containing the list of items. public virtual async IAsyncEnumerable RunStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var response = this.RunStreamingAsync(messages.AsChatMessages(), thread, options, cancellationToken); + var response = this.RunStreamingAsync(messages.AsChatMessages(), session, options, cancellationToken); await foreach (var update in response.ConfigureAwait(false)) { @@ -105,10 +105,10 @@ public class OpenAIResponseClientAgent : DelegatingAIAgent } /// - protected sealed override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunCoreAsync(messages, thread, options, cancellationToken); + protected sealed override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreAsync(messages, session, options, cancellationToken); /// - protected sealed override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunCoreStreamingAsync(messages, thread, options, cancellationToken); + protected sealed override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreStreamingAsync(messages, session, options, cancellationToken); } diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs index 07a67edae4..5311f14778 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // This sample demonstrates how to maintain conversation state using the OpenAIResponseClientAgent -// and AgentThread. By passing the same thread to multiple agent invocations, the agent +// and AgentSession. By passing the same session to multiple agent invocations, the agent // automatically maintains the conversation history, allowing the AI model to understand // context from previous exchanges. @@ -29,8 +29,8 @@ ClientResult createConversationResult = await conversationClient.CreateConversat using JsonDocument createConversationResultAsJson = JsonDocument.Parse(createConversationResult.GetRawResponse().Content.ToString()); string conversationId = createConversationResultAsJson.RootElement.GetProperty("id"u8)!.GetString()!; -// Create a thread for the conversation - this enables conversation state management for subsequent turns -AgentThread thread = await agent.GetNewThreadAsync(conversationId); +// Create a session for the conversation - this enables conversation state management for subsequent turns +AgentSession session = await agent.GetNewSessionAsync(conversationId); Console.WriteLine("=== Multi-turn Conversation Demo ===\n"); @@ -38,22 +38,22 @@ Console.WriteLine("=== Multi-turn Conversation Demo ===\n"); Console.WriteLine("User: What is the capital of France?"); UserChatMessage firstMessage = new("What is the capital of France?"); -// After this call, the conversation state associated in the options is stored in 'thread' and used in subsequent calls -ChatCompletion firstResponse = await agent.RunAsync([firstMessage], thread); +// After this call, the conversation state associated in the options is stored in 'session' and used in subsequent calls +ChatCompletion firstResponse = await agent.RunAsync([firstMessage], session); Console.WriteLine($"Assistant: {firstResponse.Content.Last().Text}\n"); // Second turn: Follow-up question that relies on conversation context Console.WriteLine("User: What famous landmarks are located there?"); UserChatMessage secondMessage = new("What famous landmarks are located there?"); -ChatCompletion secondResponse = await agent.RunAsync([secondMessage], thread); +ChatCompletion secondResponse = await agent.RunAsync([secondMessage], session); Console.WriteLine($"Assistant: {secondResponse.Content.Last().Text}\n"); // Third turn: Another follow-up that demonstrates context continuity Console.WriteLine("User: How tall is the most famous one?"); UserChatMessage thirdMessage = new("How tall is the most famous one?"); -ChatCompletion thirdResponse = await agent.RunAsync([thirdMessage], thread); +ChatCompletion thirdResponse = await agent.RunAsync([thirdMessage], session); Console.WriteLine($"Assistant: {thirdResponse.Content.Last().Text}\n"); Console.WriteLine("=== End of Conversation ==="); diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md index 5b999955b2..2b4c282ead 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md @@ -4,7 +4,7 @@ This sample demonstrates how to maintain conversation state across multiple turn ## What This Sample Shows -- **Conversation State Management**: Shows how to use `ConversationClient` and `AgentThread` to maintain conversation context across multiple agent invocations +- **Conversation State Management**: Shows how to use `ConversationClient` and `AgentSession` to maintain conversation context across multiple agent invocations - **Multi-turn Conversations**: Demonstrates follow-up questions that rely on context from previous messages in the conversation - **Server-Side Storage**: Uses OpenAI's Conversation API to manage conversation history server-side, allowing the model to access previous messages without resending them - **Conversation Lifecycle**: Demonstrates creating, retrieving, and deleting conversations @@ -24,22 +24,22 @@ ConversationClient conversationClient = openAIClient.GetConversationClient(); ClientResult createConversationResult = await conversationClient.CreateConversationAsync(BinaryContent.Create(BinaryData.FromString("{}"))); ``` -### AgentThread for Conversation State +### AgentSession for Conversation State -The `AgentThread` works with `ChatClientAgentRunOptions` to link the agent to a server-side conversation: +The `AgentSession` works with `ChatClientAgentRunOptions` to link the agent to a server-side conversation: ```csharp // Set up agent run options with the conversation ID ChatClientAgentRunOptions agentRunOptions = new() { ChatOptions = new ChatOptions() { ConversationId = conversationId } }; -// Create a thread for the conversation -AgentThread thread = await agent.GetNewThreadAsync(); +// Create a session for the conversation +AgentSession session = await agent.GetNewSessionAsync(); -// First call links the thread to the conversation -ChatCompletion firstResponse = await agent.RunAsync([firstMessage], thread, agentRunOptions); +// First call links the session to the conversation +ChatCompletion firstResponse = await agent.RunAsync([firstMessage], session, agentRunOptions); -// Subsequent calls use the thread without needing to pass options again -ChatCompletion secondResponse = await agent.RunAsync([secondMessage], thread); +// Subsequent calls use the session without needing to pass options again +ChatCompletion secondResponse = await agent.RunAsync([secondMessage], session); ``` ### Retrieving Conversation History @@ -59,9 +59,9 @@ foreach (ClientResult result in getConversationItemsResults.GetRawPages()) 1. **Create an OpenAI Client**: Initialize an `OpenAIClient` with your API key 2. **Create a Conversation**: Use `ConversationClient` to create a server-side conversation 3. **Create an Agent**: Initialize an `OpenAIResponseClientAgent` with the desired model and instructions -4. **Create a Thread**: Call `agent.GetNewThreadAsync()` to create a new conversation thread -5. **Link Thread to Conversation**: Pass `ChatClientAgentRunOptions` with the `ConversationId` on the first call -6. **Send Messages**: Subsequent calls to `agent.RunAsync()` only need the thread - context is maintained +4. **Create a Session**: Call `agent.GetNewSessionAsync()` to create a new conversation session +5. **Link Session to Conversation**: Pass `ChatClientAgentRunOptions` with the `ConversationId` on the first call +6. **Send Messages**: Subsequent calls to `agent.RunAsync()` only need the session - context is maintained 7. **Cleanup**: Delete the conversation when done using `conversationClient.DeleteConversation()` ## Running the Sample diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/README.md b/dotnet/samples/GettingStarted/AgentWithOpenAI/README.md index 019af7f2b6..74a44600bf 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/README.md +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/README.md @@ -14,4 +14,4 @@ Agent Framework provides additional support to allow OpenAI developers to use th |[Using Reasoning Capabilities](./Agent_OpenAI_Step02_Reasoning/)|This sample demonstrates how to create an AI agent with reasoning capabilities using OpenAI's reasoning models and response types.| |[Creating an Agent from a ChatClient](./Agent_OpenAI_Step03_CreateFromChatClient/)|This sample demonstrates how to create an AI agent directly from an OpenAI.Chat.ChatClient instance using OpenAIChatClientAgent.| |[Creating an Agent from an OpenAIResponseClient](./Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/)|This sample demonstrates how to create an AI agent directly from an OpenAI.Responses.OpenAIResponseClient instance using OpenAIResponseClientAgent.| -|[Managing Conversation State](./Agent_OpenAI_Step05_Conversation/)|This sample demonstrates how to maintain conversation state across multiple turns using the AgentThread for context continuity.| \ No newline at end of file +|[Managing Conversation State](./Agent_OpenAI_Step05_Conversation/)|This sample demonstrates how to maintain conversation state across multiple turns using the AgentSession for context continuity.| \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs index 1371f07ba7..0beb0063a4 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs @@ -70,16 +70,16 @@ AIAgent agent = azureOpenAIClient .WithAIContextProviderMessageRemoval()), }); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); Console.WriteLine(">> Asking about returns\n"); -Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", thread)); +Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", session)); Console.WriteLine("\n>> Asking about shipping\n"); -Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", thread)); +Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", session)); Console.WriteLine("\n>> Asking about product care\n"); -Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", thread)); +Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", session)); // Produces some sample search documents. // Each one contains a source name and link, which the agent can use to cite sources in its responses. diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs index 40e2834317..486fdbe44d 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs @@ -74,22 +74,22 @@ AIAgent agent = azureOpenAIClient AIContextProviderFactory = (ctx, ct) => new ValueTask(new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)) }); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); -Console.WriteLine(">> Asking about SK threads\n"); -Console.WriteLine(await agent.RunAsync("Hi! How do I create a thread in Semantic Kernel?", thread)); +Console.WriteLine(">> Asking about SK sessions\n"); +Console.WriteLine(await agent.RunAsync("Hi! How do I create a thread/session in Semantic Kernel?", session)); // Here we are asking a very vague question when taken out of context, // but since we are including previous messages in our search using RecentMessageMemoryLimit // the RAG search should still produce useful results. -Console.WriteLine("\n>> Asking about AF threads\n"); -Console.WriteLine(await agent.RunAsync("and in Agent Framework?", thread)); +Console.WriteLine("\n>> Asking about AF sessions\n"); +Console.WriteLine(await agent.RunAsync("and in Agent Framework?", session)); Console.WriteLine("\n>> Contrasting Approaches\n"); -Console.WriteLine(await agent.RunAsync("Please contrast the two approaches", thread)); +Console.WriteLine(await agent.RunAsync("Please contrast the two approaches", session)); Console.WriteLine("\n>> Asking about ancestry\n"); -Console.WriteLine(await agent.RunAsync("What are the predecessors to the Agent Framework?", thread)); +Console.WriteLine(await agent.RunAsync("What are the predecessors to the Agent Framework?", session)); static async Task UploadDataFromMarkdown(string markdownUrl, string sourceName, VectorStoreCollection vectorStoreCollection, int chunkSize, int overlap) { diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs index a8b21ec287..dd258faf81 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs @@ -32,16 +32,16 @@ AIAgent agent = new AzureOpenAIClient( AIContextProviderFactory = (ctx, ct) => new ValueTask(new TextSearchProvider(MockSearchAsync, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)) }); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); Console.WriteLine(">> Asking about returns\n"); -Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", thread)); +Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", session)); Console.WriteLine("\n>> Asking about shipping\n"); -Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", thread)); +Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", session)); Console.WriteLine("\n>> Asking about product care\n"); -Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", thread)); +Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", session)); static Task> MockSearchAsync(string query, CancellationToken cancellationToken) { diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs index e93fd474f6..c7faa71ed5 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs @@ -43,16 +43,16 @@ AIAgent agent = await aiProjectClient instructions: "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.", tools: [fileSearchTool]); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); Console.WriteLine(">> Asking about returns\n"); -Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", thread)); +Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", session)); Console.WriteLine("\n>> Asking about shipping\n"); -Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", thread)); +Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", session)); Console.WriteLine("\n>> Asking about product care\n"); -Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", thread)); +Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", session)); // Cleanup await fileClient.DeleteFileAsync(uploadResult.Value.Id); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Program.cs index 22e5078498..73a9a76786 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Program.cs @@ -16,18 +16,18 @@ AIAgent agent = new AzureOpenAIClient( .GetChatClient(deploymentName) .AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker"); -// Invoke the agent with a multi-turn conversation, where the context is preserved in the thread object. -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); -Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", thread)); +// Invoke the agent with a multi-turn conversation, where the context is preserved in the session object. +AgentSession session = await agent.GetNewSessionAsync(); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); +Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session)); -// Invoke the agent with a multi-turn conversation and streaming, where the context is preserved in the thread object. -thread = await agent.GetNewThreadAsync(); -await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate.", thread)) +// Invoke the agent with a multi-turn conversation and streaming, where the context is preserved in the session object. +session = await agent.GetNewSessionAsync(); +await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate.", session)) { Console.WriteLine(update); } -await foreach (var update in agent.RunStreamingAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", thread)) +await foreach (var update in agent.RunStreamingAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session)) { Console.WriteLine(update); } diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs index b12bf48e6e..ad4303583c 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs @@ -30,12 +30,12 @@ AIAgent agent = new AzureOpenAIClient( .AsAIAgent(instructions: "You are a helpful assistant", tools: [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))]); // Call the agent and check if there are any user input requests to handle. -AgentThread thread = await agent.GetNewThreadAsync(); -var response = await agent.RunAsync("What is the weather like in Amsterdam?", thread); +AgentSession session = await agent.GetNewSessionAsync(); +var response = await agent.RunAsync("What is the weather like in Amsterdam?", session); var userInputRequests = response.UserInputRequests.ToList(); // For streaming use: -// var updates = await agent.RunStreamingAsync("What is the weather like in Amsterdam?", thread).ToListAsync(); +// var updates = await agent.RunStreamingAsync("What is the weather like in Amsterdam?", session).ToListAsync(); // userInputRequests = updates.SelectMany(x => x.UserInputRequests).ToList(); while (userInputRequests.Count > 0) @@ -52,12 +52,12 @@ while (userInputRequests.Count > 0) .ToList(); // Pass the user input responses back to the agent for further processing. - response = await agent.RunAsync(userInputResponses, thread); + response = await agent.RunAsync(userInputResponses, session); userInputRequests = response.UserInputRequests.ToList(); // For streaming use: - // updates = await agent.RunStreamingAsync(userInputResponses, thread).ToListAsync(); + // updates = await agent.RunStreamingAsync(userInputResponses, session).ToListAsync(); // userInputRequests = updates.SelectMany(x => x.UserInputRequests).ToList(); } diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Program.cs index b23e1abe78..c76ec1ab6e 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Program.cs @@ -18,24 +18,24 @@ AIAgent agent = new AzureOpenAIClient( .GetChatClient(deploymentName) .AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker"); -// Start a new thread for the agent conversation. -AgentThread thread = await agent.GetNewThreadAsync(); +// Start a new session for the agent conversation. +AgentSession session = await agent.GetNewSessionAsync(); -// Run the agent with a new thread. -Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); +// Run the agent with a new session. +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); -// Serialize the thread state to a JsonElement, so it can be stored for later use. -JsonElement serializedThread = thread.Serialize(); +// Serialize the session state to a JsonElement, so it can be stored for later use. +JsonElement serializedSession = session.Serialize(); -// Save the serialized thread to a temporary file (for demonstration purposes). +// Save the serialized session to a temporary file (for demonstration purposes). string tempFilePath = Path.GetTempFileName(); -await File.WriteAllTextAsync(tempFilePath, JsonSerializer.Serialize(serializedThread)); +await File.WriteAllTextAsync(tempFilePath, JsonSerializer.Serialize(serializedSession)); -// Load the serialized thread from the temporary file (for demonstration purposes). -JsonElement reloadedSerializedThread = JsonElement.Parse(await File.ReadAllTextAsync(tempFilePath)); +// Load the serialized session from the temporary file (for demonstration purposes). +JsonElement reloadedSerializedSession = JsonElement.Parse(await File.ReadAllTextAsync(tempFilePath)); -// Deserialize the thread state after loading from storage. -AgentThread resumedThread = await agent.DeserializeThreadAsync(reloadedSerializedThread); +// Deserialize the session state after loading from storage. +AgentSession resumedSession = await agent.DeserializeSessionAsync(reloadedSerializedSession); -// Run the agent again with the resumed thread. -Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedThread)); +// Run the agent again with the resumed session. +Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedSession)); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs index 50ad22784f..b4dc0e8e0e 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs @@ -33,37 +33,37 @@ AIAgent agent = new AzureOpenAIClient( Name = "Joker", ChatHistoryProviderFactory = (ctx, ct) => new ValueTask( // 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 + // Each session 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(); +// Start a new session for the agent conversation. +AgentSession session = await agent.GetNewSessionAsync(); -// 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)); +// Run the agent with the session that stores chat history in the vector store. +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); -// Serialize the thread state, so it can be stored for later use. -// Since the chat history is stored in the vector store, the serialized thread +// Serialize the session state, so it can be stored for later use. +// Since the chat history is stored in the vector store, the serialized session // only contains the guid that the messages are stored under in the vector store. -JsonElement serializedThread = thread.Serialize(); +JsonElement serializedSession = session.Serialize(); -Console.WriteLine("\n--- Serialized thread ---\n"); -Console.WriteLine(JsonSerializer.Serialize(serializedThread, new JsonSerializerOptions { WriteIndented = true })); +Console.WriteLine("\n--- Serialized session ---\n"); +Console.WriteLine(JsonSerializer.Serialize(serializedSession, new JsonSerializerOptions { WriteIndented = true })); -// The serialized thread can now be saved to a database, file, or any other storage mechanism +// The serialized session can now be saved to a database, file, or any other storage mechanism // and loaded again later. -// Deserialize the thread state after loading from storage. -AgentThread resumedThread = await agent.DeserializeThreadAsync(serializedThread); +// Deserialize the session state after loading from storage. +AgentSession resumedSession = await agent.DeserializeSessionAsync(serializedSession); -// 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)); +// Run the agent with the session 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.", resumedSession)); -// 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()!; -Console.WriteLine($"\nThread is stored in vector store under key: {chatHistoryProvider.ThreadDbKey}"); +// We can access the VectorChatHistoryProvider via the session's GetService method if we need to read the key under which chat history is stored. +var chatHistoryProvider = resumedSession.GetService()!; +Console.WriteLine($"\nSession is stored in vector store under key: {chatHistoryProvider.SessionDbKey}"); namespace SampleApp { @@ -80,12 +80,12 @@ namespace SampleApp 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 = serializedState.Deserialize(); + // Here we can deserialize the session id so that we can access the same messages as before the suspension. + this.SessionDbKey = serializedState.Deserialize(); } } - public string? ThreadDbKey { get; private set; } + public string? SessionDbKey { get; private set; } public override async ValueTask> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default) { @@ -94,7 +94,7 @@ namespace SampleApp var records = await collection .GetAsync( - x => x.ThreadId == this.ThreadDbKey, 10, + x => x.SessionId == this.SessionDbKey, 10, new() { OrderBy = x => x.Descending(y => y.Timestamp) }, cancellationToken) .ToListAsync(cancellationToken); @@ -113,7 +113,7 @@ namespace SampleApp return; } - this.ThreadDbKey ??= Guid.NewGuid().ToString("N"); + this.SessionDbKey ??= Guid.NewGuid().ToString("N"); var collection = this._vectorStore.GetCollection("ChatHistory"); await collection.EnsureCollectionExistsAsync(cancellationToken); @@ -124,17 +124,17 @@ namespace SampleApp await collection.UpsertAsync(allNewMessages.Select(x => new ChatHistoryItem() { - Key = this.ThreadDbKey + x.MessageId, + Key = this.SessionDbKey + x.MessageId, Timestamp = DateTimeOffset.UtcNow, - ThreadId = this.ThreadDbKey, + SessionId = this.SessionDbKey, SerializedMessage = JsonSerializer.Serialize(x), MessageText = x.Text }), cancellationToken); } public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) => - // We have to serialize the thread id, so that on deserialization we can retrieve the messages using the same thread id. - JsonSerializer.SerializeToElement(this.ThreadDbKey); + // We have to serialize the session id, so that on deserialization we can retrieve the messages using the same session id. + JsonSerializer.SerializeToElement(this.SessionDbKey); /// /// The data structure used to store chat history items in the vector store. @@ -145,7 +145,7 @@ namespace SampleApp public string? Key { get; set; } [VectorStoreData] - public string? ThreadId { get; set; } + public string? SessionId { get; set; } [VectorStoreData] public DateTimeOffset? Timestamp { get; set; } diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs index ab0ac64e99..0f25862ffd 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs @@ -44,12 +44,12 @@ await host.RunAsync().ConfigureAwait(false); /// internal sealed class SampleService(AIAgent agent, IHostApplicationLifetime appLifetime) : IHostedService { - private AgentThread? _thread; + private AgentSession? _session; public async Task StartAsync(CancellationToken cancellationToken) { - // Create a thread that will be used for the entirety of the service lifetime so that the user can ask follow up questions. - this._thread = await agent.GetNewThreadAsync(cancellationToken); + // Create a session that will be used for the entirety of the service lifetime so that the user can ask follow up questions. + this._session = await agent.GetNewSessionAsync(cancellationToken); _ = this.RunAsync(appLifetime.ApplicationStopping); } @@ -72,7 +72,7 @@ internal sealed class SampleService(AIAgent agent, IHostApplicationLifetime appL } // Stream the output to the console as it is generated. - await foreach (var update in agent.RunStreamingAsync(input, this._thread, cancellationToken: cancellationToken)) + await foreach (var update in agent.RunStreamingAsync(input, this._session, cancellationToken: cancellationToken)) { Console.Write(update); } diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs index b517e3ee95..4cbff0efe0 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs @@ -22,9 +22,9 @@ ChatMessage message = new(ChatRole.User, [ new UriContent("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", "image/jpeg") ]); -var thread = await agent.GetNewThreadAsync(); +var session = await agent.GetNewSessionAsync(); -await foreach (var update in agent.RunStreamingAsync(message, thread)) +await foreach (var update in agent.RunStreamingAsync(message, session)) { Console.WriteLine(update); } diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Program.cs index fee7b2900c..9332e0f05e 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Program.cs @@ -32,41 +32,41 @@ AIAgent agent = new AzureOpenAIClient( // Enable background responses (only supported by {Azure}OpenAI Responses at this time). AgentRunOptions options = new() { AllowBackgroundResponses = true }; -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); // Start the initial run. -AgentResponse response = await agent.RunAsync("Write a very long novel about a team of astronauts exploring an uncharted galaxy.", thread, options); +AgentResponse response = await agent.RunAsync("Write a very long novel about a team of astronauts exploring an uncharted galaxy.", session, options); // Poll for background responses until complete. while (response.ContinuationToken is not null) { - PersistAgentState(thread, response.ContinuationToken); + PersistAgentState(session, response.ContinuationToken); await Task.Delay(TimeSpan.FromSeconds(10)); - var (restoredThread, continuationToken) = await RestoreAgentState(agent); + var (restoredSession, continuationToken) = await RestoreAgentState(agent); options.ContinuationToken = continuationToken; - response = await agent.RunAsync(restoredThread, options); + response = await agent.RunAsync(restoredSession, options); } Console.WriteLine(response.Text); -void PersistAgentState(AgentThread thread, ResponseContinuationToken? continuationToken) +void PersistAgentState(AgentSession? session, ResponseContinuationToken? continuationToken) { - stateStore["thread"] = thread.Serialize(); + stateStore["session"] = session!.Serialize(); stateStore["continuationToken"] = JsonSerializer.SerializeToElement(continuationToken, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ResponseContinuationToken))); } -async Task<(AgentThread Thread, ResponseContinuationToken? ContinuationToken)> RestoreAgentState(AIAgent agent) +async Task<(AgentSession Session, ResponseContinuationToken? ContinuationToken)> RestoreAgentState(AIAgent agent) { - JsonElement serializedThread = stateStore["thread"] ?? throw new InvalidOperationException("No serialized thread found in state store."); + JsonElement serializedSession = stateStore["session"] ?? throw new InvalidOperationException("No serialized session found in state store."); JsonElement? serializedToken = stateStore["continuationToken"]; - AgentThread thread = await agent.DeserializeThreadAsync(serializedThread); + AgentSession session = await agent.DeserializeSessionAsync(serializedSession); ResponseContinuationToken? continuationToken = (ResponseContinuationToken?)serializedToken?.Deserialize(AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ResponseContinuationToken))); - return (thread, continuationToken); + return (session, continuationToken); } [Description("Researches relevant space facts and scientific information for writing a science fiction novel")] diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Program.cs index 7e8f9de058..049160046e 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Program.cs @@ -45,7 +45,7 @@ var middlewareEnabledAgent = originalAgent .Use(GuardrailMiddleware, null) .Build(); -var thread = await middlewareEnabledAgent.GetNewThreadAsync(); +var session = await middlewareEnabledAgent.GetNewSessionAsync(); Console.WriteLine("\n\n=== Example 1: Wording Guardrail ==="); var guardRailedResponse = await middlewareEnabledAgent.RunAsync("Tell me something harmful."); @@ -65,7 +65,7 @@ var options = new ChatClientAgentRunOptions(new() Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))] }); -var functionCallResponse = await middlewareEnabledAgent.RunAsync("What's the current time and the weather in Seattle?", thread, options); +var functionCallResponse = await middlewareEnabledAgent.RunAsync("What's the current time and the weather in Seattle?", session, options); Console.WriteLine($"Function calling response: {functionCallResponse}"); // Special per-request middleware agent. @@ -89,7 +89,7 @@ var response = await originalAgent // Using per-request middleware pipeline with .Use(PerRequestFunctionCallingMiddleware) .Use(ConsolePromptingApprovalMiddleware, null) .Build() - .RunAsync("What's the current time and the weather in Seattle?", thread, optionsWithApproval); + .RunAsync("What's the current time and the weather in Seattle?", session, optionsWithApproval); Console.WriteLine($"Per-request middleware response: {response}"); @@ -131,13 +131,13 @@ async ValueTask PerRequestFunctionCallingMiddleware(AIAgent agent, Func } // This middleware redacts PII information from input and output messages. -async Task PIIMiddleware(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +async Task PIIMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { // Redact PII information from input messages var filteredMessages = FilterMessages(messages); Console.WriteLine("Pii Middleware - Filtered Messages Pre-Run"); - var response = await innerAgent.RunAsync(filteredMessages, thread, options, cancellationToken).ConfigureAwait(false); + var response = await innerAgent.RunAsync(filteredMessages, session, options, cancellationToken).ConfigureAwait(false); // Redact PII information from output messages response.Messages = FilterMessages(response.Messages); @@ -171,7 +171,7 @@ async Task PIIMiddleware(IEnumerable messages, Agent } // This middleware enforces guardrails by redacting certain keywords from input and output messages. -async Task GuardrailMiddleware(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +async Task GuardrailMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { // Redact keywords from input messages var filteredMessages = FilterMessages(messages); @@ -179,7 +179,7 @@ async Task GuardrailMiddleware(IEnumerable messages, Console.WriteLine("Guardrail Middleware - Filtered messages Pre-Run"); // Proceed with the agent run - var response = await innerAgent.RunAsync(filteredMessages, thread, options, cancellationToken); + var response = await innerAgent.RunAsync(filteredMessages, session, options, cancellationToken); // Redact keywords from output messages response.Messages = FilterMessages(response.Messages); @@ -208,9 +208,9 @@ async Task GuardrailMiddleware(IEnumerable messages, } // This middleware handles Human in the loop console interaction for any user approval required during function calling. -async Task ConsolePromptingApprovalMiddleware(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +async Task ConsolePromptingApprovalMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { - var response = await innerAgent.RunAsync(messages, thread, options, cancellationToken); + var response = await innerAgent.RunAsync(messages, session, options, cancellationToken); var userInputRequests = response.UserInputRequests.ToList(); @@ -229,7 +229,7 @@ async Task ConsolePromptingApprovalMiddleware(IEnumerable new ValueTask(new InMemoryChatHistoryProvider(new MessageCountingChatReducer(2), ctx.SerializedState, ctx.JsonSerializerOptions)) }); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); // Invoke the agent and output the text result. -Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); // Get the chat history to see how many messages are stored. -IList? chatHistory = thread.GetService>(); +IList? chatHistory = session.GetService>(); Console.WriteLine($"\nChat history has {chatHistory?.Count} messages.\n"); // Invoke the agent a few more times. -Console.WriteLine(await agent.RunAsync("Tell me a joke about a robot.", thread)); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a robot.", session)); Console.WriteLine($"\nChat history has {chatHistory?.Count} messages.\n"); -Console.WriteLine(await agent.RunAsync("Tell me a joke about a lemur.", thread)); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a lemur.", session)); Console.WriteLine($"\nChat history has {chatHistory?.Count} messages.\n"); // At this point, the chat history has exceeded the limit and the original message will not exist anymore, // so asking a follow up question about it will not work as expected. -Console.WriteLine(await agent.RunAsync("Tell me the joke about the pirate again, but add emojis and use the voice of a parrot.", thread)); +Console.WriteLine(await agent.RunAsync("Tell me the joke about the pirate again, but add emojis and use the voice of a parrot.", session)); Console.WriteLine($"\nChat history has {chatHistory?.Count} messages.\n"); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs index ae0151c8ab..55dbfa6199 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs @@ -19,10 +19,10 @@ AIAgent agent = new AzureOpenAIClient( // Enable background responses (only supported by OpenAI Responses at this time). AgentRunOptions options = new() { AllowBackgroundResponses = true }; -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); // Start the initial run. -AgentResponse response = await agent.RunAsync("Write a very long novel about otters in space.", thread, options); +AgentResponse response = await agent.RunAsync("Write a very long novel about otters in space.", session, options); // Poll until the response is complete. while (response.ContinuationToken is { } token) @@ -33,19 +33,19 @@ while (response.ContinuationToken is { } token) // Continue with the token. options.ContinuationToken = token; - response = await agent.RunAsync(thread, options); + response = await agent.RunAsync(session, options); } // Display the result. Console.WriteLine(response.Text); -// Reset options and thread for streaming. +// Reset options and session for streaming. options = new() { AllowBackgroundResponses = true }; -thread = await agent.GetNewThreadAsync(); +session = await agent.GetNewSessionAsync(); AgentResponseUpdate? lastReceivedUpdate = null; // Start streaming. -await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Write a very long novel about otters in space.", thread, options)) +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Write a very long novel about otters in space.", session, options)) { // Output each update. Console.Write(update.Text); @@ -63,7 +63,7 @@ await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Write a ve // Resume from interruption point. options.ContinuationToken = lastReceivedUpdate?.ContinuationToken; -await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(thread, options)) +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(session, options)) { // Output each update. Console.Write(update.Text); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Program.cs index e36612d89b..2c55c5294f 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Program.cs @@ -39,9 +39,9 @@ Console.WriteLine(); try { - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); - await foreach (var response in agent.RunStreamingAsync(Task, thread)) + await foreach (var response in agent.RunStreamingAsync(Task, session)) { Console.Write(response.Text); } diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Program.cs index 63b6f8d4fc..c3b4d6f979 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Program.cs @@ -58,20 +58,20 @@ AIAgent agent = new AzureOpenAIClient( }); // Invoke the agent and output the text result. -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("I need to pick up milk from the supermarket.", thread) + "\n"); -Console.WriteLine(await agent.RunAsync("I need to take Sally for soccer practice.", thread) + "\n"); -Console.WriteLine(await agent.RunAsync("I need to make a dentist appointment for Jimmy.", thread) + "\n"); -Console.WriteLine(await agent.RunAsync("I've taken Sally to soccer practice.", thread) + "\n"); +AgentSession session = await agent.GetNewSessionAsync(); +Console.WriteLine(await agent.RunAsync("I need to pick up milk from the supermarket.", session) + "\n"); +Console.WriteLine(await agent.RunAsync("I need to take Sally for soccer practice.", session) + "\n"); +Console.WriteLine(await agent.RunAsync("I need to make a dentist appointment for Jimmy.", session) + "\n"); +Console.WriteLine(await agent.RunAsync("I've taken Sally to soccer practice.", session) + "\n"); -// We can serialize the thread, and it will contain both the chat history and the data that each AI context provider serialized. -JsonElement serializedThread = thread.Serialize(); +// We can serialize the session, and it will contain both the chat history and the data that each AI context provider serialized. +JsonElement serializedSession = session.Serialize(); // Let's print it to console to show the contents. -Console.WriteLine(JsonSerializer.Serialize(serializedThread, options: new JsonSerializerOptions() { WriteIndented = true, IndentSize = 2 }) + "\n"); -// The serialized thread can be stored long term in a persistent store, but in this case we will just deserialize again and continue the conversation. -thread = await agent.DeserializeThreadAsync(serializedThread); +Console.WriteLine(JsonSerializer.Serialize(serializedSession, options: new JsonSerializerOptions() { WriteIndented = true, IndentSize = 2 }) + "\n"); +// The serialized session can be stored long term in a persistent store, but in this case we will just deserialize again and continue the conversation. +session = await agent.DeserializeSessionAsync(serializedSession); -Console.WriteLine(await agent.RunAsync("Considering my appointments, can you create a plan for my day that plans out when I should complete the items on my todo list?", thread) + "\n"); +Console.WriteLine(await agent.RunAsync("Considering my appointments, can you create a plan for my day that plans out when I should complete the items on my todo list?", session) + "\n"); namespace SampleApp { diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs index 07bd1b149b..f55e57cc65 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs @@ -22,25 +22,25 @@ AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deplo // Retrieve an AIAgent for the created server side agent version. ChatClientAgent jokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, options); -// Invoke the agent with a multi-turn conversation, where the context is preserved in the thread object. +// Invoke the agent with a multi-turn conversation, where the context is preserved in the session object. // Create a conversation in the server ProjectConversationsClient conversationsClient = aiProjectClient.GetProjectOpenAIClient().GetProjectConversationsClient(); ProjectConversation conversation = await conversationsClient.CreateProjectConversationAsync(); // Providing the conversation Id is not strictly necessary, but by not providing it no information will show up in the Foundry Project UI as conversations. -// Threads that doesn't have a conversation Id will work based on the `PreviousResponseId`. -AgentThread thread = await jokerAgent.GetNewThreadAsync(conversation.Id); +// Sessions that don't have a conversation Id will work based on the `PreviousResponseId`. +AgentSession session = await jokerAgent.GetNewSessionAsync(conversation.Id); -Console.WriteLine(await jokerAgent.RunAsync("Tell me a joke about a pirate.", thread)); -Console.WriteLine(await jokerAgent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", thread)); +Console.WriteLine(await jokerAgent.RunAsync("Tell me a joke about a pirate.", session)); +Console.WriteLine(await jokerAgent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session)); -// Invoke the agent with a multi-turn conversation and streaming, where the context is preserved in the thread object. -thread = await jokerAgent.GetNewThreadAsync(conversation.Id); -await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Tell me a joke about a pirate.", thread)) +// Invoke the agent with a multi-turn conversation and streaming, where the context is preserved in the session object. +session = await jokerAgent.GetNewSessionAsync(conversation.Id); +await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Tell me a joke about a pirate.", session)) { Console.WriteLine(update); } -await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", thread)) +await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session)) { Console.WriteLine(update); } diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md index 44c50f7e8a..4cbe5a5922 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md @@ -54,6 +54,6 @@ The sample will: When working with multi-turn conversations, there are two approaches: -- **With Conversation ID**: By passing a `conversation.Id` to `GetNewThreadAsync()`, the conversation will be visible in the Azure Foundry Project UI. This is useful for tracking and debugging conversations. -- **Without Conversation ID**: Threads created without a conversation ID still work correctly, maintaining context via `PreviousResponseId`. However, these conversations may not appear in the Foundry UI. +- **With Conversation ID**: By passing a `conversation.Id` to `GetNewSessionAsync()`, the conversation will be visible in the Azure Foundry Project UI. This is useful for tracking and debugging conversations. +- **Without Conversation ID**: Sessions created without a conversation ID still work correctly, maintaining context via `PreviousResponseId`. However, these conversations may not appear in the Foundry UI. diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs index 0f51bb8364..4ff34d86f6 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs @@ -37,12 +37,12 @@ var newAgent = await aiProjectClient.CreateAIAgentAsync(name: AssistantName, mod var existingAgent = await aiProjectClient.GetAIAgentAsync(name: AssistantName, tools: [tool]); // Non-streaming agent interaction with function tools. -AgentThread thread = await existingAgent.GetNewThreadAsync(); -Console.WriteLine(await existingAgent.RunAsync("What is the weather like in Amsterdam?", thread)); +AgentSession session = await existingAgent.GetNewSessionAsync(); +Console.WriteLine(await existingAgent.RunAsync("What is the weather like in Amsterdam?", session)); // Streaming agent interaction with function tools. -thread = await existingAgent.GetNewThreadAsync(); -await foreach (AgentResponseUpdate update in existingAgent.RunStreamingAsync("What is the weather like in Amsterdam?", thread)) +session = await existingAgent.GetNewSessionAsync(); +await foreach (AgentResponseUpdate update in existingAgent.RunStreamingAsync("What is the weather like in Amsterdam?", session)) { Console.WriteLine(update); } diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs index 1eb140cedc..b030ca8c64 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs @@ -32,8 +32,8 @@ AIAgent agent = await aiProjectClient.CreateAIAgentAsync(name: AssistantName, mo // Call the agent with approval-required function tools. // The agent will request approval before invoking the function. -AgentThread thread = await agent.GetNewThreadAsync(); -AgentResponse response = await agent.RunAsync("What is the weather like in Amsterdam?", thread); +AgentSession session = await agent.GetNewSessionAsync(); +AgentResponse response = await agent.RunAsync("What is the weather like in Amsterdam?", session); // Check if there are any user input requests (approvals needed). List userInputRequests = response.UserInputRequests.ToList(); @@ -53,7 +53,7 @@ while (userInputRequests.Count > 0) .ToList(); // Pass the user input responses back to the agent for further processing. - response = await agent.RunAsync(userInputMessages, thread); + response = await agent.RunAsync(userInputMessages, session); userInputRequests = response.UserInputRequests.ToList(); } diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs index 7c1c65c01d..9d55230e50 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs @@ -18,27 +18,27 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential( AIAgent agent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: JokerInstructions); -// Start a new thread for the agent conversation. -AgentThread thread = await agent.GetNewThreadAsync(); +// Start a new session for the agent conversation. +AgentSession session = await agent.GetNewSessionAsync(); -// Run the agent with a new thread. -Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); +// Run the agent with a new session. +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); -// Serialize the thread state to a JsonElement, so it can be stored for later use. -JsonElement serializedThread = thread.Serialize(); +// Serialize the session state to a JsonElement, so it can be stored for later use. +JsonElement serializedSession = session.Serialize(); -// Save the serialized thread to a temporary file (for demonstration purposes). +// Save the serialized session to a temporary file (for demonstration purposes). string tempFilePath = Path.GetTempFileName(); -await File.WriteAllTextAsync(tempFilePath, JsonSerializer.Serialize(serializedThread)); +await File.WriteAllTextAsync(tempFilePath, JsonSerializer.Serialize(serializedSession)); -// Load the serialized thread from the temporary file (for demonstration purposes). -JsonElement reloadedSerializedThread = JsonElement.Parse(await File.ReadAllTextAsync(tempFilePath))!; +// Load the serialized session from the temporary file (for demonstration purposes). +JsonElement reloadedSerializedSession = JsonElement.Parse(await File.ReadAllTextAsync(tempFilePath))!; -// Deserialize the thread state after loading from storage. -AgentThread resumedThread = await agent.DeserializeThreadAsync(reloadedSerializedThread); +// Deserialize the session state after loading from storage. +AgentSession resumedSession = await agent.DeserializeSessionAsync(reloadedSerializedSession); -// Run the agent again with the resumed thread. -Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedThread)); +// Run the agent again with the resumed session. +Console.WriteLine(await agent.RunAsync("Now tell the same joke in the voice of a pirate, and add some emojis to the joke.", resumedSession)); // Cleanup by agent name removes the agent version created. await aiProjectClient.Agents.DeleteAgentAsync(agent.Name); diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs index a247c1d0f7..1ac24011b4 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs @@ -38,12 +38,12 @@ AIAgent agent = (await aiProjectClient.CreateAIAgentAsync(name: JokerName, model .Build(); // Invoke the agent and output the text result. -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); +AgentSession session = await agent.GetNewSessionAsync(); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session)); // Invoke the agent with streaming support. -thread = await agent.GetNewThreadAsync(); -await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Tell me a joke about a pirate.", thread)) +session = await agent.GetNewSessionAsync(); +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync("Tell me a joke about a pirate.", session)) { Console.WriteLine(update); } diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs index f94acf1e38..dcded5c0ab 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs @@ -49,12 +49,12 @@ await host.RunAsync().ConfigureAwait(false); /// internal sealed class SampleService(AIProjectClient client, AIAgent agent, IHostApplicationLifetime appLifetime) : IHostedService { - private AgentThread? _thread; + private AgentSession? _session; public async Task StartAsync(CancellationToken cancellationToken) { - // Create a thread that will be used for the entirety of the service lifetime so that the user can ask follow up questions. - this._thread = await agent.GetNewThreadAsync(cancellationToken); + // Create a session that will be used for the entirety of the service lifetime so that the user can ask follow up questions. + this._session = await agent.GetNewSessionAsync(cancellationToken); _ = this.RunAsync(appLifetime.ApplicationStopping); } @@ -77,7 +77,7 @@ internal sealed class SampleService(AIProjectClient client, AIAgent agent, IHost } // Stream the output to the console as it is generated. - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, this._thread, cancellationToken: cancellationToken)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, this._session, cancellationToken: cancellationToken)) { Console.Write(update); } diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs index efaab99b8a..547998fb27 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs @@ -24,9 +24,9 @@ ChatMessage message = new(ChatRole.User, [ new DataContent(File.ReadAllBytes("assets/walkway.jpg"), "image/jpeg") ]); -AgentThread thread = await agent.GetNewThreadAsync(); +AgentSession session = await agent.GetNewSessionAsync(); -await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(message, thread)) +await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(message, session)) { Console.WriteLine(update); } diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs index e29dff2953..29a1eda312 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs @@ -39,8 +39,8 @@ AIAgent agent = await aiProjectClient.CreateAIAgentAsync( tools: [weatherAgent.AsAIFunction()]); // Invoke the agent and output the text result. -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", thread)); +AgentSession session = await agent.GetNewSessionAsync(); +Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", session)); // Cleanup by agent name removes the agent versions created. await aiProjectClient.Agents.DeleteAgentAsync(agent.Name); diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs index c1750e3387..d8c9fb3d15 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs @@ -49,7 +49,7 @@ AIAgent middlewareEnabledAgent = originalAgent .Use(GuardrailMiddleware, null) .Build(); -AgentThread thread = await middlewareEnabledAgent.GetNewThreadAsync(); +AgentSession session = await middlewareEnabledAgent.GetNewSessionAsync(); Console.WriteLine("\n\n=== Example 1: Wording Guardrail ==="); AgentResponse guardRailedResponse = await middlewareEnabledAgent.RunAsync("Tell me something harmful."); @@ -63,7 +63,7 @@ Console.WriteLine("\n\n=== Example 3: Agent function middleware ==="); // Agent function middleware support is limited to agents that wraps a upstream ChatClientAgent or derived from it. -AgentResponse functionCallResponse = await middlewareEnabledAgent.RunAsync("What's the current time and the weather in Seattle?", thread); +AgentResponse functionCallResponse = await middlewareEnabledAgent.RunAsync("What's the current time and the weather in Seattle?", session); Console.WriteLine($"Function calling response: {functionCallResponse}"); // Special per-request middleware agent. @@ -113,13 +113,13 @@ async ValueTask FunctionCallOverrideWeather(AIAgent agent, FunctionInvo } // This middleware redacts PII information from input and output messages. -async Task PIIMiddleware(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +async Task PIIMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { // Redact PII information from input messages var filteredMessages = FilterMessages(messages); Console.WriteLine("Pii Middleware - Filtered Messages Pre-Run"); - var response = await innerAgent.RunAsync(filteredMessages, thread, options, cancellationToken).ConfigureAwait(false); + var response = await innerAgent.RunAsync(filteredMessages, session, options, cancellationToken).ConfigureAwait(false); // Redact PII information from output messages response.Messages = FilterMessages(response.Messages); @@ -152,7 +152,7 @@ async Task PIIMiddleware(IEnumerable messages, Agent } // This middleware enforces guardrails by redacting certain keywords from input and output messages. -async Task GuardrailMiddleware(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +async Task GuardrailMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { // Redact keywords from input messages var filteredMessages = FilterMessages(messages); @@ -160,7 +160,7 @@ async Task GuardrailMiddleware(IEnumerable messages, Console.WriteLine("Guardrail Middleware - Filtered messages Pre-Run"); // Proceed with the agent run - var response = await innerAgent.RunAsync(filteredMessages, thread, options, cancellationToken); + var response = await innerAgent.RunAsync(filteredMessages, session, options, cancellationToken); // Redact keywords from output messages response.Messages = FilterMessages(response.Messages); @@ -189,9 +189,9 @@ async Task GuardrailMiddleware(IEnumerable messages, } // This middleware handles Human in the loop console interaction for any user approval required during function calling. -async Task ConsolePromptingApprovalMiddleware(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) +async Task ConsolePromptingApprovalMiddleware(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { - AgentResponse response = await innerAgent.RunAsync(messages, thread, options, cancellationToken); + AgentResponse response = await innerAgent.RunAsync(messages, session, options, cancellationToken); List userInputRequests = response.UserInputRequests.ToList(); @@ -211,7 +211,7 @@ async Task ConsolePromptingApprovalMiddleware(IEnumerable 0) @@ -98,7 +98,7 @@ while (userInputRequests.Count > 0) .ToList(); // Pass the user input responses back to the agent for further processing. - response = await agentWithRequiredApproval.RunAsync(userInputResponses, threadWithRequiredApproval); + response = await agentWithRequiredApproval.RunAsync(userInputResponses, sessionWithRequiredApproval); userInputRequests = response.UserInputRequests.ToList(); } diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs index 986e7d0977..c8edf00d03 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs @@ -37,8 +37,8 @@ AIAgent agent = new AzureOpenAIClient( tools: [mcpTool]); // You can then invoke the agent like any other AIAgent. -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", thread)); +AgentSession session = await agent.GetNewSessionAsync(); +Console.WriteLine(await agent.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", session)); // **** MCP Tool with Approval Required **** // ***************************************** @@ -64,8 +64,8 @@ AIAgent agentWithRequiredApproval = new AzureOpenAIClient( tools: [mcpToolWithApproval]); // You can then invoke the agent like any other AIAgent. -var threadWithRequiredApproval = await agentWithRequiredApproval.GetNewThreadAsync(); -var response = await agentWithRequiredApproval.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", threadWithRequiredApproval); +var sessionWithRequiredApproval = await agentWithRequiredApproval.GetNewSessionAsync(); +var response = await agentWithRequiredApproval.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", sessionWithRequiredApproval); var userInputRequests = response.UserInputRequests.ToList(); while (userInputRequests.Count > 0) @@ -87,7 +87,7 @@ while (userInputRequests.Count > 0) .ToList(); // Pass the user input responses back to the agent for further processing. - response = await agentWithRequiredApproval.RunAsync(userInputResponses, threadWithRequiredApproval); + response = await agentWithRequiredApproval.RunAsync(userInputResponses, sessionWithRequiredApproval); userInputRequests = response.UserInputRequests.ToList(); } diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs index 594f447e8c..9689b1c306 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs @@ -109,7 +109,7 @@ internal sealed class SloganGeneratedEvent(SloganResult sloganResult) : Workflow internal sealed class SloganWriterExecutor : Executor { private readonly AIAgent _agent; - private AgentThread? _thread; + private AgentSession? _session; /// /// Initializes a new instance of the class. @@ -136,9 +136,9 @@ internal sealed class SloganWriterExecutor : Executor public async ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) { - this._thread ??= await this._agent.GetNewThreadAsync(cancellationToken); + this._session ??= await this._agent.GetNewSessionAsync(cancellationToken); - var result = await this._agent.RunAsync(message, this._thread, cancellationToken: cancellationToken); + var result = await this._agent.RunAsync(message, this._session, cancellationToken: cancellationToken); var sloganResult = JsonSerializer.Deserialize(result.Text) ?? throw new InvalidOperationException("Failed to deserialize slogan result."); @@ -157,7 +157,7 @@ internal sealed class SloganWriterExecutor : Executor Please use this feedback to improve your slogan. """; - var result = await this._agent.RunAsync(feedbackMessage, this._thread, cancellationToken: cancellationToken); + var result = await this._agent.RunAsync(feedbackMessage, this._session, cancellationToken: cancellationToken); var sloganResult = JsonSerializer.Deserialize(result.Text) ?? throw new InvalidOperationException("Failed to deserialize slogan result."); await context.AddEventAsync(new SloganGeneratedEvent(sloganResult), cancellationToken); @@ -180,7 +180,7 @@ internal sealed class FeedbackEvent(FeedbackResult feedbackResult) : WorkflowEve internal sealed class FeedbackExecutor : Executor { private readonly AIAgent _agent; - private AgentThread? _thread; + private AgentSession? _session; public int MinimumRating { get; init; } = 8; @@ -209,7 +209,7 @@ internal sealed class FeedbackExecutor : Executor public override async ValueTask HandleAsync(SloganResult message, IWorkflowContext context, CancellationToken cancellationToken = default) { - this._thread ??= await this._agent.GetNewThreadAsync(cancellationToken); + this._session ??= await this._agent.GetNewSessionAsync(cancellationToken); var sloganMessage = $""" Here is a slogan for the task '{message.Task}': @@ -217,7 +217,7 @@ internal sealed class FeedbackExecutor : Executor Please provide feedback on this slogan, including comments, a rating from 1 to 10, and suggested actions for improvement. """; - var response = await this._agent.RunAsync(sloganMessage, this._thread, cancellationToken: cancellationToken); + var response = await this._agent.RunAsync(sloganMessage, this._session, cancellationToken: cancellationToken); var feedback = JsonSerializer.Deserialize(response.Text) ?? throw new InvalidOperationException("Failed to deserialize feedback."); await context.AddEventAsync(new FeedbackEvent(feedback), cancellationToken); diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs index 4ba04bd87f..908ed3b914 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs @@ -37,7 +37,7 @@ public static class Program // Create the workflow and turn it into an agent var workflow = WorkflowFactory.BuildWorkflow(chatClient); var agent = workflow.AsAgent("workflow-agent", "Workflow Agent"); - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); // Start an interactive loop to interact with the workflow as if it were an agent while (true) @@ -50,16 +50,16 @@ public static class Program break; } - await ProcessInputAsync(agent, thread, input); + await ProcessInputAsync(agent, session, input); } // Helper method to process user input and display streaming responses. To display // multiple interleaved responses correctly, we buffer updates by message ID and // re-render all messages on each update. - static async Task ProcessInputAsync(AIAgent agent, AgentThread thread, string input) + static async Task ProcessInputAsync(AIAgent agent, AgentSession? session, string input) { Dictionary> buffer = []; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, thread)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, session)) { if (update.MessageId is null || string.IsNullOrEmpty(update.Text)) { diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/Program.cs index 5d76a0e319..96d728b487 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/Program.cs @@ -47,7 +47,7 @@ internal sealed class Program AIAgent agent = aiProjectClient.AsAIAgent(agentVersion); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ProjectConversation conversation = await aiProjectClient @@ -65,7 +65,7 @@ internal sealed class Program }; ChatClientAgentRunOptions runOptions = new(chatOptions); - IAsyncEnumerable agentResponseUpdates = agent.RunStreamingAsync(workflowInput, thread, runOptions); + IAsyncEnumerable agentResponseUpdates = agent.RunStreamingAsync(workflowInput, session, runOptions); string? lastMessageId = null; await foreach (AgentResponseUpdate responseUpdate in agentResponseUpdates) diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/Program.cs index bf9f17ac80..806fad421b 100644 --- a/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/Program.cs @@ -90,7 +90,7 @@ public static class Program { EnableSensitiveData = true // enable sensitive data at the agent level such as prompts and responses }; - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); // Start an interactive loop to interact with the workflow as if it were an agent while (true) @@ -103,16 +103,16 @@ public static class Program break; } - await ProcessInputAsync(agent, thread, input); + await ProcessInputAsync(agent, session, input); } // Helper method to process user input and display streaming responses. To display // multiple interleaved responses correctly, we buffer updates by message ID and // re-render all messages on each update. - static async Task ProcessInputAsync(AIAgent agent, AgentThread thread, string input) + static async Task ProcessInputAsync(AIAgent agent, AgentSession? session, string input) { Dictionary> buffer = []; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, thread)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, session)) { if (update.MessageId is null || string.IsNullOrEmpty(update.Text)) { diff --git a/dotnet/samples/M365Agent/AFAgentApplication.cs b/dotnet/samples/M365Agent/AFAgentApplication.cs index 0962c1d6d2..9b7d25abe4 100644 --- a/dotnet/samples/M365Agent/AFAgentApplication.cs +++ b/dotnet/samples/M365Agent/AFAgentApplication.cs @@ -39,17 +39,17 @@ internal sealed class AFAgentApplication : AgentApplication await turnContext.StreamingResponse.QueueInformativeUpdateAsync("Working on a response for you", cancellationToken); // Get the conversation history from turn state. - JsonElement threadElementStart = turnState.GetValue("conversation.chatHistory"); + JsonElement sessionElementStart = turnState.GetValue("conversation.chatHistory"); - // Deserialize the conversation history into an AgentThread, or create a new one if none exists. - AgentThread agentThread = threadElementStart.ValueKind is not JsonValueKind.Undefined and not JsonValueKind.Null - ? await this._agent.DeserializeThreadAsync(threadElementStart, JsonUtilities.DefaultOptions, cancellationToken) - : await this._agent.GetNewThreadAsync(cancellationToken); + // Deserialize the conversation history into an AgentSession, or create a new one if none exists. + AgentSession agentSession = sessionElementStart.ValueKind is not JsonValueKind.Undefined and not JsonValueKind.Null + ? await this._agent.DeserializeSessionAsync(sessionElementStart, JsonUtilities.DefaultOptions, cancellationToken) + : await this._agent.GetNewSessionAsync(cancellationToken); ChatMessage chatMessage = HandleUserInput(turnContext); // Invoke the WeatherForecastAgent to process the message - AgentResponse agentResponse = await this._agent.RunAsync(chatMessage, agentThread, cancellationToken: cancellationToken); + AgentResponse agentResponse = await this._agent.RunAsync(chatMessage, agentSession, cancellationToken: cancellationToken); // Check for any user input requests in the response // and turn them into adaptive cards in the streaming response. @@ -80,8 +80,8 @@ internal sealed class AFAgentApplication : AgentApplication } // Serialize and save the updated conversation history back to turn state. - JsonElement threadElementEnd = agentThread.Serialize(JsonUtilities.DefaultOptions); - turnState.SetValue("conversation.chatHistory", threadElementEnd); + JsonElement sessionElementEnd = agentSession.Serialize(JsonUtilities.DefaultOptions); + turnState.SetValue("conversation.chatHistory", sessionElementEnd); // End the streaming response await turnContext.StreamingResponse.EndStreamAsync(cancellationToken); diff --git a/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs b/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs index a4023a2c58..5968e4eb28 100644 --- a/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs +++ b/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs @@ -48,9 +48,9 @@ public class WeatherForecastAgent : DelegatingAIAgent { } - protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - var response = await base.RunCoreAsync(messages, thread, options, cancellationToken); + var response = await base.RunCoreAsync(messages, session, options, cancellationToken); // If the agent returned a valid structured output response // we might be able to enhance the response with an adaptive card. diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs index 01e7c8f146..bdb667d195 100644 --- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs @@ -52,27 +52,27 @@ public sealed class A2AAgent : AIAgent } /// - public sealed override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) - => new(new A2AAgentThread()); + public sealed override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) + => new(new A2AAgentSession()); /// - /// Get a new instance using an existing context id, to continue that conversation. + /// Get a new instance using an existing context id, to continue that conversation. /// /// The context id to continue. - /// A value task representing the asynchronous operation. The task result contains a new instance. - public ValueTask GetNewThreadAsync(string contextId) - => new(new A2AAgentThread() { ContextId = contextId }); + /// A value task representing the asynchronous operation. The task result contains a new instance. + public ValueTask GetNewSessionAsync(string contextId) + => new(new A2AAgentSession() { ContextId = contextId }); /// - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new A2AAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new A2AAgentSession(serializedSession, jsonSerializerOptions)); /// - protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { _ = Throw.IfNull(messages); - A2AAgentThread typedThread = await this.GetA2AThreadAsync(thread, options, cancellationToken).ConfigureAwait(false); + A2AAgentSession typedSession = await this.GetA2ASessionAsync(session, options, cancellationToken).ConfigureAwait(false); this._logger.LogA2AAgentInvokingAgent(nameof(RunAsync), this.Id, this.Name); @@ -86,7 +86,7 @@ public sealed class A2AAgent : AIAgent { MessageSendParams sendParams = new() { - Message = CreateA2AMessage(typedThread, messages), + Message = CreateA2AMessage(typedSession, messages), Metadata = options?.AdditionalProperties?.ToA2AMetadata() }; @@ -97,7 +97,7 @@ public sealed class A2AAgent : AIAgent if (a2aResponse is AgentMessage message) { - UpdateThread(typedThread, message.ContextId); + UpdateSession(typedSession, message.ContextId); return new AgentResponse { @@ -111,7 +111,7 @@ public sealed class A2AAgent : AIAgent if (a2aResponse is AgentTask agentTask) { - UpdateThread(typedThread, agentTask.ContextId, agentTask.Id); + UpdateSession(typedSession, agentTask.ContextId, agentTask.Id); var response = new AgentResponse { @@ -135,11 +135,11 @@ public sealed class A2AAgent : AIAgent } /// - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { _ = Throw.IfNull(messages); - A2AAgentThread typedThread = await this.GetA2AThreadAsync(thread, options, cancellationToken).ConfigureAwait(false); + A2AAgentSession typedSession = await this.GetA2ASessionAsync(session, options, cancellationToken).ConfigureAwait(false); this._logger.LogA2AAgentInvokingAgent(nameof(RunStreamingAsync), this.Id, this.Name); @@ -160,7 +160,7 @@ public sealed class A2AAgent : AIAgent MessageSendParams sendParams = new() { - Message = CreateA2AMessage(typedThread, messages), + Message = CreateA2AMessage(typedSession, messages), Metadata = options?.AdditionalProperties?.ToA2AMetadata() }; @@ -199,7 +199,7 @@ public sealed class A2AAgent : AIAgent } } - UpdateThread(typedThread, contextId, taskId); + UpdateSession(typedSession, contextId, taskId); } /// @@ -211,57 +211,57 @@ public sealed class A2AAgent : AIAgent /// public override string? Description => this._description; - private async ValueTask GetA2AThreadAsync(AgentThread? thread, AgentRunOptions? options, CancellationToken cancellationToken) + private async ValueTask GetA2ASessionAsync(AgentSession? session, AgentRunOptions? options, CancellationToken cancellationToken) { // Aligning with other agent implementations that support background responses, where - // a thread is required for background responses to prevent inconsistent experience - // for callers if they forget to provide the thread for initial or follow-up runs. - if (options?.AllowBackgroundResponses is true && thread is null) + // a session is required for background responses to prevent inconsistent experience + // for callers if they forget to provide the session for initial or follow-up runs. + if (options?.AllowBackgroundResponses is true && session is null) { - throw new InvalidOperationException("A thread must be provided when AllowBackgroundResponses is enabled."); + throw new InvalidOperationException("A session must be provided when AllowBackgroundResponses is enabled."); } - thread ??= await this.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); + session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); - if (thread is not A2AAgentThread typedThread) + if (session is not A2AAgentSession typedSession) { - throw new InvalidOperationException($"The provided thread type {thread.GetType()} is not compatible with the agent. Only A2A agent created threads are supported."); + throw new InvalidOperationException($"The provided session type {session.GetType()} is not compatible with the agent. Only A2A agent created sessions are supported."); } - return typedThread; + return typedSession; } - private static void UpdateThread(A2AAgentThread? thread, string? contextId, string? taskId = null) + private static void UpdateSession(A2AAgentSession? session, string? contextId, string? taskId = null) { - if (thread is null) + if (session is null) { return; } // Surface cases where the A2A agent responds with a response that - // has a different context Id than the thread's conversation Id. - if (thread.ContextId is not null && contextId is not null && thread.ContextId != contextId) + // has a different context Id than the session's conversation Id. + if (session.ContextId is not null && contextId is not null && session.ContextId != contextId) { throw new InvalidOperationException( - $"The {nameof(contextId)} returned from the A2A agent is different from the conversation Id of the provided {nameof(AgentThread)}."); + $"The {nameof(contextId)} returned from the A2A agent is different from the conversation Id of the provided {nameof(AgentSession)}."); } - // Assign a server-generated context Id to the thread if it's not already set. - thread.ContextId ??= contextId; - thread.TaskId = taskId; + // Assign a server-generated context Id to the session if it's not already set. + session.ContextId ??= contextId; + session.TaskId = taskId; } - private static AgentMessage CreateA2AMessage(A2AAgentThread typedThread, IEnumerable messages) + private static AgentMessage CreateA2AMessage(A2AAgentSession typedSession, IEnumerable messages) { var a2aMessage = messages.ToA2AMessage(); // Linking the message to the existing conversation, if any. // See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#group-related-interactions - a2aMessage.ContextId = typedThread.ContextId; + a2aMessage.ContextId = typedSession.ContextId; // Link the message as a follow-up to an existing task, if any. // See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#task-refinements - a2aMessage.ReferenceTaskIds = typedThread.TaskId is null ? null : [typedThread.TaskId]; + a2aMessage.ReferenceTaskIds = typedSession.TaskId is null ? null : [typedSession.TaskId]; return a2aMessage; } diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgentThread.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgentSession.cs similarity index 63% rename from dotnet/src/Microsoft.Agents.AI.A2A/A2AAgentThread.cs rename to dotnet/src/Microsoft.Agents.AI.A2A/A2AAgentSession.cs index 55942c8dd1..6b415b5b1a 100644 --- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgentSession.cs @@ -6,23 +6,23 @@ using System.Text.Json; namespace Microsoft.Agents.AI.A2A; /// -/// Thread for A2A based agents. +/// Session for A2A based agents. /// -public sealed class A2AAgentThread : AgentThread +public sealed class A2AAgentSession : AgentSession { - internal A2AAgentThread() + internal A2AAgentSession() { } - internal A2AAgentThread(JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null) + internal A2AAgentSession(JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null) { - if (serializedThreadState.ValueKind != JsonValueKind.Object) + if (serializedSessionState.ValueKind != JsonValueKind.Object) { - throw new ArgumentException("The serialized thread state must be a JSON object.", nameof(serializedThreadState)); + throw new ArgumentException("The serialized session state must be a JSON object.", nameof(serializedSessionState)); } - var state = serializedThreadState.Deserialize( - A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(A2AAgentThreadState))) as A2AAgentThreadState; + var state = serializedSessionState.Deserialize( + A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(A2AAgentSessionState))) as A2AAgentSessionState; if (state?.ContextId is string contextId) { @@ -48,16 +48,16 @@ public sealed class A2AAgentThread : AgentThread /// public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { - var state = new A2AAgentThreadState + var state = new A2AAgentSessionState { ContextId = this.ContextId, TaskId = this.TaskId }; - return JsonSerializer.SerializeToElement(state, A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(A2AAgentThreadState))); + return JsonSerializer.SerializeToElement(state, A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(A2AAgentSessionState))); } - internal sealed class A2AAgentThreadState + internal sealed class A2AAgentSessionState { public string? ContextId { get; set; } diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AJsonUtilities.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AJsonUtilities.cs index 2fbb2e8617..5f079d9573 100644 --- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AJsonUtilities.cs +++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AJsonUtilities.cs @@ -74,7 +74,7 @@ public static partial class A2AJsonUtilities NumberHandling = JsonNumberHandling.AllowReadingFromString)] // A2A agent types - [JsonSerializable(typeof(A2AAgentThread.A2AAgentThreadState))] + [JsonSerializable(typeof(A2AAgentSession.A2AAgentSessionState))] [ExcludeFromCodeCoverage] private sealed partial class JsonContext : JsonSerializerContext; } diff --git a/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs index 37c9c60413..ddaf6bd592 100644 --- a/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs +++ b/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs @@ -110,7 +110,7 @@ public sealed class AGUIChatClient : DelegatingChatClient firstUpdate = update; if (firstUpdate.AdditionalProperties?.TryGetValue("agui_thread_id", out string? threadId) is true) { - // Capture the thread id from the first update to use as conversation id if none was provided + // Capture the session id from the first update to use as conversation id if none was provided conversationId = threadId; } } @@ -276,7 +276,7 @@ public sealed class AGUIChatClient : DelegatingChatClient } } - // Extract the thread id from the options additional properties + // Extract the session id from the options additional properties private static string? ExtractThreadIdFromOptions(ChatOptions? options) { if (options?.AdditionalProperties is null || @@ -288,7 +288,7 @@ public sealed class AGUIChatClient : DelegatingChatClient return threadId; } - // Extract the thread id from the second last message's function call content additional properties + // Extract the session id from the second last message's function call content additional properties private static string? ExtractTemporaryThreadId(List messagesList) { if (messagesList.Count < 2) diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs index 3314177bf1..9ec8d6b7e9 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs @@ -107,67 +107,67 @@ public abstract class AIAgent => this.GetService(typeof(TService), serviceKey) is TService service ? service : default; /// - /// Creates a new conversation thread that is compatible with this agent. + /// Creates a new conversation session that is compatible with this agent. /// /// The to monitor for cancellation requests. The default is . - /// A value task that represents the asynchronous operation. The task result contains a new instance ready for use with this agent. + /// A value task that represents the asynchronous operation. The task result contains a new instance ready for use with this agent. /// /// - /// This method creates a fresh conversation thread that can be used to maintain state - /// and context for interactions with this agent. Each thread represents an independent + /// This method creates a fresh conversation session that can be used to maintain state + /// and context for interactions with this agent. Each session represents an independent /// conversation session. /// /// - /// If the agent supports multiple thread types, this method returns the default or - /// configured thread type. For service-backed agents, the actual thread creation + /// If the agent supports multiple session types, this method returns the default or + /// configured session type. For service-backed agents, the actual session creation /// may be deferred until first use to optimize performance. /// /// - public abstract ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default); + public abstract ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default); /// - /// Deserializes an agent thread from its JSON serialized representation. + /// Deserializes an agent session from its JSON serialized representation. /// - /// A containing the serialized thread state. + /// A containing the serialized session state. /// Optional settings to customize the deserialization process. /// The to monitor for cancellation requests. The default is . - /// A value task that represents the asynchronous operation. The task result contains a restored instance with the state from . - /// The is not in the expected format. + /// A value task that represents the asynchronous operation. The task result contains a restored instance with the state from . + /// The is not in the expected format. /// The serialized data is invalid or cannot be deserialized. /// - /// This method enables restoration of conversation threads from previously saved state, + /// This method enables restoration of conversation sessions from previously saved state, /// allowing conversations to resume across application restarts or be migrated between /// different agent instances. /// - public abstract ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default); + public abstract ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default); /// - /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread. + /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the session. /// - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous operation. The task result contains an with the agent's output. /// - /// This overload is useful when the agent has sufficient context from previous messages in the thread + /// This overload is useful when the agent has sufficient context from previous messages in the session /// or from its initial configuration to generate a meaningful response without additional input. /// public Task RunAsync( - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - this.RunAsync([], thread, options, cancellationToken); + this.RunAsync([], session, options, cancellationToken); /// /// Runs the agent with a text message from the user. /// /// The user message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -179,22 +179,22 @@ public abstract class AIAgent /// public Task RunAsync( string message, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { _ = Throw.IfNullOrWhitespace(message); - return this.RunAsync(new ChatMessage(ChatRole.User, message), thread, options, cancellationToken); + return this.RunAsync(new ChatMessage(ChatRole.User, message), session, options, cancellationToken); } /// /// Runs the agent with a single chat message. /// /// The chat message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -202,22 +202,22 @@ public abstract class AIAgent /// is . public Task RunAsync( ChatMessage message, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { _ = Throw.IfNull(message); - return this.RunAsync([message], thread, options, cancellationToken); + return this.RunAsync([message], session, options, cancellationToken); } /// /// Runs the agent with a collection of chat messages, providing the core invocation logic that all other overloads delegate to. /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -230,23 +230,23 @@ public abstract class AIAgent /// /// /// The messages are processed in the order provided and become part of the conversation history. - /// The agent's response will also be added to if one is provided. + /// The agent's response will also be added to if one is provided. /// /// public Task RunAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - this.RunCoreAsync(messages, thread, options, cancellationToken); + this.RunCoreAsync(messages, session, options, cancellationToken); /// /// Core implementation of the agent invocation logic with a collection of chat messages. /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -259,38 +259,38 @@ public abstract class AIAgent /// /// /// The messages are processed in the order provided and become part of the conversation history. - /// The agent's response will also be added to if one is provided. + /// The agent's response will also be added to if one is provided. /// /// protected abstract Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default); /// /// Runs the agent in streaming mode without providing new input messages, relying on existing context and instructions. /// - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// An asynchronous enumerable of instances representing the streaming response. public IAsyncEnumerable RunStreamingAsync( - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - this.RunStreamingAsync([], thread, options, cancellationToken); + this.RunStreamingAsync([], session, options, cancellationToken); /// /// Runs the agent in streaming mode with a text message from the user. /// /// The user message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -302,22 +302,22 @@ public abstract class AIAgent /// public IAsyncEnumerable RunStreamingAsync( string message, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { _ = Throw.IfNullOrWhitespace(message); - return this.RunStreamingAsync(new ChatMessage(ChatRole.User, message), thread, options, cancellationToken); + return this.RunStreamingAsync(new ChatMessage(ChatRole.User, message), session, options, cancellationToken); } /// /// Runs the agent in streaming mode with a single chat message. /// /// The chat message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -325,22 +325,22 @@ public abstract class AIAgent /// is . public IAsyncEnumerable RunStreamingAsync( ChatMessage message, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { _ = Throw.IfNull(message); - return this.RunStreamingAsync([message], thread, options, cancellationToken); + return this.RunStreamingAsync([message], session, options, cancellationToken); } /// /// Runs the agent in streaming mode with a collection of chat messages, providing the core streaming invocation logic. /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response updates generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response updates generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -357,18 +357,18 @@ public abstract class AIAgent /// public IAsyncEnumerable RunStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - this.RunCoreStreamingAsync(messages, thread, options, cancellationToken); + this.RunCoreStreamingAsync(messages, session, options, cancellationToken); /// /// Core implementation of the agent streaming invocation logic with a collection of chat messages. /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response updates generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response updates generated during invocation. /// /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . @@ -385,7 +385,7 @@ public abstract class AIAgent /// protected abstract IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default); } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs index 3d420fb573..17fbb9e4c6 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs @@ -80,8 +80,8 @@ public static partial class AgentAbstractionsJsonUtilities [JsonSerializable(typeof(AgentResponse[]))] [JsonSerializable(typeof(AgentResponseUpdate))] [JsonSerializable(typeof(AgentResponseUpdate[]))] - [JsonSerializable(typeof(ServiceIdAgentThread.ServiceIdAgentThreadState))] - [JsonSerializable(typeof(InMemoryAgentThread.InMemoryAgentThreadState))] + [JsonSerializable(typeof(ServiceIdAgentSession.ServiceIdAgentSessionState))] + [JsonSerializable(typeof(InMemoryAgentSession.InMemoryAgentSessionState))] [JsonSerializable(typeof(InMemoryChatHistoryProvider.State))] [ExcludeFromCodeCoverage] diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs index dbded1ef88..ba6068c554 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs @@ -171,7 +171,7 @@ public class AgentResponse /// /// This property should be used in conjunction with to /// continue to poll for the completion of the response. Pass this token to - /// on subsequent calls to + /// on subsequent calls to /// to poll for completion. /// /// diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs index 041af06593..b1e5c88ad7 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs @@ -155,7 +155,7 @@ public class AgentResponseUpdate /// except for the last update, for which the token will be . /// /// This property should be used for stream resumption, where the continuation token of the latest received update should be - /// passed to on subsequent calls to + /// passed to on subsequent calls to /// to resume streaming from the point of interruption. /// /// diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs index 5fea157b75..611d15036c 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs @@ -42,12 +42,12 @@ public class AgentRunOptions /// /// This property is used for background responses that can be activated via the /// property if the implementation supports them. - /// Streamed background responses, such as those returned by default by + /// Streamed background responses, such as those returned by default by /// can be resumed if interrupted. This means that a continuation token obtained from the /// of an update just before the interruption occurred can be passed to this property to resume the stream from the point of interruption. - /// Non-streamed background responses, such as those returned by , + /// Non-streamed background responses, such as those returned by , /// can be polled for completion by obtaining the token from the property - /// and passing it via this property on subsequent calls to . + /// and passing it via this property on subsequent calls to . /// public ResponseContinuationToken? ContinuationToken { get; set; } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThread.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSession.cs similarity index 65% rename from dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThread.cs rename to dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSession.cs index 579202c368..b722582668 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSession.cs @@ -11,7 +11,7 @@ namespace Microsoft.Agents.AI; /// /// /// -/// An contains the state of a specific conversation with an agent which may include: +/// An contains the state of a specific conversation with an agent which may include: /// /// Conversation history or a reference to externally stored conversation history. /// Memories or a reference to externally stored memories. @@ -19,37 +19,37 @@ namespace Microsoft.Agents.AI; /// /// /// -/// An may also have behaviors attached to it that may include: +/// An may also have behaviors attached to it that may include: /// /// Customized storage of state. /// Data extraction from and injection into a conversation. /// Chat history reduction, e.g. where messages needs to be summarized or truncated to reduce the size. /// -/// An is always constructed by an so that the -/// can attach any necessary behaviors to the . See the -/// and methods for more information. +/// An is always constructed by an so that the +/// can attach any necessary behaviors to the . See the +/// and methods for more information. /// /// -/// Because of these behaviors, an may not be reusable across different agents, since each agent -/// may add different behaviors to the it creates. +/// Because of these behaviors, an may not be reusable across different agents, since each agent +/// may add different behaviors to the it creates. /// /// -/// To support conversations that may need to survive application restarts or separate service requests, an can be serialized +/// To support conversations that may need to survive application restarts or separate service requests, an can be serialized /// and deserialized, so that it can be saved in a persistent store. -/// The provides the method to serialize the thread to a -/// and the method -/// can be used to deserialize the thread. +/// The provides the method to serialize the session to a +/// and the method +/// can be used to deserialize the session. /// /// /// -/// -/// -public abstract class AgentThread +/// +/// +public abstract class AgentSession { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - protected AgentThread() + protected AgentSession() { } @@ -61,13 +61,13 @@ public abstract class AgentThread public virtual JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) => default; - /// Asks the for an object of the specified type . + /// 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 , + /// 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. For example, to access a if available for the instance, /// may be used to request it. /// @@ -80,12 +80,12 @@ public abstract class AgentThread : null; } - /// Asks the for an object of type . + /// 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 , + /// 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) diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs index 1b75e45629..d809582ea4 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs @@ -61,7 +61,7 @@ public abstract class ChatHistoryProvider /// /// /// - /// Each instance should be associated with a single to ensure proper message isolation + /// Each instance should be associated with a single to ensure proper message isolation /// and context management. /// /// diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs index e9a3d5bc7a..9e7bda3f28 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs @@ -74,25 +74,25 @@ public abstract class DelegatingAIAgent : AIAgent } /// - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => this.InnerAgent.GetNewThreadAsync(cancellationToken); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => this.InnerAgent.GetNewSessionAsync(cancellationToken); /// - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => this.InnerAgent.DeserializeThreadAsync(serializedThread, jsonSerializerOptions, cancellationToken); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => this.InnerAgent.DeserializeSessionAsync(serializedSession, jsonSerializerOptions, cancellationToken); /// protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => this.InnerAgent.RunAsync(messages, thread, options, cancellationToken); + => this.InnerAgent.RunAsync(messages, session, options, cancellationToken); /// protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); + => this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken); } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentThread.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentSession.cs similarity index 75% rename from dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentThread.cs rename to dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentSession.cs index cc909b936c..f74c4b72a8 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentSession.cs @@ -9,11 +9,11 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; /// -/// Provides an abstract base class for an that maintain all chat history in local memory. +/// 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 +/// 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. /// @@ -23,65 +23,65 @@ namespace Microsoft.Agents.AI; /// /// [DebuggerDisplay("{DebuggerDisplay,nq}")] -public abstract class InMemoryAgentThread : AgentThread +public abstract class InMemoryAgentSession : AgentSession { /// - /// Initializes a new instance of the class. + /// 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 + /// This constructor allows sharing of between sessions or providing pre-configured /// with specific reduction or processing logic. /// - protected InMemoryAgentThread(InMemoryChatHistoryProvider? chatHistoryProvider = null) + protected InMemoryAgentSession(InMemoryChatHistoryProvider? chatHistoryProvider = null) { this.ChatHistoryProvider = chatHistoryProvider ?? []; } /// - /// Initializes a new instance of the class. + /// 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 + /// This constructor is useful for initializing sessions with existing conversation history or /// for migrating conversations from other storage systems. /// - protected InMemoryAgentThread(IEnumerable messages) + protected InMemoryAgentSession(IEnumerable messages) { this.ChatHistoryProvider = [.. messages]; } /// - /// Initializes a new instance of the class from previously serialized state. + /// Initializes a new instance of the class from previously serialized state. /// - /// A representing the serialized state of the thread. + /// A representing the serialized state of the session. /// 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. + /// 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, + protected InMemoryAgentSession( + JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null, Func? chatHistoryProviderFactory = null) { - if (serializedThreadState.ValueKind != JsonValueKind.Object) + if (serializedSessionState.ValueKind != JsonValueKind.Object) { - throw new ArgumentException("The serialized thread state must be a JSON object.", nameof(serializedThreadState)); + throw new ArgumentException("The serialized session state must be a JSON object.", nameof(serializedSessionState)); } - var state = serializedThreadState.Deserialize( - AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(InMemoryAgentThreadState))) as InMemoryAgentThreadState; + var state = serializedSessionState.Deserialize( + AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(InMemoryAgentSessionState))) as InMemoryAgentSessionState; this.ChatHistoryProvider = chatHistoryProviderFactory?.Invoke(state?.ChatHistoryProviderState ?? default, jsonSerializerOptions) ?? @@ -102,12 +102,12 @@ public abstract class InMemoryAgentThread : AgentThread { var chatHistoryProviderState = this.ChatHistoryProvider.Serialize(jsonSerializerOptions); - var state = new InMemoryAgentThreadState + var state = new InMemoryAgentSessionState { ChatHistoryProviderState = chatHistoryProviderState, }; - return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(InMemoryAgentThreadState))); + return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(InMemoryAgentSessionState))); } /// @@ -117,7 +117,7 @@ public abstract class InMemoryAgentThread : AgentThread [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => $"Count = {this.ChatHistoryProvider.Count}"; - internal sealed class InMemoryAgentThreadState + internal sealed class InMemoryAgentSessionState { public JsonElement? ChatHistoryProviderState { get; set; } } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentThread.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentSession.cs similarity index 53% rename from dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentThread.cs rename to dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentSession.cs index 22f9f98a83..3701c75c1d 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentSession.cs @@ -8,63 +8,63 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Provides a base class for agent threads that store conversation state remotely in a service and maintain only an identifier reference locally. +/// Provides a base class for agent sessions that store conversation state remotely in a service and maintain only an identifier reference locally. /// /// /// This class is designed for scenarios where conversation state is managed by an external service (such as a cloud-based AI service) -/// rather than being stored locally. The thread maintains only the service identifier needed to reference the remote conversation state. +/// rather than being stored locally. The session maintains only the service identifier needed to reference the remote conversation state. /// -[DebuggerDisplay("ServiceThreadId = {ServiceThreadId}")] -public abstract class ServiceIdAgentThread : AgentThread +[DebuggerDisplay("ServiceSessionId = {ServiceSessionId}")] +public abstract class ServiceIdAgentSession : AgentSession { /// - /// Initializes a new instance of the class without a service thread identifier. + /// Initializes a new instance of the class without a service session identifier. /// /// - /// When using this constructor, the will be initially + /// When using this constructor, the will be initially /// and should be set by derived classes when the remote conversation is created. /// - protected ServiceIdAgentThread() + protected ServiceIdAgentSession() { } /// - /// Initializes a new instance of the class with the specified service thread identifier. + /// Initializes a new instance of the class with the specified service session identifier. /// - /// The unique identifier that references the conversation state stored in the remote service. - /// is . - /// is empty or contains only whitespace. - protected ServiceIdAgentThread(string serviceThreadId) + /// The unique identifier that references the conversation state stored in the remote service. + /// is . + /// is empty or contains only whitespace. + protected ServiceIdAgentSession(string serviceSessionId) { - this.ServiceThreadId = Throw.IfNullOrEmpty(serviceThreadId); + this.ServiceSessionId = Throw.IfNullOrEmpty(serviceSessionId); } /// - /// Initializes a new instance of the class from previously serialized state. + /// Initializes a new instance of the class from previously serialized state. /// - /// A representing the serialized state of the thread. + /// A representing the serialized state of the session. /// Optional settings for customizing the JSON deserialization process. - /// The is not a JSON object. - /// The is invalid or cannot be deserialized to the expected type. + /// The is not a JSON object. + /// The is invalid or cannot be deserialized to the expected type. /// - /// This constructor enables restoration of a service-backed thread from serialized state, typically used - /// when deserializing thread information that was previously saved or transmitted across application boundaries. + /// This constructor enables restoration of a service-backed session from serialized state, typically used + /// when deserializing session information that was previously saved or transmitted across application boundaries. /// - protected ServiceIdAgentThread( - JsonElement serializedThreadState, + protected ServiceIdAgentSession( + JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null) { - if (serializedThreadState.ValueKind != JsonValueKind.Object) + if (serializedSessionState.ValueKind != JsonValueKind.Object) { - throw new ArgumentException("The serialized thread state must be a JSON object.", nameof(serializedThreadState)); + throw new ArgumentException("The serialized session state must be a JSON object.", nameof(serializedSessionState)); } - var state = serializedThreadState.Deserialize( - AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ServiceIdAgentThreadState))) as ServiceIdAgentThreadState; + var state = serializedSessionState.Deserialize( + AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ServiceIdAgentSessionState))) as ServiceIdAgentSessionState; - if (state?.ServiceThreadId is string serviceThreadId) + if (state?.ServiceSessionId is string serviceSessionId) { - this.ServiceThreadId = serviceThreadId; + this.ServiceSessionId = serviceSessionId; } } @@ -80,29 +80,29 @@ public abstract class ServiceIdAgentThread : AgentThread /// API calls to the backing service. The exact format and meaning of this identifier depends on the /// specific service implementation. /// - protected string? ServiceThreadId { get; set; } + protected string? ServiceSessionId { get; set; } /// /// Serializes the current object's state to a using the specified serialization options. /// /// The JSON serialization options to use for the serialization process. - /// A representation of the object's state, containing the service thread identifier. + /// A representation of the object's state, containing the service session identifier. /// - /// The serialized state contains only the service thread identifier, as all other conversation state + /// The serialized state contains only the service session identifier, as all other conversation state /// is maintained remotely by the backing service. This makes the serialized representation very lightweight. /// public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { - var state = new ServiceIdAgentThreadState + var state = new ServiceIdAgentSessionState { - ServiceThreadId = this.ServiceThreadId, + ServiceSessionId = this.ServiceSessionId, }; - return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ServiceIdAgentThreadState))); + return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ServiceIdAgentSessionState))); } - internal sealed class ServiceIdAgentThreadState + internal sealed class ServiceIdAgentSessionState { - public string? ServiceThreadId { get; set; } + public string? ServiceSessionId { get; set; } } } diff --git a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs index 6b69975f50..bf786a1712 100644 --- a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs @@ -42,43 +42,43 @@ public class CopilotStudioAgent : AIAgent } /// - public sealed override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) - => new(new CopilotStudioAgentThread()); + public sealed override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) + => new(new CopilotStudioAgentSession()); /// - /// Get a new instance using an existing conversation id, to continue that conversation. + /// Get a new instance using an existing conversation id, to continue that conversation. /// /// The conversation id to continue. - /// A new instance. - public ValueTask GetNewThreadAsync(string conversationId) - => new(new CopilotStudioAgentThread() { ConversationId = conversationId }); + /// A new instance. + public ValueTask GetNewSessionAsync(string conversationId) + => new(new CopilotStudioAgentSession() { ConversationId = conversationId }); /// - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new CopilotStudioAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new CopilotStudioAgentSession(serializedSession, jsonSerializerOptions)); /// protected override async Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { Throw.IfNull(messages); - // Ensure that we have a valid thread to work with. - // If the thread ID is null, we need to start a new conversation and set the thread ID accordingly. - thread ??= await this.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); - if (thread is not CopilotStudioAgentThread typedThread) + // Ensure that we have a valid session to work with. + // If the session ID is null, we need to start a new conversation and set the session ID accordingly. + session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); + if (session is not CopilotStudioAgentSession typedSession) { - throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used."); + throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be used."); } - typedThread.ConversationId ??= await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false); + typedSession.ConversationId ??= await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false); // Invoke the Copilot Studio agent with the provided messages. string question = string.Join("\n", messages.Select(m => m.Text)); - var responseMessages = ActivityProcessor.ProcessActivityAsync(this.Client.AskQuestionAsync(question, typedThread.ConversationId, cancellationToken), streaming: false, this._logger); + var responseMessages = ActivityProcessor.ProcessActivityAsync(this.Client.AskQuestionAsync(question, typedSession.ConversationId, cancellationToken), streaming: false, this._logger); var responseMessagesList = new List(); await foreach (var message in responseMessages.ConfigureAwait(false)) { @@ -98,26 +98,26 @@ public class CopilotStudioAgent : AIAgent /// protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { Throw.IfNull(messages); - // Ensure that we have a valid thread to work with. - // If the thread ID is null, we need to start a new conversation and set the thread ID accordingly. + // Ensure that we have a valid session to work with. + // If the session ID is null, we need to start a new conversation and set the session ID accordingly. - thread ??= await this.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); - if (thread is not CopilotStudioAgentThread typedThread) + session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); + if (session is not CopilotStudioAgentSession typedSession) { - throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used."); + throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be used."); } - typedThread.ConversationId ??= await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false); + typedSession.ConversationId ??= await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false); // Invoke the Copilot Studio agent with the provided messages. string question = string.Join("\n", messages.Select(m => m.Text)); - var responseMessages = ActivityProcessor.ProcessActivityAsync(this.Client.AskQuestionAsync(question, typedThread.ConversationId, cancellationToken), streaming: true, this._logger); + var responseMessages = ActivityProcessor.ProcessActivityAsync(this.Client.AskQuestionAsync(question, typedSession.ConversationId, cancellationToken), streaming: true, this._logger); // Enumerate the response messages await foreach (ChatMessage message in responseMessages.ConfigureAwait(false)) diff --git a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgentSession.cs b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgentSession.cs new file mode 100644 index 0000000000..66f9d02533 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgentSession.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Text.Json; + +namespace Microsoft.Agents.AI.CopilotStudio; + +/// +/// Session for CopilotStudio based agents. +/// +public sealed class CopilotStudioAgentSession : ServiceIdAgentSession +{ + internal CopilotStudioAgentSession() + { + } + + internal CopilotStudioAgentSession(JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null) : base(serializedSessionState, jsonSerializerOptions) + { + } + + /// + /// Gets the ID for the current conversation with the Copilot Studio agent. + /// + public string? ConversationId + { + get { return this.ServiceSessionId; } + internal set { this.ServiceSessionId = value; } + } +} diff --git a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgentThread.cs b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgentThread.cs deleted file mode 100644 index c868d75851..0000000000 --- a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgentThread.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Text.Json; - -namespace Microsoft.Agents.AI.CopilotStudio; - -/// -/// Thread for CopilotStudio based agents. -/// -public sealed class CopilotStudioAgentThread : ServiceIdAgentThread -{ - internal CopilotStudioAgentThread() - { - } - - internal CopilotStudioAgentThread(JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null) : base(serializedThreadState, jsonSerializerOptions) - { - } - - /// - /// Gets the ID for the current conversation with the Copilot Studio agent. - /// - public string? ConversationId - { - get { return this.ServiceThreadId; } - internal set { this.ServiceThreadId = value; } - } -} diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/AgentEntity.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/AgentEntity.cs index 15432804f3..09c6df31ea 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/AgentEntity.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/AgentEntity.cs @@ -67,7 +67,7 @@ internal class AgentEntity(IServiceProvider services, CancellationToken cancella // Start the agent response stream IAsyncEnumerable responseStream = agentWrapper.RunStreamingAsync( this.State.Data.ConversationHistory.SelectMany(e => e.Messages).Select(m => m.ToChatMessage()), - await agentWrapper.GetNewThreadAsync(cancellationToken).ConfigureAwait(false), + await agentWrapper.GetNewSessionAsync(cancellationToken).ConfigureAwait(false), options: null, this._cancellationToken); diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md b/dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md index 8f8f64fe5c..2c1460b213 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md @@ -7,6 +7,7 @@ - Added TTL configuration for durable agent entities ([#2679](https://github.com/microsoft/agent-framework/pull/2679)) - Switch to new "Run" method name ([#2843](https://github.com/microsoft/agent-framework/pull/2843)) - Removed AgentThreadMetadata and used AgentSessionId directly instead ([#3067](https://github.com/microsoft/agent-framework/pull/3067)); +- Renamed AgentThread to AgentSession ([#3430](https://github.com/microsoft/agent-framework/pull/3430)) ## v1.0.0-preview.251204.1 diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs index dd598e2618..f944131b4e 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs @@ -30,44 +30,44 @@ public sealed class DurableAIAgent : AIAgent } /// - /// Creates a new agent thread for this agent using a random session ID. + /// Creates a new agent session for this agent using a random session ID. /// /// The cancellation token. - /// A value task that represents the asynchronous operation. The task result contains a new agent thread. - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + /// A value task that represents the asynchronous operation. The task result contains a new agent session. + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) { AgentSessionId sessionId = this._context.NewAgentSessionId(this._agentName); - return ValueTask.FromResult(new DurableAgentThread(sessionId)); + return ValueTask.FromResult(new DurableAgentSession(sessionId)); } /// - /// Deserializes an agent thread from JSON. + /// Deserializes an agent session from JSON. /// - /// The serialized thread data. + /// The serialized session data. /// Optional JSON serializer options. /// The cancellation token. - /// A value task that represents the asynchronous operation. The task result contains the deserialized agent thread. - public override ValueTask DeserializeThreadAsync( - JsonElement serializedThread, + /// A value task that represents the asynchronous operation. The task result contains the deserialized agent session. + public override ValueTask DeserializeSessionAsync( + JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { - return ValueTask.FromResult(DurableAgentThread.Deserialize(serializedThread, jsonSerializerOptions)); + return ValueTask.FromResult(DurableAgentSession.Deserialize(serializedSession, jsonSerializerOptions)); } /// /// Runs the agent with messages and returns the response. /// /// The messages to send to the agent. - /// The agent thread to use. + /// The agent session to use. /// Optional run options. /// The cancellation token. /// The response from the agent. /// Thrown when the agent has not been registered. - /// Thrown when the provided thread is not valid for a durable agent. + /// Thrown when the provided session is not valid for a durable agent. /// Thrown when cancellation is requested (cancellation is not supported for durable agents). protected override async Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -76,13 +76,13 @@ public sealed class DurableAIAgent : AIAgent throw new NotSupportedException("Cancellation is not supported for durable agents."); } - thread ??= await this.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); - if (thread is not DurableAgentThread durableThread) + session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); + if (session is not DurableAgentSession durableSession) { throw new ArgumentException( - "The provided thread is not valid for a durable agent. " + - "Create a new thread using GetNewThreadAsync or provide a thread previously created by this agent.", - paramName: nameof(thread)); + "The provided session is not valid for a durable agent. " + + "Create a new session using GetNewSessionAsync or provide a session previously created by this agent.", + paramName: nameof(session)); } IList? enableToolNames = null; @@ -108,7 +108,7 @@ public sealed class DurableAIAgent : AIAgent try { return await this._context.Entities.CallEntityAsync( - durableThread.SessionId, + durableSession.SessionId, nameof(AgentEntity.Run), request); } @@ -126,19 +126,19 @@ public sealed class DurableAIAgent : AIAgent /// as a single update. /// /// The messages to send to the agent. - /// The agent thread to use. + /// The agent session to use. /// Optional run options. /// The cancellation token. /// A streaming response enumerable. protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { // Streaming is not supported for durable agents, so we just return the full response // as a single update. - AgentResponse response = await this.RunAsync(messages, thread, options, cancellationToken); + AgentResponse response = await this.RunAsync(messages, session, options, cancellationToken); foreach (AgentResponseUpdate update in response.ToAgentResponseUpdates()) { yield return update; @@ -149,7 +149,7 @@ public sealed class DurableAIAgent : AIAgent /// Runs the agent with a message and returns the deserialized output as an instance of . /// /// The message to send to the agent. - /// The agent thread to use. + /// The agent session to use. /// Optional JSON serializer options. /// Optional run options. /// The cancellation token. @@ -164,14 +164,14 @@ public sealed class DurableAIAgent : AIAgent /// The output from the agent. public async Task> RunAsync( string message, - AgentThread? thread = null, + AgentSession? session = null, JsonSerializerOptions? serializerOptions = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { return await this.RunAsync( messages: [new ChatMessage(ChatRole.User, message) { CreatedAt = DateTimeOffset.UtcNow }], - thread, + session, serializerOptions, options, cancellationToken); @@ -181,7 +181,7 @@ public sealed class DurableAIAgent : AIAgent /// Runs the agent with messages and returns the deserialized output as an instance of . /// /// The messages to send to the agent. - /// The agent thread to use. + /// The agent session to use. /// Optional JSON serializer options. /// Optional run options. /// The cancellation token. @@ -198,7 +198,7 @@ public sealed class DurableAIAgent : AIAgent [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050", Justification = "Fallback to reflection-based deserialization is intentional for library flexibility with user-defined types.")] public async Task> RunAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, JsonSerializerOptions? serializerOptions = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -223,7 +223,7 @@ public sealed class DurableAIAgent : AIAgent // Create the JSON schema for the response type durableOptions.ResponseFormat = ChatResponseFormat.ForJsonSchema(); - AgentResponse response = await this.RunAsync(messages, thread, durableOptions, cancellationToken); + AgentResponse response = await this.RunAsync(messages, session, durableOptions, cancellationToken); // Deserialize the response text to the requested type if (string.IsNullOrEmpty(response.Text)) diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs index 2461302e6e..d61bd6df77 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs @@ -11,31 +11,31 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient) public override string? Name { get; } = name; - public override ValueTask DeserializeThreadAsync( - JsonElement serializedThread, + public override ValueTask DeserializeSessionAsync( + JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { - return ValueTask.FromResult(DurableAgentThread.Deserialize(serializedThread, jsonSerializerOptions)); + return ValueTask.FromResult(DurableAgentSession.Deserialize(serializedSession, jsonSerializerOptions)); } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) { - return ValueTask.FromResult(new DurableAgentThread(AgentSessionId.WithRandomKey(this.Name!))); + return ValueTask.FromResult(new DurableAgentSession(AgentSessionId.WithRandomKey(this.Name!))); } protected override async Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - thread ??= await this.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); - if (thread is not DurableAgentThread durableThread) + session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); + if (session is not DurableAgentSession durableSession) { throw new ArgumentException( - "The provided thread is not valid for a durable agent. " + - "Create a new thread using GetNewThread or provide a thread previously created by this agent.", - paramName: nameof(thread)); + "The provided session is not valid for a durable agent. " + + "Create a new session using GetNewSession or provide a session previously created by this agent.", + paramName: nameof(session)); } IList? enableToolNames = null; @@ -57,7 +57,7 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient) } RunRequest request = new([.. messages], responseFormat, enableToolCalls, enableToolNames); - AgentSessionId sessionId = durableThread.SessionId; + AgentSessionId sessionId = durableSession.SessionId; AgentRunHandle agentRunHandle = await this._agentClient.RunAgentAsync(sessionId, request, cancellationToken); @@ -72,7 +72,7 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient) protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentContext.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentContext.cs index 94a6c00424..209d27ceab 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentContext.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentContext.cs @@ -25,7 +25,7 @@ public class DurableAgentContext IServiceProvider services) { this.EntityContext = entityContext; - this.CurrentThread = new DurableAgentThread(entityContext.Id); + this.CurrentSession = new DurableAgentSession(entityContext.Id); this.Client = client; this._services = services; this._cancellationToken = lifetime.ApplicationStopping; @@ -51,7 +51,7 @@ public class DurableAgentContext /// /// Gets the current agent thread. /// - public DurableAgentThread CurrentThread { get; } + public DurableAgentSession CurrentSession { get; } /// /// Sets the current durable agent context instance. diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentJsonUtilities.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentJsonUtilities.cs index 966218058c..7670b9e147 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentJsonUtilities.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentJsonUtilities.cs @@ -85,7 +85,7 @@ internal static partial class DurableAgentJsonUtilities // Durable Agent State Types [JsonSerializable(typeof(DurableAgentState))] - [JsonSerializable(typeof(DurableAgentThread))] + [JsonSerializable(typeof(DurableAgentSession))] // Request Types [JsonSerializable(typeof(RunRequest))] diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentThread.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentSession.cs similarity index 73% rename from dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentThread.cs rename to dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentSession.cs index 98dc8ea4b1..6bd1c821d2 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAgentSession.cs @@ -10,10 +10,10 @@ namespace Microsoft.Agents.AI.DurableTask; /// An agent thread implementation for durable agents. /// [DebuggerDisplay("{SessionId}")] -public sealed class DurableAgentThread : AgentThread +public sealed class DurableAgentSession : AgentSession { [JsonConstructor] - internal DurableAgentThread(AgentSessionId sessionId) + internal DurableAgentSession(AgentSessionId sessionId) { this.SessionId = sessionId; } @@ -30,18 +30,18 @@ public sealed class DurableAgentThread : AgentThread { return JsonSerializer.SerializeToElement( this, - DurableAgentJsonUtilities.DefaultOptions.GetTypeInfo(typeof(DurableAgentThread))); + DurableAgentJsonUtilities.DefaultOptions.GetTypeInfo(typeof(DurableAgentSession))); } /// - /// Deserializes a DurableAgentThread from JSON. + /// Deserializes a DurableAgentSession from JSON. /// - /// The serialized thread data. + /// The serialized thread data. /// Optional JSON serializer options. - /// The deserialized DurableAgentThread. - internal static DurableAgentThread Deserialize(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) + /// The deserialized DurableAgentSession. + internal static DurableAgentSession Deserialize(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null) { - if (!serializedThread.TryGetProperty("sessionId", out JsonElement sessionIdElement) || + if (!serializedSession.TryGetProperty("sessionId", out JsonElement sessionIdElement) || sessionIdElement.ValueKind != JsonValueKind.String) { throw new JsonException("Invalid or missing sessionId property."); @@ -49,7 +49,7 @@ public sealed class DurableAgentThread : AgentThread string sessionIdString = sessionIdElement.GetString() ?? throw new JsonException("sessionId property is null."); AgentSessionId sessionId = AgentSessionId.Parse(sessionIdString); - return new DurableAgentThread(sessionId); + return new DurableAgentSession(sessionId); } /// diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs index e58db5e9b4..ce4eef8668 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs @@ -23,13 +23,13 @@ internal sealed class EntityAgentWrapper( protected override async Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { AgentResponse response = await base.RunCoreAsync( messages, - thread, + session, this.GetAgentEntityRunOptions(options), cancellationToken); @@ -39,13 +39,13 @@ internal sealed class EntityAgentWrapper( protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { await foreach (AgentResponseUpdate update in base.RunCoreStreamingAsync( messages, - thread, + session, this.GetAgentEntityRunOptions(options), cancellationToken)) { diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/EndpointRouteBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/EndpointRouteBuilderExtensions.cs index b6b9c4da48..943b6e4a5c 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/EndpointRouteBuilderExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/EndpointRouteBuilderExtensions.cs @@ -174,8 +174,8 @@ public static class MicrosoftAgentAIHostingA2AEndpointRouteBuilderExtensions ArgumentNullException.ThrowIfNull(agent); var loggerFactory = endpoints.ServiceProvider.GetRequiredService(); - var agentThreadStore = endpoints.ServiceProvider.GetKeyedService(agent.Name); - var taskManager = agent.MapA2A(loggerFactory: loggerFactory, agentThreadStore: agentThreadStore); + var agentSessionStore = endpoints.ServiceProvider.GetKeyedService(agent.Name); + var taskManager = agent.MapA2A(loggerFactory: loggerFactory, agentSessionStore: agentSessionStore); var endpointConventionBuilder = endpoints.MapA2A(taskManager, path); configureTaskManager(taskManager); @@ -218,8 +218,8 @@ public static class MicrosoftAgentAIHostingA2AEndpointRouteBuilderExtensions ArgumentNullException.ThrowIfNull(agent); var loggerFactory = endpoints.ServiceProvider.GetRequiredService(); - var agentThreadStore = endpoints.ServiceProvider.GetKeyedService(agent.Name); - var taskManager = agent.MapA2A(agentCard: agentCard, agentThreadStore: agentThreadStore, loggerFactory: loggerFactory); + var agentSessionStore = endpoints.ServiceProvider.GetKeyedService(agent.Name); + var taskManager = agent.MapA2A(agentCard: agentCard, agentSessionStore: agentSessionStore, loggerFactory: loggerFactory); var endpointConventionBuilder = endpoints.MapA2A(taskManager, path); configureTaskManager(taskManager); diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/AIAgentExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/AIAgentExtensions.cs index a2cb300687..da3fd782de 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/AIAgentExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/AIAgentExtensions.cs @@ -20,20 +20,20 @@ public static class AIAgentExtensions /// Agent to attach A2A messaging processing capabilities to. /// Instance of to configure for A2A messaging. New instance will be created if not passed. /// The logger factory to use for creating instances. - /// The store to store thread contents and metadata. + /// The store to store session contents and metadata. /// The configured . public static ITaskManager MapA2A( this AIAgent agent, ITaskManager? taskManager = null, ILoggerFactory? loggerFactory = null, - AgentThreadStore? agentThreadStore = null) + AgentSessionStore? agentSessionStore = null) { ArgumentNullException.ThrowIfNull(agent); ArgumentNullException.ThrowIfNull(agent.Name); var hostAgent = new AIHostAgent( innerAgent: agent, - threadStore: agentThreadStore ?? new NoopAgentThreadStore()); + sessionStore: agentSessionStore ?? new NoopAgentSessionStore()); taskManager ??= new TaskManager(); taskManager.OnMessageReceived += OnMessageReceivedAsync; @@ -42,18 +42,18 @@ public static class AIAgentExtensions async Task OnMessageReceivedAsync(MessageSendParams messageSendParams, CancellationToken cancellationToken) { var contextId = messageSendParams.Message.ContextId ?? Guid.NewGuid().ToString("N"); - var thread = await hostAgent.GetOrCreateThreadAsync(contextId, cancellationToken).ConfigureAwait(false); + var session = await hostAgent.GetOrCreateSessionAsync(contextId, cancellationToken).ConfigureAwait(false); var options = messageSendParams.Metadata is not { Count: > 0 } ? null : new AgentRunOptions { AdditionalProperties = messageSendParams.Metadata.ToAdditionalProperties() }; var response = await hostAgent.RunAsync( messageSendParams.ToChatMessages(), - thread: thread, + session: session, options: options, cancellationToken: cancellationToken).ConfigureAwait(false); - await hostAgent.SaveThreadAsync(contextId, thread, cancellationToken).ConfigureAwait(false); + await hostAgent.SaveSessionAsync(contextId, session, cancellationToken).ConfigureAwait(false); var parts = response.Messages.ToParts(); return new AgentMessage { @@ -73,16 +73,16 @@ public static class AIAgentExtensions /// The agent card to return on query. /// Instance of to configure for A2A messaging. New instance will be created if not passed. /// The logger factory to use for creating instances. - /// The store to store thread contents and metadata. + /// The store to store session contents and metadata. /// The configured . public static ITaskManager MapA2A( this AIAgent agent, AgentCard agentCard, ITaskManager? taskManager = null, ILoggerFactory? loggerFactory = null, - AgentThreadStore? agentThreadStore = null) + AgentSessionStore? agentSessionStore = null) { - taskManager = agent.MapA2A(taskManager, loggerFactory, agentThreadStore); + taskManager = agent.MapA2A(taskManager, loggerFactory, agentSessionStore); taskManager.OnAgentCardQuery += (context, query) => { diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/BuiltInFunctions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/BuiltInFunctions.cs index edde523271..8573a80613 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/BuiltInFunctions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/BuiltInFunctions.cs @@ -69,7 +69,7 @@ internal static class BuiltInFunctions message = await req.ReadAsStringAsync(); } - // The thread ID can come from query string or JSON body + // The session ID can come from query string or JSON body string? threadIdFromQuery = req.Query["thread_id"]; // Validate that if thread_id is specified in both places, they must match @@ -121,7 +121,7 @@ internal static class BuiltInFunctions { AgentResponse agentResponse = await agentProxy.RunAsync( message: new ChatMessage(ChatRole.User, message), - thread: new DurableAgentThread(sessionId), + session: new DurableAgentSession(sessionId), options: options, cancellationToken: context.CancellationToken); @@ -136,7 +136,7 @@ internal static class BuiltInFunctions // Fire and forget - return 202 Accepted await agentProxy.RunAsync( message: new ChatMessage(ChatRole.User, message), - thread: new DurableAgentThread(sessionId), + session: new DurableAgentSession(sessionId), options: options, cancellationToken: context.CancellationToken); @@ -172,7 +172,7 @@ internal static class BuiltInFunctions AgentResponse agentResponse = await agentProxy.RunAsync( message: new ChatMessage(ChatRole.User, query), - thread: new DurableAgentThread(sessionId), + session: new DurableAgentSession(sessionId), options: null); return agentResponse.Text; @@ -216,25 +216,25 @@ internal static class BuiltInFunctions /// The HTTP request data. /// The function context. /// The HTTP status code (typically 200 OK). - /// The thread ID for the conversation. + /// The session ID for the conversation. /// The agent's response. /// The HTTP response data containing the success response. private static async Task CreateSuccessResponseAsync( HttpRequestData req, FunctionContext context, HttpStatusCode statusCode, - string threadId, + string sessionId, AgentResponse agentResponse) { HttpResponseData response = req.CreateResponse(statusCode); - response.Headers.Add("x-ms-thread-id", threadId); + response.Headers.Add("x-ms-thread-id", sessionId); bool acceptsJson = req.Headers.TryGetValues("Accept", out IEnumerable? acceptValues) && acceptValues.Contains("application/json", StringComparer.OrdinalIgnoreCase); if (acceptsJson) { - AgentRunSuccessResponse successResponse = new((int)statusCode, threadId, agentResponse); + AgentRunSuccessResponse successResponse = new((int)statusCode, sessionId, agentResponse); await response.WriteAsJsonAsync(successResponse, context.CancellationToken); } else @@ -251,22 +251,22 @@ internal static class BuiltInFunctions /// /// The HTTP request data. /// The function context. - /// The thread ID for the conversation. + /// The session ID for the conversation. /// The HTTP response data containing the accepted response. private static async Task CreateAcceptedResponseAsync( HttpRequestData req, FunctionContext context, - string threadId) + string sessionId) { HttpResponseData response = req.CreateResponse(HttpStatusCode.Accepted); - response.Headers.Add("x-ms-thread-id", threadId); + response.Headers.Add("x-ms-thread-id", sessionId); bool acceptsJson = req.Headers.TryGetValues("Accept", out IEnumerable? acceptValues) && acceptValues.Contains("application/json", StringComparer.OrdinalIgnoreCase); if (acceptsJson) { - AgentRunAcceptedResponse acceptedResponse = new((int)HttpStatusCode.Accepted, threadId); + AgentRunAcceptedResponse acceptedResponse = new((int)HttpStatusCode.Accepted, sessionId); await response.WriteAsJsonAsync(acceptedResponse, context.CancellationToken); } else @@ -298,7 +298,7 @@ internal static class BuiltInFunctions /// Represents a request to run an agent. /// /// The message to send to the agent. - /// The optional thread ID to continue a conversation. + /// The optional session ID to continue a conversation. private sealed record AgentRunRequest( [property: JsonPropertyName("message")] string? Message, [property: JsonPropertyName("thread_id")] string? ThreadId); @@ -316,7 +316,7 @@ internal static class BuiltInFunctions /// Represents a successful agent run response. /// /// The HTTP status code. - /// The thread ID for the conversation. + /// The session ID for the conversation. /// The agent response. private sealed record AgentRunSuccessResponse( [property: JsonPropertyName("status")] int Status, @@ -327,7 +327,7 @@ internal static class BuiltInFunctions /// Represents an accepted (fire-and-forget) agent run response. /// /// The HTTP status code. - /// The thread ID for the conversation. + /// The session ID for the conversation. private sealed record AgentRunAcceptedResponse( [property: JsonPropertyName("status")] int Status, [property: JsonPropertyName("thread_id")] string ThreadId); diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/README.md b/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/README.md index 2b3c87c348..0506a0ea23 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/README.md +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/README.md @@ -74,7 +74,7 @@ public static async Task SpamDetectionOrchestration( // Get the spam detection agent DurableAIAgent spamDetectionAgent = context.GetAgent("SpamDetectionAgent"); - AgentThread spamThread = await spamDetectionAgent.GetNewThreadAsync(); + AgentSession spamSession = await spamDetectionAgent.GetNewSessionAsync(); // Step 1: Check if the email is spam AgentResponse spamDetectionResponse = await spamDetectionAgent.RunAsync( @@ -84,7 +84,7 @@ public static async Task SpamDetectionOrchestration( Email ID: {email.EmailId} Content: {email.EmailContent} """, - thread: spamThread); + session: spamSession); DetectionResult result = spamDetectionResponse.Result; // Step 2: Conditional logic based on spam detection result @@ -97,7 +97,7 @@ public static async Task SpamDetectionOrchestration( { // Generate and send response for legitimate email DurableAIAgent emailAssistantAgent = context.GetAgent("EmailAssistantAgent"); - AgentThread emailThread = await emailAssistantAgent.GetNewThreadAsync(); + AgentSession emailSession = await emailAssistantAgent.GetNewSessionAsync(); AgentResponse emailAssistantResponse = await emailAssistantAgent.RunAsync( message: @@ -107,7 +107,7 @@ public static async Task SpamDetectionOrchestration( Email ID: {email.EmailId} Content: {email.EmailContent} """, - thread: emailThread); + session: emailSession); EmailResponse emailResponse = emailAssistantResponse.Result; return await context.CallActivityAsync(nameof(SendEmail), emailResponse.Response); diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/AIHostAgent.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/AIHostAgent.cs index c11a630ffe..f2220e97ad 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting/AIHostAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting/AIHostAgent.cs @@ -8,66 +8,66 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Hosting; /// -/// Provides a hosting wrapper around an that adds thread persistence capabilities +/// Provides a hosting wrapper around an that adds session persistence capabilities /// for server-hosted scenarios where conversations need to be restored across requests. /// /// /// /// wraps an existing agent implementation and adds the ability to -/// persist and restore conversation threads using an . +/// persist and restore conversation threads using an . /// /// -/// This wrapper enables thread persistence without requiring type-specific knowledge of the thread type, -/// as all thread operations work through the base abstraction. +/// This wrapper enables session persistence without requiring type-specific knowledge of the session type, +/// as all session operations work through the base abstraction. /// /// public class AIHostAgent : DelegatingAIAgent { - private readonly AgentThreadStore _threadStore; + private readonly AgentSessionStore _sessionStore; /// /// Initializes a new instance of the class. /// /// The underlying agent implementation to wrap. - /// The thread store to use for persisting conversation state. + /// The session store to use for persisting conversation state. /// - /// or is . + /// or is . /// - public AIHostAgent(AIAgent innerAgent, AgentThreadStore threadStore) + public AIHostAgent(AIAgent innerAgent, AgentSessionStore sessionStore) : base(innerAgent) { - this._threadStore = Throw.IfNull(threadStore); + this._sessionStore = Throw.IfNull(sessionStore); } /// - /// Gets an existing agent thread for the specified conversation, or creates a new one if none exists. + /// Gets an existing agent session for the specified conversation, or creates a new one if none exists. /// - /// The unique identifier of the conversation for which to retrieve or create the agent thread. Cannot be null, + /// The unique identifier of the conversation for which to retrieve or create the agent session. Cannot be null, /// empty, or consist only of white-space characters. /// A cancellation token that can be used to cancel the asynchronous operation. - /// A task that represents the asynchronous operation. The task result contains the agent thread associated with the - /// specified conversation. If no thread exists, a new thread is created and returned. - public ValueTask GetOrCreateThreadAsync(string conversationId, CancellationToken cancellationToken = default) + /// A task that represents the asynchronous operation. The task result contains the agent session associated with the + /// specified conversation. If no session exists, a new session is created and returned. + public ValueTask GetOrCreateSessionAsync(string conversationId, CancellationToken cancellationToken = default) { _ = Throw.IfNullOrWhitespace(conversationId); - return this._threadStore.GetThreadAsync(this.InnerAgent, conversationId, cancellationToken); + return this._sessionStore.GetSessionAsync(this.InnerAgent, conversationId, cancellationToken); } /// - /// Persists a conversation thread to the thread store. + /// Persists a conversation session to the session store. /// /// The unique identifier for the conversation. - /// The thread to persist. + /// The session to persist. /// The to monitor for cancellation requests. /// A task that represents the asynchronous save operation. /// is null or whitespace. - /// is . - public ValueTask SaveThreadAsync(string conversationId, AgentThread thread, CancellationToken cancellationToken = default) + /// is . + public ValueTask SaveSessionAsync(string conversationId, AgentSession session, CancellationToken cancellationToken = default) { _ = Throw.IfNullOrWhitespace(conversationId); - _ = Throw.IfNull(thread); + _ = Throw.IfNull(session); - return this._threadStore.SaveThreadAsync(this.InnerAgent, conversationId, thread, cancellationToken); + return this._sessionStore.SaveSessionAsync(this.InnerAgent, conversationId, session, cancellationToken); } } diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/IAgentThreadStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs similarity index 66% rename from dotnet/src/Microsoft.Agents.AI.Hosting/IAgentThreadStore.cs rename to dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs index f95999dde3..2f57e26409 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting/IAgentThreadStore.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs @@ -13,33 +13,33 @@ namespace Microsoft.Agents.AI.Hosting; /// allowing conversations to be resumed across HTTP requests, application restarts, /// or different service instances in hosted scenarios. /// -public abstract class AgentThreadStore +public abstract class AgentSessionStore { /// - /// Saves a serialized agent thread to persistent storage. + /// Saves a serialized agent session to persistent storage. /// - /// The agent that owns this thread. - /// The unique identifier for the conversation/thread. - /// The thread to save. + /// The agent that owns this session. + /// The unique identifier for the conversation/session. + /// The session to save. /// The to monitor for cancellation requests. /// A task that represents the asynchronous save operation. - public abstract ValueTask SaveThreadAsync( + public abstract ValueTask SaveSessionAsync( AIAgent agent, string conversationId, - AgentThread thread, + AgentSession session, CancellationToken cancellationToken = default); /// - /// Retrieves a serialized agent thread from persistent storage. + /// Retrieves a serialized agent session from persistent storage. /// - /// The agent that owns this thread. - /// The unique identifier for the conversation/thread to retrieve. + /// The agent that owns this session. + /// The unique identifier for the conversation/session to retrieve. /// The to monitor for cancellation requests. /// /// A task that represents the asynchronous retrieval operation. - /// The task result contains the serialized thread state, or if not found. + /// The task result contains the serialized session state, or if not found. /// - public abstract ValueTask GetThreadAsync( + public abstract ValueTask GetSessionAsync( AIAgent agent, string conversationId, CancellationToken cancellationToken = default); diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/HostedAgentBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/HostedAgentBuilderExtensions.cs index e2c52ff9e0..12c1e08dfd 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting/HostedAgentBuilderExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting/HostedAgentBuilderExtensions.cs @@ -13,45 +13,45 @@ namespace Microsoft.Agents.AI.Hosting; public static class HostedAgentBuilderExtensions { /// - /// Configures the host agent builder to use an in-memory thread store for agent thread management. + /// Configures the host agent builder to use an in-memory session store for agent session management. /// - /// The host agent builder to configure with the in-memory thread store. - /// The same instance, configured to use an in-memory thread store. - public static IHostedAgentBuilder WithInMemoryThreadStore(this IHostedAgentBuilder builder) + /// The host agent builder to configure with the in-memory session store. + /// The same instance, configured to use an in-memory session store. + public static IHostedAgentBuilder WithInMemorySessionStore(this IHostedAgentBuilder builder) { - builder.ServiceCollection.AddKeyedSingleton(builder.Name, new InMemoryAgentThreadStore()); + builder.ServiceCollection.AddKeyedSingleton(builder.Name, new InMemoryAgentSessionStore()); return builder; } /// - /// Registers the specified agent thread store with the host agent builder, enabling thread-specific storage for + /// Registers the specified agent session store with the host agent builder, enabling session-specific storage for /// agent operations. /// - /// The host agent builder to configure with the thread store. Cannot be null. - /// The agent thread store instance to register. Cannot be null. + /// The host agent builder to configure with the session store. Cannot be null. + /// The agent session store instance to register. Cannot be null. /// The same host agent builder instance, allowing for method chaining. - public static IHostedAgentBuilder WithThreadStore(this IHostedAgentBuilder builder, AgentThreadStore store) + public static IHostedAgentBuilder WithSessionStore(this IHostedAgentBuilder builder, AgentSessionStore store) { builder.ServiceCollection.AddKeyedSingleton(builder.Name, store); return builder; } /// - /// Configures the host agent builder to use a custom thread store implementation for agent threads. + /// Configures the host agent builder to use a custom session store implementation for agent sessions. /// /// The host agent builder to configure. - /// A factory function that creates an agent thread store instance using the provided service provider and agent + /// A factory function that creates an agent session store instance using the provided service provider and agent /// name. /// The same host agent builder instance, enabling further configuration. - public static IHostedAgentBuilder WithThreadStore(this IHostedAgentBuilder builder, Func createAgentThreadStore) + public static IHostedAgentBuilder WithSessionStore(this IHostedAgentBuilder builder, Func createAgentSessionStore) { builder.ServiceCollection.AddKeyedSingleton(builder.Name, (sp, key) => { Throw.IfNull(key); var keyString = key as string; Throw.IfNullOrEmpty(keyString); - return createAgentThreadStore(sp, keyString) ?? - throw new InvalidOperationException($"The agent thread store factory did not return a valid {nameof(AgentThreadStore)} instance for key '{keyString}'."); + return createAgentSessionStore(sp, keyString) ?? + throw new InvalidOperationException($"The agent session store factory did not return a valid {nameof(AgentSessionStore)} instance for key '{keyString}'."); }); return builder; } diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentThreadStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentSessionStore.cs similarity index 56% rename from dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentThreadStore.cs rename to dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentSessionStore.cs index febbf4c06a..ec1381f405 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentThreadStore.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentSessionStore.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace Microsoft.Agents.AI.Hosting; /// -/// Provides an in-memory implementation of for development and testing scenarios. +/// Provides an in-memory implementation of for development and testing scenarios. /// /// /// @@ -16,7 +16,7 @@ namespace Microsoft.Agents.AI.Hosting; /// /// Single-instance development scenarios /// Testing and prototyping -/// Scenarios where thread persistence across restarts is not required +/// Scenarios where session persistence across restarts is not required /// /// /// @@ -25,28 +25,28 @@ namespace Microsoft.Agents.AI.Hosting; /// such as Redis, SQL Server, or Azure Cosmos DB. /// /// -public sealed class InMemoryAgentThreadStore : AgentThreadStore +public sealed class InMemoryAgentSessionStore : AgentSessionStore { private readonly ConcurrentDictionary _threads = new(); /// - public override ValueTask SaveThreadAsync(AIAgent agent, string conversationId, AgentThread thread, CancellationToken cancellationToken = default) + public override ValueTask SaveSessionAsync(AIAgent agent, string conversationId, AgentSession session, CancellationToken cancellationToken = default) { var key = GetKey(conversationId, agent.Id); - this._threads[key] = thread.Serialize(); + this._threads[key] = session.Serialize(); return default; } /// - public override async ValueTask GetThreadAsync(AIAgent agent, string conversationId, CancellationToken cancellationToken = default) + public override async ValueTask GetSessionAsync(AIAgent agent, string conversationId, CancellationToken cancellationToken = default) { var key = GetKey(conversationId, agent.Id); - JsonElement? threadContent = this._threads.TryGetValue(key, out var existingThread) ? existingThread : null; + JsonElement? sessionContent = this._threads.TryGetValue(key, out var existingSession) ? existingSession : null; - return threadContent switch + return sessionContent switch { - null => await agent.GetNewThreadAsync(cancellationToken).ConfigureAwait(false), - _ => await agent.DeserializeThreadAsync(threadContent.Value, cancellationToken: cancellationToken).ConfigureAwait(false), + null => await agent.GetNewSessionAsync(cancellationToken).ConfigureAwait(false), + _ => await agent.DeserializeSessionAsync(sessionContent.Value, cancellationToken: cancellationToken).ConfigureAwait(false), }; } diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/NoopAgentSessionStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/NoopAgentSessionStore.cs new file mode 100644 index 0000000000..c0736afda3 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.Hosting/NoopAgentSessionStore.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Agents.AI.Hosting; + +/// +/// This store implementation does not have any store under the hood and therefore does not store sessions. +/// always returns a new session. +/// +public sealed class NoopAgentSessionStore : AgentSessionStore +{ + /// + public override ValueTask SaveSessionAsync(AIAgent agent, string conversationId, AgentSession session, CancellationToken cancellationToken = default) + { + return new ValueTask(); + } + + /// + public override ValueTask GetSessionAsync(AIAgent agent, string conversationId, CancellationToken cancellationToken = default) + { + return agent.GetNewSessionAsync(cancellationToken); + } +} diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/NoopAgentThreadStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/NoopAgentThreadStore.cs deleted file mode 100644 index 02c78178a0..0000000000 --- a/dotnet/src/Microsoft.Agents.AI.Hosting/NoopAgentThreadStore.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Agents.AI.Hosting; - -/// -/// This store implementation does not have any store under the hood and operates with empty threads. -/// It is the "noop" store, and could be used if you are keeping the thread contents on the client side for example. -/// -public sealed class NoopAgentThreadStore : AgentThreadStore -{ - /// - public override ValueTask SaveThreadAsync(AIAgent agent, string conversationId, AgentThread thread, CancellationToken cancellationToken = default) - { - return new ValueTask(); - } - - /// - public override ValueTask GetThreadAsync(AIAgent agent, string conversationId, CancellationToken cancellationToken = default) - { - return agent.GetNewThreadAsync(cancellationToken); - } -} diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs index defc934b30..a3598c0abc 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs @@ -25,7 +25,7 @@ public static class AIAgentWithOpenAIExtensions /// /// The AI agent to run. /// The collection of OpenAI chat messages to send to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided messages and agent response. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// A representing the asynchronous operation that returns a native OpenAI response. @@ -36,12 +36,12 @@ public static class AIAgentWithOpenAIExtensions /// This method converts the OpenAI chat messages to the Microsoft Extensions AI format using the appropriate conversion method, /// runs the agent with the converted message collection, and then extracts the native OpenAI from the response using . /// - public static async Task RunAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public static async Task RunAsync(this AIAgent agent, IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { Throw.IfNull(agent); Throw.IfNull(messages); - var response = await agent.RunAsync([.. messages.AsChatMessages()], thread, options, cancellationToken).ConfigureAwait(false); + var response = await agent.RunAsync([.. messages.AsChatMessages()], session, options, cancellationToken).ConfigureAwait(false); return response.AsOpenAIChatCompletion(); } @@ -51,7 +51,7 @@ public static class AIAgentWithOpenAIExtensions /// /// The AI agent to run. /// The collection of OpenAI chat messages to send to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided message and agent response. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided message and agent response. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// A representing the asynchronous operation that returns a native OpenAI response. @@ -62,12 +62,12 @@ public static class AIAgentWithOpenAIExtensions /// This method converts the OpenAI chat messages to the Microsoft Extensions AI format using the appropriate conversion method, /// runs the agent, and then extracts the native OpenAI from the response using . /// - public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { Throw.IfNull(agent); Throw.IfNull(messages); - IAsyncEnumerable response = agent.RunStreamingAsync([.. messages.AsChatMessages()], thread, options, cancellationToken); + IAsyncEnumerable response = agent.RunStreamingAsync([.. messages.AsChatMessages()], session, options, cancellationToken); return new AsyncStreamingChatCompletionUpdateCollectionResult(response); } @@ -77,7 +77,7 @@ public static class AIAgentWithOpenAIExtensions /// /// The AI agent to run. /// The collection of OpenAI response items to send to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided messages and agent response. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// A representing the asynchronous operation that returns a native OpenAI response. @@ -88,12 +88,12 @@ public static class AIAgentWithOpenAIExtensions /// This method converts the OpenAI response items to the Microsoft Extensions AI format using the appropriate conversion method, /// runs the agent with the converted message collection, and then extracts the native OpenAI from the response using . /// - public static async Task RunAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public static async Task RunAsync(this AIAgent agent, IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { Throw.IfNull(agent); Throw.IfNull(messages); - var response = await agent.RunAsync(messages.AsChatMessages(), thread, options, cancellationToken).ConfigureAwait(false); + var response = await agent.RunAsync(messages.AsChatMessages(), session, options, cancellationToken).ConfigureAwait(false); return response.AsOpenAIResponse(); } @@ -103,7 +103,7 @@ public static class AIAgentWithOpenAIExtensions /// /// The AI agent to run. /// The collection of OpenAI response items to send to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response updates. + /// The conversation session to continue with this invocation. If not provided, creates a new session. The session will be mutated with the provided messages and agent response updates. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . /// An representing the asynchronous enumerable that yields native OpenAI instances as they are streamed. @@ -116,12 +116,12 @@ public static class AIAgentWithOpenAIExtensions /// The method attempts to extract from the underlying response representation. If a raw update is not available, /// it is skipped because the OpenAI library does not currently expose model factory methods for creating such instances. /// - public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { Throw.IfNull(agent); Throw.IfNull(messages); - IAsyncEnumerable response = agent.RunStreamingAsync([.. messages.AsChatMessages()], thread, options, cancellationToken); + IAsyncEnumerable response = agent.RunStreamingAsync([.. messages.AsChatMessages()], session, options, cancellationToken); return new AsyncStreamingResponseUpdateCollectionResult(response); } diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/IScopedContentProcessor.cs b/dotnet/src/Microsoft.Agents.AI.Purview/IScopedContentProcessor.cs index 059e7c4d2d..cf60e7ec36 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/IScopedContentProcessor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/IScopedContentProcessor.cs @@ -18,11 +18,11 @@ internal interface IScopedContentProcessor /// The list of messages should be a prompt or response. /// /// A list of objects sent to the agent or received from the agent.. - /// The thread where the messages were sent. + /// The session where the messages were sent. /// An activity to indicate prompt or response. /// Purview settings containing tenant id, app name, etc. /// The user who sent the prompt or is receiving the response. /// Cancellation token. /// A bool indicating if the request should be blocked and the user id of the user who made the request. - Task<(bool shouldBlock, string? userId)> ProcessMessagesAsync(IEnumerable messages, string? threadId, Activity activity, PurviewSettings purviewSettings, string? userId, CancellationToken cancellationToken); + Task<(bool shouldBlock, string? userId)> ProcessMessagesAsync(IEnumerable messages, string? sessionId, Activity activity, PurviewSettings purviewSettings, string? userId, CancellationToken cancellationToken); } diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs index c30c089198..336be0e108 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs @@ -30,27 +30,27 @@ internal class PurviewAgent : AIAgent, IDisposable } /// - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { - return this._innerAgent.DeserializeThreadAsync(serializedThread, jsonSerializerOptions, cancellationToken); + return this._innerAgent.DeserializeSessionAsync(serializedSession, jsonSerializerOptions, cancellationToken); } /// - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) { - return this._innerAgent.GetNewThreadAsync(cancellationToken); + return this._innerAgent.GetNewSessionAsync(cancellationToken); } /// - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this._purviewWrapper.ProcessAgentContentAsync(messages, thread, options, this._innerAgent, cancellationToken); + return this._purviewWrapper.ProcessAgentContentAsync(messages, session, options, this._innerAgent, cancellationToken); } /// - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var response = await this._purviewWrapper.ProcessAgentContentAsync(messages, thread, options, this._innerAgent, cancellationToken).ConfigureAwait(false); + var response = await this._purviewWrapper.ProcessAgentContentAsync(messages, session, options, this._innerAgent, cancellationToken).ConfigureAwait(false); foreach (var update in response.ToAgentResponseUpdates()) { yield return update; diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs index 14cddbe5c4..5a63448478 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs @@ -35,12 +35,12 @@ internal sealed class PurviewWrapper : IDisposable this._backgroundJobRunner = backgroundJobRunner; } - private static string GetThreadIdFromAgentThread(AgentThread? thread, IEnumerable messages) + private static string GetSessionIdFromAgentSession(AgentSession? session, IEnumerable messages) { - if (thread is ChatClientAgentThread chatClientAgentThread && - chatClientAgentThread.ConversationId != null) + if (session is ChatClientAgentSession chatClientAgentSession && + chatClientAgentSession.ConversationId != null) { - return chatClientAgentThread.ConversationId; + return chatClientAgentSession.ConversationId; } foreach (ChatMessage message in messages) @@ -129,20 +129,20 @@ internal sealed class PurviewWrapper : IDisposable /// Processes a prompt and response exchange at an agent level. /// /// The messages sent to the agent. - /// The thread used for this agent conversation. + /// The session used for this agent conversation. /// The options used with this agent. /// The wrapped agent. /// The cancellation token used to interrupt async operations. /// The agent's response. This could be the response from the agent or a message indicating that Purview has blocked the prompt or response. - public async Task ProcessAgentContentAsync(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) + public async Task ProcessAgentContentAsync(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { - string threadId = GetThreadIdFromAgentThread(thread, messages); + string sessionId = GetSessionIdFromAgentSession(session, messages); string? resolvedUserId = null; try { - (bool shouldBlockPrompt, resolvedUserId) = await this._scopedProcessor.ProcessMessagesAsync(messages, threadId, Activity.UploadText, this._purviewSettings, null, cancellationToken).ConfigureAwait(false); + (bool shouldBlockPrompt, resolvedUserId) = await this._scopedProcessor.ProcessMessagesAsync(messages, sessionId, Activity.UploadText, this._purviewSettings, null, cancellationToken).ConfigureAwait(false); if (shouldBlockPrompt) { @@ -167,11 +167,11 @@ internal sealed class PurviewWrapper : IDisposable } } - AgentResponse response = await innerAgent.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false); + AgentResponse response = await innerAgent.RunAsync(messages, session, options, cancellationToken).ConfigureAwait(false); try { - (bool shouldBlockResponse, _) = await this._scopedProcessor.ProcessMessagesAsync(response.Messages, threadId, Activity.UploadText, this._purviewSettings, resolvedUserId, cancellationToken).ConfigureAwait(false); + (bool shouldBlockResponse, _) = await this._scopedProcessor.ProcessMessagesAsync(response.Messages, sessionId, Activity.UploadText, this._purviewSettings, resolvedUserId, cancellationToken).ConfigureAwait(false); if (shouldBlockResponse) { diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/ScopedContentProcessor.cs b/dotnet/src/Microsoft.Agents.AI.Purview/ScopedContentProcessor.cs index 454da2d549..177b1a07d8 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/ScopedContentProcessor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/ScopedContentProcessor.cs @@ -35,9 +35,9 @@ internal sealed class ScopedContentProcessor : IScopedContentProcessor } /// - public async Task<(bool shouldBlock, string? userId)> ProcessMessagesAsync(IEnumerable messages, string? threadId, Activity activity, PurviewSettings purviewSettings, string? userId, CancellationToken cancellationToken) + public async Task<(bool shouldBlock, string? userId)> ProcessMessagesAsync(IEnumerable messages, string? sessionId, Activity activity, PurviewSettings purviewSettings, string? userId, CancellationToken cancellationToken) { - List pcRequests = await this.MapMessageToPCRequestsAsync(messages, threadId, activity, purviewSettings, userId, cancellationToken).ConfigureAwait(false); + List pcRequests = await this.MapMessageToPCRequestsAsync(messages, sessionId, activity, purviewSettings, userId, cancellationToken).ConfigureAwait(false); bool shouldBlock = false; string? resolvedUserId = null; @@ -93,13 +93,13 @@ internal sealed class ScopedContentProcessor : IScopedContentProcessor /// Transform a list of ChatMessages into a list of ProcessContentRequests. /// /// The messages to transform. - /// The id of the message thread. + /// The id of the message session. /// The activity performed on the content. /// The settings used for purview integration. /// The entra id of the user who made the interaction. /// The cancellation token used to cancel async operations. /// A list of process content requests. - private async Task> MapMessageToPCRequestsAsync(IEnumerable messages, string? threadId, Activity activity, PurviewSettings settings, string? userId, CancellationToken cancellationToken) + private async Task> MapMessageToPCRequestsAsync(IEnumerable messages, string? sessionId, Activity activity, PurviewSettings settings, string? userId, CancellationToken cancellationToken) { List pcRequests = []; TokenInfo? tokenInfo = null; @@ -123,7 +123,7 @@ internal sealed class ScopedContentProcessor : IScopedContentProcessor ContentBase content = new PurviewTextContent(message.Text); ProcessConversationMetadata conversationmetadata = new(content, messageId, false, $"Agent Framework Message {messageId}") { - CorrelationId = threadId ?? Guid.NewGuid().ToString() + CorrelationId = sessionId ?? Guid.NewGuid().ToString() }; ActivityMetadata activityMetadata = new(activity); PolicyLocation policyLocation; diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs index 68dbc10704..f53a6f076b 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs @@ -16,8 +16,7 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor { private readonly AIAgent _agent; private readonly AIAgentHostOptions _options; - - private AgentThread? _thread; + private AgentSession? _session; private bool? _currentTurnEmitEvents; private AIContentExternalHandler? _userInputHandler; @@ -93,8 +92,8 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor public bool ShouldEmitStreamingEvents(bool? emitEvents) => emitEvents ?? this._options.EmitAgentUpdateEvents ?? false; - private async ValueTask EnsureThreadAsync(IWorkflowContext context, CancellationToken cancellationToken) => - this._thread ??= await this._agent.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); + private async ValueTask EnsureSessionAsync(IWorkflowContext context, CancellationToken cancellationToken) => + this._session ??= await this._agent.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); private const string UserInputRequestStateKey = nameof(_userInputHandler); private const string FunctionCallRequestStateKey = nameof(_functionCallHandler); @@ -102,7 +101,7 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor protected internal override async ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellationToken = default) { - AIAgentHostState state = new(this._thread?.Serialize(), this._currentTurnEmitEvents); + AIAgentHostState state = new(this._session?.Serialize(), this._currentTurnEmitEvents); Task coreStateTask = context.QueueStateUpdateAsync(AIAgentHostStateKey, state, cancellationToken: cancellationToken).AsTask(); Task userInputRequestsTask = this._userInputHandler?.OnCheckpointingAsync(UserInputRequestStateKey, context, cancellationToken).AsTask() ?? Task.CompletedTask; Task functionCallRequestsTask = this._functionCallHandler?.OnCheckpointingAsync(FunctionCallRequestStateKey, context, cancellationToken).AsTask() ?? Task.CompletedTask; @@ -120,8 +119,8 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor AIAgentHostState? state = await context.ReadStateAsync(AIAgentHostStateKey, cancellationToken: cancellationToken).ConfigureAwait(false); if (state != null) { - this._thread = state.ThreadState.HasValue - ? await this._agent.DeserializeThreadAsync(state.ThreadState.Value, cancellationToken: cancellationToken).ConfigureAwait(false) + this._session = state.ThreadState.HasValue + ? await this._agent.DeserializeSessionAsync(state.ThreadState.Value, cancellationToken: cancellationToken).ConfigureAwait(false) : null; this._currentTurnEmitEvents = state.CurrentTurnEmitEvents; } @@ -175,7 +174,7 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor // Run the agent in streaming mode only when agent run update events are to be emitted. IAsyncEnumerable agentStream = this._agent.RunStreamingAsync( messages, - await this.EnsureThreadAsync(context, cancellationToken).ConfigureAwait(false), + await this.EnsureSessionAsync(context, cancellationToken).ConfigureAwait(false), cancellationToken: cancellationToken); List updates = []; @@ -192,7 +191,7 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor { // Otherwise, run the agent in non-streaming mode. response = await this._agent.RunAsync(messages, - await this.EnsureThreadAsync(context, cancellationToken).ConfigureAwait(false), + await this.EnsureSessionAsync(context, cancellationToken).ConfigureAwait(false), cancellationToken: cancellationToken) .ConfigureAwait(false); diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs index fe597c17f3..3bd1059ce8 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs @@ -65,60 +65,60 @@ internal sealed class WorkflowHostAgent : AIAgent protocol.ThrowIfNotChatProtocol(allowCatchAll: true); } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) - => new(new WorkflowThread(this._workflow, this.GenerateNewId(), this._executionEnvironment, this._checkpointManager, this._includeExceptionDetails, this._includeWorkflowOutputsInResponse)); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) + => new(new WorkflowSession(this._workflow, this.GenerateNewId(), this._executionEnvironment, this._checkpointManager, this._includeExceptionDetails, this._includeWorkflowOutputsInResponse)); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new WorkflowThread(this._workflow, serializedThread, this._executionEnvironment, this._checkpointManager, this._includeExceptionDetails, this._includeWorkflowOutputsInResponse, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new WorkflowSession(this._workflow, serializedSession, this._executionEnvironment, this._checkpointManager, this._includeExceptionDetails, this._includeWorkflowOutputsInResponse, jsonSerializerOptions)); - private async ValueTask UpdateThreadAsync(IEnumerable messages, AgentThread? thread = null, CancellationToken cancellationToken = default) + private async ValueTask UpdateSessionAsync(IEnumerable messages, AgentSession? session = null, CancellationToken cancellationToken = default) { - thread ??= await this.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); + session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); - if (thread is not WorkflowThread workflowThread) + if (session is not WorkflowSession workflowSession) { - throw new ArgumentException($"Incompatible thread type: {thread.GetType()} (expecting {typeof(WorkflowThread)})", nameof(thread)); + throw new ArgumentException($"Incompatible session type: {session.GetType()} (expecting {typeof(WorkflowSession)})", nameof(session)); } // For workflow threads, messages are added directly via the internal AddMessages method // The MessageStore methods are used for agent invocation scenarios - workflowThread.ChatHistoryProvider.AddMessages(messages); - return workflowThread; + workflowSession.ChatHistoryProvider.AddMessages(messages); + return workflowSession; } protected override async Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { await this.ValidateWorkflowAsync().ConfigureAwait(false); - WorkflowThread workflowThread = await this.UpdateThreadAsync(messages, thread, cancellationToken).ConfigureAwait(false); + WorkflowSession workflowSession = await this.UpdateSessionAsync(messages, session, cancellationToken).ConfigureAwait(false); MessageMerger merger = new(); - await foreach (AgentResponseUpdate update in workflowThread.InvokeStageAsync(cancellationToken) + await foreach (AgentResponseUpdate update in workflowSession.InvokeStageAsync(cancellationToken) .ConfigureAwait(false) .WithCancellation(cancellationToken)) { merger.AddUpdate(update); } - return merger.ComputeMerged(workflowThread.LastResponseId!, this.Id, this.Name); + return merger.ComputeMerged(workflowSession.LastResponseId!, this.Id, this.Name); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { await this.ValidateWorkflowAsync().ConfigureAwait(false); - WorkflowThread workflowThread = await this.UpdateThreadAsync(messages, thread, cancellationToken).ConfigureAwait(false); - await foreach (AgentResponseUpdate update in workflowThread.InvokeStageAsync(cancellationToken) + WorkflowSession workflowSession = await this.UpdateSessionAsync(messages, session, cancellationToken).ConfigureAwait(false); + await foreach (AgentResponseUpdate update in workflowSession.InvokeStageAsync(cancellationToken) .ConfigureAwait(false) .WithCancellation(cancellationToken)) { diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowThread.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowSession.cs similarity index 88% rename from dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowThread.cs rename to dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowSession.cs index 270ec02149..d7dc1c74ce 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowSession.cs @@ -14,7 +14,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Workflows; -internal sealed class WorkflowThread : AgentThread +internal sealed class WorkflowSession : AgentSession { private readonly Workflow _workflow; private readonly IWorkflowExecutionEnvironment _executionEnvironment; @@ -24,7 +24,7 @@ internal sealed class WorkflowThread : AgentThread private readonly CheckpointManager _checkpointManager; private readonly InMemoryCheckpointManager? _inMemoryCheckpointManager; - public WorkflowThread(Workflow workflow, string runId, IWorkflowExecutionEnvironment executionEnvironment, CheckpointManager? checkpointManager = null, bool includeExceptionDetails = false, bool includeWorkflowOutputsInResponse = false) + public WorkflowSession(Workflow workflow, string runId, IWorkflowExecutionEnvironment executionEnvironment, CheckpointManager? checkpointManager = null, bool includeExceptionDetails = false, bool includeWorkflowOutputsInResponse = false) { this._workflow = Throw.IfNull(workflow); this._executionEnvironment = Throw.IfNull(executionEnvironment); @@ -40,7 +40,7 @@ internal sealed class WorkflowThread : AgentThread this.ChatHistoryProvider = new WorkflowChatHistoryProvider(); } - public WorkflowThread(Workflow workflow, JsonElement serializedThread, IWorkflowExecutionEnvironment executionEnvironment, CheckpointManager? checkpointManager = null, bool includeExceptionDetails = false, bool includeWorkflowOutputsInResponse = false, JsonSerializerOptions? jsonSerializerOptions = null) + public WorkflowSession(Workflow workflow, JsonElement serializedSession, IWorkflowExecutionEnvironment executionEnvironment, CheckpointManager? checkpointManager = null, bool includeExceptionDetails = false, bool includeWorkflowOutputsInResponse = false, JsonSerializerOptions? jsonSerializerOptions = null) { this._workflow = Throw.IfNull(workflow); this._executionEnvironment = Throw.IfNull(executionEnvironment); @@ -48,19 +48,19 @@ internal sealed class WorkflowThread : AgentThread this._includeWorkflowOutputsInResponse = includeWorkflowOutputsInResponse; JsonMarshaller marshaller = new(jsonSerializerOptions); - ThreadState threadState = marshaller.Marshal(serializedThread); + SessionState sessionState = marshaller.Marshal(serializedSession); - this._inMemoryCheckpointManager = threadState.CheckpointManager; + this._inMemoryCheckpointManager = sessionState.CheckpointManager; if (this._inMemoryCheckpointManager is not null && checkpointManager is not null) { - // The thread was externalized with an in-memory checkpoint manager, but the caller is providing an external one. - throw new ArgumentException("Cannot provide an external checkpoint manager when deserializing a thread that " + + // The session was externalized with an in-memory checkpoint manager, but the caller is providing an external one. + throw new ArgumentException("Cannot provide an external checkpoint manager when deserializing a session that " + "was serialized with an in-memory checkpoint manager.", nameof(checkpointManager)); } else if (this._inMemoryCheckpointManager is null && checkpointManager is null) { - // The thread was externalized without an in-memory checkpoint manager, and the caller is not providing an external one. - throw new ArgumentException("An external checkpoint manager must be provided when deserializing a thread that " + + // The session was externalized without an in-memory checkpoint manager, and the caller is not providing an external one. + throw new ArgumentException("An external checkpoint manager must be provided when deserializing a session that " + "was serialized without an in-memory checkpoint manager.", nameof(checkpointManager)); } else @@ -68,9 +68,9 @@ internal sealed class WorkflowThread : AgentThread this._checkpointManager = checkpointManager ?? new(this._inMemoryCheckpointManager!); } - this.RunId = threadState.RunId; - this.LastCheckpoint = threadState.LastCheckpoint; - this.ChatHistoryProvider = new WorkflowChatHistoryProvider(threadState.ChatHistoryProviderState); + this.RunId = sessionState.RunId; + this.LastCheckpoint = sessionState.LastCheckpoint; + this.ChatHistoryProvider = new WorkflowChatHistoryProvider(sessionState.ChatHistoryProviderState); } public CheckpointInfo? LastCheckpoint { get; set; } @@ -78,7 +78,7 @@ internal sealed class WorkflowThread : AgentThread public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { JsonMarshaller marshaller = new(jsonSerializerOptions); - ThreadState info = new( + SessionState info = new( this.RunId, this.LastCheckpoint, this.ChatHistoryProvider.ExportStoreState(), @@ -124,7 +124,7 @@ internal sealed class WorkflowThread : AgentThread private async ValueTask> CreateOrResumeRunAsync(List messages, CancellationToken cancellationToken = default) { - // The workflow is validated to be a ChatProtocol workflow by the WorkflowHostAgent before creating the thread, + // The workflow is validated to be a ChatProtocol workflow by the WorkflowHostAgent before creating the session, // and does not need to be checked again here. if (this.LastCheckpoint is not null) { @@ -251,7 +251,7 @@ internal sealed class WorkflowThread : AgentThread /// public WorkflowChatHistoryProvider ChatHistoryProvider { get; } - internal sealed class ThreadState( + internal sealed class SessionState( string runId, CheckpointInfo? lastCheckpoint, WorkflowChatHistoryProvider.StoreState chatHistoryProviderState, diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowsJsonUtilities.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowsJsonUtilities.cs index 65467f09a2..08d1dcbcbb 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowsJsonUtilities.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowsJsonUtilities.cs @@ -84,7 +84,7 @@ internal static partial class WorkflowsJsonUtilities // Workflow-as-Agent [JsonSerializable(typeof(WorkflowChatHistoryProvider.StoreState))] - [JsonSerializable(typeof(WorkflowThread.ThreadState))] + [JsonSerializable(typeof(WorkflowSession.SessionState))] // Message Types [JsonSerializable(typeof(ChatMessage))] diff --git a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs b/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs index 7d629c42b1..961fd7f20e 100644 --- a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs +++ b/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs @@ -94,13 +94,13 @@ public sealed class AIAgentBuilder /// /// Adds to the agent pipeline an anonymous delegating agent based on a delegate that provides - /// an implementation for both and . + /// an implementation for both and . /// /// - /// A delegate that provides the implementation for both and - /// . This delegate is invoked with the list of messages, the agent - /// thread, the run options, a delegate that represents invoking the inner agent, and a cancellation token. The delegate should be passed - /// whatever messages, thread, options, and cancellation token should be passed along to the next stage in the pipeline. + /// A delegate that provides the implementation for both and + /// . This delegate is invoked with the list of messages, the agent + /// session, the run options, a delegate that represents invoking the inner agent, and a cancellation token. The delegate should be passed + /// whatever messages, session, options, and cancellation token should be passed along to the next stage in the pipeline. /// It will handle both the non-streaming and streaming cases. /// /// The updated instance. @@ -109,7 +109,7 @@ public sealed class AIAgentBuilder /// need to interact with the results of the operation, which will come from the inner agent. /// /// is . - public AIAgentBuilder Use(Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task> sharedFunc) + public AIAgentBuilder Use(Func, AgentSession?, AgentRunOptions?, Func, AgentSession?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task> sharedFunc) { _ = Throw.IfNull(sharedFunc); @@ -118,33 +118,33 @@ public sealed class AIAgentBuilder /// /// Adds to the agent pipeline an anonymous delegating agent based on a delegate that provides - /// an implementation for both and . + /// an implementation for both and . /// /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// The updated instance. /// /// One or both delegates can be provided. If both are provided, they will be used for their respective methods: - /// will provide the implementation of , and - /// will provide the implementation of . + /// will provide the implementation of , and + /// will provide the implementation of . /// If only one of the delegates is provided, it will be used for both methods. That means that if - /// is supplied without , the implementation of + /// is supplied without , the implementation of /// will employ limited streaming, as it will be operating on the batch output produced by . And if /// is supplied without , the implementation of - /// will be implemented by combining the updates from . + /// will be implemented by combining the updates from . /// /// Both and are . public AIAgentBuilder Use( - Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, Task>? runFunc, - Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? runStreamingFunc) + Func, AgentSession?, AgentRunOptions?, AIAgent, CancellationToken, Task>? runFunc, + Func, AgentSession?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? runStreamingFunc) { AnonymousDelegatingAIAgent.ThrowIfBothDelegatesNull(runFunc, runStreamingFunc); diff --git a/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs b/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs index 07247b059d..2314d09273 100644 --- a/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs @@ -42,8 +42,8 @@ public static partial class AIAgentExtensions /// Optional metadata to customize the function representation, such as name and description. /// If not provided, defaults will be inferred from the agent's properties. /// - /// - /// Optional to use for function invocations. If not provided, a new thread + /// + /// Optional to use for function invocations. If not provided, a new session /// will be created for each function call, which may not preserve conversation context. /// /// @@ -59,12 +59,12 @@ public static partial class AIAgentExtensions /// /// /// The resulting is stateful, referencing both the and the optional - /// . Especially if a specific thread is provided, avoid using the resulting function concurrently - /// in multiple conversations or in requests where the parallel function calls may result in concurrent usage of the thread, + /// . Especially if a specific session is provided, avoid using the resulting function concurrently + /// in multiple conversations or in requests where the parallel function calls may result in concurrent usage of the session, /// as that could lead to undefined and unpredictable behavior. /// /// - public static AIFunction AsAIFunction(this AIAgent agent, AIFunctionFactoryOptions? options = null, AgentThread? thread = null) + public static AIFunction AsAIFunction(this AIAgent agent, AIFunctionFactoryOptions? options = null, AgentSession? session = null) { Throw.IfNull(agent); @@ -78,7 +78,7 @@ public static partial class AIAgentExtensions ? new AgentRunOptions { AdditionalProperties = dict } : null; - var response = await agent.RunAsync(query, thread: thread, options: agentRunOptions, cancellationToken: cancellationToken).ConfigureAwait(false); + var response = await agent.RunAsync(query, session: session, options: agentRunOptions, cancellationToken: cancellationToken).ConfigureAwait(false); return response.Text; } diff --git a/dotnet/src/Microsoft.Agents.AI/AgentJsonUtilities.cs b/dotnet/src/Microsoft.Agents.AI/AgentJsonUtilities.cs index fe3f73b28b..5c915b6b01 100644 --- a/dotnet/src/Microsoft.Agents.AI/AgentJsonUtilities.cs +++ b/dotnet/src/Microsoft.Agents.AI/AgentJsonUtilities.cs @@ -65,7 +65,7 @@ internal static partial class AgentJsonUtilities NumberHandling = JsonNumberHandling.AllowReadingFromString)] // Agent abstraction types - [JsonSerializable(typeof(ChatClientAgentThread.ThreadState))] + [JsonSerializable(typeof(ChatClientAgentSession.SessionState))] [JsonSerializable(typeof(TextSearchProvider.TextSearchProviderState))] [JsonSerializable(typeof(ChatHistoryMemoryProvider.ChatHistoryMemoryProviderState))] diff --git a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs index 48de303bc1..800d43311a 100644 --- a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs @@ -18,7 +18,7 @@ namespace Microsoft.Agents.AI; internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent { /// The delegate to use as the implementation of . - private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, Task>? _runFunc; + private readonly Func, AgentSession?, AgentRunOptions?, AIAgent, CancellationToken, Task>? _runFunc; /// The delegate to use as the implementation of . /// @@ -26,10 +26,10 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// will be invoked with the same arguments as the method itself. /// When , will delegate directly to the inner agent. /// - private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? _runStreamingFunc; + private readonly Func, AgentSession?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? _runStreamingFunc; /// The delegate to use as the implementation of both and . - private readonly Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>? _sharedFunc; + private readonly Func, AgentSession?, AgentRunOptions?, Func, AgentSession?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>? _sharedFunc; /// /// Initializes a new instance of the class. @@ -48,7 +48,7 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// is . public AnonymousDelegatingAIAgent( AIAgent innerAgent, - Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task> sharedFunc) + Func, AgentSession?, AgentRunOptions?, Func, AgentSession?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task> sharedFunc) : base(innerAgent) { _ = Throw.IfNull(sharedFunc); @@ -74,8 +74,8 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// Both and are . public AnonymousDelegatingAIAgent( AIAgent innerAgent, - Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, Task>? runFunc, - Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? runStreamingFunc) + Func, AgentSession?, AgentRunOptions?, AIAgent, CancellationToken, Task>? runFunc, + Func, AgentSession?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? runStreamingFunc) : base(innerAgent) { ThrowIfBothDelegatesNull(runFunc, runStreamingFunc); @@ -87,7 +87,7 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -95,19 +95,19 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent if (this._sharedFunc is not null) { - return GetRunViaSharedAsync(messages, thread, options, cancellationToken); + return GetRunViaSharedAsync(messages, session, options, cancellationToken); async Task GetRunViaSharedAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, CancellationToken cancellationToken) + IEnumerable messages, AgentSession? session, AgentRunOptions? options, CancellationToken cancellationToken) { AgentResponse? response = null; await this._sharedFunc( messages, - thread, + session, options, - async (messages, thread, options, cancellationToken) - => response = await this.InnerAgent.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false), + async (messages, session, options, cancellationToken) + => response = await this.InnerAgent.RunAsync(messages, session, options, cancellationToken).ConfigureAwait(false), cancellationToken) .ConfigureAwait(false); @@ -121,12 +121,12 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent } else if (this._runFunc is not null) { - return this._runFunc(messages, thread, options, this.InnerAgent, cancellationToken); + return this._runFunc(messages, session, options, this.InnerAgent, cancellationToken); } else { Debug.Assert(this._runStreamingFunc is not null, "Expected non-null streaming delegate."); - return this._runStreamingFunc!(messages, thread, options, this.InnerAgent, cancellationToken) + return this._runStreamingFunc!(messages, session, options, this.InnerAgent, cancellationToken) .ToAgentResponseAsync(cancellationToken); } } @@ -134,7 +134,7 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -150,9 +150,9 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent Exception? error = null; try { - await this._sharedFunc(messages, thread, options, async (messages, thread, options, cancellationToken) => + await this._sharedFunc(messages, session, options, async (messages, session, options, cancellationToken) => { - await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { await updates.Writer.WriteAsync(update, cancellationToken).ConfigureAwait(false); } @@ -173,12 +173,12 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent } else if (this._runStreamingFunc is not null) { - return this._runStreamingFunc(messages, thread, options, this.InnerAgent, cancellationToken); + return this._runStreamingFunc(messages, session, options, this.InnerAgent, cancellationToken); } else { Debug.Assert(this._runFunc is not null, "Expected non-null non-streaming delegate."); - return GetStreamingRunAsyncViaRunAsync(this._runFunc!(messages, thread, options, this.InnerAgent, cancellationToken)); + return GetStreamingRunAsyncViaRunAsync(this._runFunc!(messages, session, options, this.InnerAgent, cancellationToken)); static async IAsyncEnumerable GetStreamingRunAsyncViaRunAsync(Task task) { diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs index 3b416c8979..937a6d0f0b 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs @@ -151,7 +151,7 @@ public sealed partial class ChatClientAgent : AIAgent /// protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -168,7 +168,7 @@ public sealed partial class ChatClientAgent : AIAgent }; } - return this.RunCoreAsync(GetResponseAsync, CreateResponse, messages, thread, options, cancellationToken); + return this.RunCoreAsync(GetResponseAsync, CreateResponse, messages, session, options, cancellationToken); } /// @@ -198,19 +198,19 @@ public sealed partial class ChatClientAgent : AIAgent /// protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); - (ChatClientAgentThread safeThread, + (ChatClientAgentSession safeSession, ChatOptions? chatOptions, List inputMessagesForChatClient, IList? aiContextProviderMessages, IList? chatHistoryProviderMessages, ChatClientAgentContinuationToken? continuationToken) = - await this.PrepareThreadAndMessagesAsync(thread, inputMessages, options, cancellationToken).ConfigureAwait(false); + await this.PrepareSessionAndMessagesAsync(session, inputMessages, options, cancellationToken).ConfigureAwait(false); var chatClient = this.ChatClient; @@ -231,8 +231,8 @@ public sealed partial class ChatClientAgent : AIAgent } catch (Exception ex) { - await NotifyChatHistoryProviderOfFailureAsync(safeThread, ex, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); - await NotifyAIContextProviderOfFailureAsync(safeThread, ex, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, cancellationToken).ConfigureAwait(false); + await NotifyChatHistoryProviderOfFailureAsync(safeSession, ex, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); + await NotifyAIContextProviderOfFailureAsync(safeSession, ex, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, cancellationToken).ConfigureAwait(false); throw; } @@ -246,8 +246,8 @@ public sealed partial class ChatClientAgent : AIAgent } catch (Exception ex) { - await NotifyChatHistoryProviderOfFailureAsync(safeThread, ex, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); - await NotifyAIContextProviderOfFailureAsync(safeThread, ex, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, cancellationToken).ConfigureAwait(false); + await NotifyChatHistoryProviderOfFailureAsync(safeSession, ex, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); + await NotifyAIContextProviderOfFailureAsync(safeSession, ex, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, cancellationToken).ConfigureAwait(false); throw; } @@ -273,23 +273,23 @@ public sealed partial class ChatClientAgent : AIAgent } catch (Exception ex) { - await NotifyChatHistoryProviderOfFailureAsync(safeThread, ex, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); - await NotifyAIContextProviderOfFailureAsync(safeThread, ex, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, cancellationToken).ConfigureAwait(false); + await NotifyChatHistoryProviderOfFailureAsync(safeSession, ex, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); + await NotifyAIContextProviderOfFailureAsync(safeSession, ex, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, cancellationToken).ConfigureAwait(false); throw; } } var chatResponse = responseUpdates.ToChatResponse(); - // We can derive the type of supported thread from whether we have a conversation id, - // so let's update it and set the conversation id for the service thread case. - await this.UpdateThreadWithTypeAndConversationIdAsync(safeThread, chatResponse.ConversationId, cancellationToken).ConfigureAwait(false); + // We can derive the type of supported session from whether we have a conversation id, + // so let's update it and set the conversation id for the service session case. + await this.UpdateSessionWithTypeAndConversationIdAsync(safeSession, chatResponse.ConversationId, cancellationToken).ConfigureAwait(false); - // To avoid inconsistent state we only notify the thread of the input messages if no error occurs after the initial request. - await NotifyChatHistoryProviderOfNewMessagesAsync(safeThread, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatResponse.Messages, chatOptions, cancellationToken).ConfigureAwait(false); + // To avoid inconsistent state we only notify the session of the input messages if no error occurs after the initial request. + await NotifyChatHistoryProviderOfNewMessagesAsync(safeSession, GetInputMessages(inputMessages, continuationToken), chatHistoryProviderMessages, aiContextProviderMessages, chatResponse.Messages, chatOptions, cancellationToken).ConfigureAwait(false); // Notify the AIContextProvider of all new messages. - await NotifyAIContextProviderOfSuccessAsync(safeThread, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, chatResponse.Messages, cancellationToken).ConfigureAwait(false); + await NotifyAIContextProviderOfSuccessAsync(safeSession, GetInputMessages(inputMessages, continuationToken), aiContextProviderMessages, chatResponse.Messages, cancellationToken).ConfigureAwait(false); } /// @@ -302,7 +302,7 @@ public sealed partial class ChatClientAgent : AIAgent : this.ChatClient.GetService(serviceType, serviceKey)); /// - public override async ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override async ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) { ChatHistoryProvider? chatHistoryProvider = this._agentOptions?.ChatHistoryProviderFactory is not null ? await this._agentOptions.ChatHistoryProviderFactory.Invoke(new() { SerializedState = default, JsonSerializerOptions = null }, cancellationToken).ConfigureAwait(false) @@ -312,7 +312,7 @@ public sealed partial class ChatClientAgent : AIAgent ? await this._agentOptions.AIContextProviderFactory.Invoke(new() { SerializedState = default, JsonSerializerOptions = null }, cancellationToken).ConfigureAwait(false) : null; - return new ChatClientAgentThread + return new ChatClientAgentSession { ChatHistoryProvider = chatHistoryProvider, AIContextProvider = contextProvider @@ -320,16 +320,16 @@ public sealed partial class ChatClientAgent : AIAgent } /// - /// Creates a new agent thread instance using an existing conversation identifier to continue that conversation. + /// Creates a new agent session instance using an existing conversation identifier to continue that conversation. /// /// The identifier of an existing conversation to continue. /// The to monitor for cancellation requests. /// - /// A value task representing the asynchronous operation. The task result contains a new instance configured to work with the specified conversation. + /// A value task representing the asynchronous operation. The task result contains a new instance configured to work with the specified conversation. /// /// /// - /// This method creates an that relies on server-side chat history storage, where the chat history + /// This method creates an that relies on server-side chat history storage, where the chat history /// is maintained by the underlying AI service rather than by a local . /// /// @@ -337,13 +337,13 @@ public sealed partial class ChatClientAgent : AIAgent /// instances that support server-side conversation storage through their underlying . /// /// - public async ValueTask GetNewThreadAsync(string conversationId, CancellationToken cancellationToken = default) + public async ValueTask GetNewSessionAsync(string conversationId, CancellationToken cancellationToken = default) { AIContextProvider? contextProvider = this._agentOptions?.AIContextProviderFactory is not null ? await this._agentOptions.AIContextProviderFactory.Invoke(new() { SerializedState = default, JsonSerializerOptions = null }, cancellationToken).ConfigureAwait(false) : null; - return new ChatClientAgentThread() + return new ChatClientAgentSession() { ConversationId = conversationId, AIContextProvider = contextProvider @@ -351,34 +351,34 @@ public sealed partial class ChatClientAgent : AIAgent } /// - /// Creates a new agent thread instance using an existing to continue a conversation. + /// Creates a new agent session instance using an existing to continue a conversation. /// /// The instance to use for managing the conversation's message history. /// The to monitor for cancellation requests. /// - /// A value task representing the asynchronous operation. The task result contains a new instance configured to work with the provided . + /// A value task representing the asynchronous operation. The task result contains a new instance configured to work with the provided . /// /// /// /// This method creates threads that do not support server-side conversation storage. - /// Some AI services require server-side conversation storage to function properly, and creating a thread + /// Some AI services require server-side conversation storage to function properly, and creating a session /// with a may not be compatible with these services. /// /// - /// Where a service requires server-side conversation storage, use . + /// Where a service requires server-side conversation storage, use . /// /// /// If the agent detects, during the first run, that the underlying AI service requires server-side conversation storage, - /// the thread will throw an exception to indicate that it cannot continue using the provided . + /// the session will throw an exception to indicate that it cannot continue using the provided . /// /// - public async ValueTask GetNewThreadAsync(ChatHistoryProvider chatHistoryProvider, CancellationToken cancellationToken = default) + public async ValueTask GetNewSessionAsync(ChatHistoryProvider chatHistoryProvider, CancellationToken cancellationToken = default) { AIContextProvider? contextProvider = this._agentOptions?.AIContextProviderFactory is not null ? await this._agentOptions.AIContextProviderFactory.Invoke(new() { SerializedState = default, JsonSerializerOptions = null }, cancellationToken).ConfigureAwait(false) : null; - return new ChatClientAgentThread() + return new ChatClientAgentSession() { ChatHistoryProvider = Throw.IfNull(chatHistoryProvider), AIContextProvider = contextProvider @@ -386,7 +386,7 @@ public sealed partial class ChatClientAgent : AIAgent } /// - public override async ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override async ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { Func>? chatHistoryProviderFactory = this._agentOptions?.ChatHistoryProviderFactory is null ? null : @@ -396,8 +396,8 @@ public sealed partial class ChatClientAgent : AIAgent null : (jse, jso, ct) => this._agentOptions.AIContextProviderFactory.Invoke(new() { SerializedState = jse, JsonSerializerOptions = jso }, ct); - return await ChatClientAgentThread.DeserializeAsync( - serializedThread, + return await ChatClientAgentSession.DeserializeAsync( + serializedSession, jsonSerializerOptions, chatHistoryProviderFactory, aiContextProviderFactory, @@ -410,7 +410,7 @@ public sealed partial class ChatClientAgent : AIAgent Func, ChatOptions?, CancellationToken, Task> chatClientRunFunc, Func agentResponseFactoryFunc, IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) where TAgentResponse : AgentResponse @@ -418,13 +418,13 @@ public sealed partial class ChatClientAgent : AIAgent { var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); - (ChatClientAgentThread safeThread, + (ChatClientAgentSession safeSession, ChatOptions? chatOptions, List inputMessagesForChatClient, IList? aiContextProviderMessages, IList? chatHistoryProviderMessages, ChatClientAgentContinuationToken? _) = - await this.PrepareThreadAndMessagesAsync(thread, inputMessages, options, cancellationToken).ConfigureAwait(false); + await this.PrepareSessionAndMessagesAsync(session, inputMessages, options, cancellationToken).ConfigureAwait(false); var chatClient = this.ChatClient; @@ -442,16 +442,16 @@ public sealed partial class ChatClientAgent : AIAgent } catch (Exception ex) { - await NotifyChatHistoryProviderOfFailureAsync(safeThread, ex, inputMessages, chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); - await NotifyAIContextProviderOfFailureAsync(safeThread, ex, inputMessages, aiContextProviderMessages, cancellationToken).ConfigureAwait(false); + await NotifyChatHistoryProviderOfFailureAsync(safeSession, ex, inputMessages, chatHistoryProviderMessages, aiContextProviderMessages, chatOptions, cancellationToken).ConfigureAwait(false); + await NotifyAIContextProviderOfFailureAsync(safeSession, ex, inputMessages, aiContextProviderMessages, cancellationToken).ConfigureAwait(false); throw; } this._logger.LogAgentChatClientInvokedAgent(nameof(RunAsync), this.Id, loggingAgentName, this._chatClientType, inputMessages.Count); - // We can derive the type of supported thread from whether we have a conversation id, - // so let's update it and set the conversation id for the service thread case. - await this.UpdateThreadWithTypeAndConversationIdAsync(safeThread, chatResponse.ConversationId, cancellationToken).ConfigureAwait(false); + // We can derive the type of supported session from whether we have a conversation id, + // so let's update it and set the conversation id for the service session case. + await this.UpdateSessionWithTypeAndConversationIdAsync(safeSession, chatResponse.ConversationId, cancellationToken).ConfigureAwait(false); // Ensure that the author name is set for each message in the response. foreach (ChatMessage chatResponseMessage in chatResponse.Messages) @@ -459,11 +459,11 @@ public sealed partial class ChatClientAgent : AIAgent chatResponseMessage.AuthorName ??= this.Name; } - // Only notify the thread of new messages if the chatResponse was successful to avoid inconsistent message state in the thread. - await NotifyChatHistoryProviderOfNewMessagesAsync(safeThread, inputMessages, chatHistoryProviderMessages, aiContextProviderMessages, chatResponse.Messages, chatOptions, cancellationToken).ConfigureAwait(false); + // Only notify the session of new messages if the chatResponse was successful to avoid inconsistent message state in the session. + await NotifyChatHistoryProviderOfNewMessagesAsync(safeSession, inputMessages, chatHistoryProviderMessages, aiContextProviderMessages, chatResponse.Messages, chatOptions, cancellationToken).ConfigureAwait(false); // Notify the AIContextProvider of all new messages. - await NotifyAIContextProviderOfSuccessAsync(safeThread, inputMessages, aiContextProviderMessages, chatResponse.Messages, cancellationToken).ConfigureAwait(false); + await NotifyAIContextProviderOfSuccessAsync(safeSession, inputMessages, aiContextProviderMessages, chatResponse.Messages, cancellationToken).ConfigureAwait(false); var agentResponse = agentResponseFactoryFunc(chatResponse); @@ -476,15 +476,15 @@ public sealed partial class ChatClientAgent : AIAgent /// Notify the when an agent run succeeded, if there is an . /// private static async Task NotifyAIContextProviderOfSuccessAsync( - ChatClientAgentThread thread, + ChatClientAgentSession session, IEnumerable inputMessages, IList? aiContextProviderMessages, IEnumerable responseMessages, CancellationToken cancellationToken) { - if (thread.AIContextProvider is not null) + if (session.AIContextProvider is not null) { - await thread.AIContextProvider.InvokedAsync(new(inputMessages, aiContextProviderMessages) { ResponseMessages = responseMessages }, + await session.AIContextProvider.InvokedAsync(new(inputMessages, aiContextProviderMessages) { ResponseMessages = responseMessages }, cancellationToken).ConfigureAwait(false); } } @@ -493,15 +493,15 @@ public sealed partial class ChatClientAgent : AIAgent /// Notify the of any failure during an agent run, if there is an . /// private static async Task NotifyAIContextProviderOfFailureAsync( - ChatClientAgentThread thread, + ChatClientAgentSession session, Exception ex, IEnumerable inputMessages, IList? aiContextProviderMessages, CancellationToken cancellationToken) { - if (thread.AIContextProvider is not null) + if (session.AIContextProvider is not null) { - await thread.AIContextProvider.InvokedAsync(new(inputMessages, aiContextProviderMessages) { InvokeException = ex }, + await session.AIContextProvider.InvokedAsync(new(inputMessages, aiContextProviderMessages) { InvokeException = ex }, cancellationToken).ConfigureAwait(false); } } @@ -659,40 +659,40 @@ public sealed partial class ChatClientAgent : AIAgent } /// - /// Prepares the thread, chat options, and messages for agent execution. + /// Prepares the session, chat options, and messages for agent execution. /// - /// The conversation thread to use or create. + /// The conversation session to use or create. /// The input messages to use. /// Optional parameters for agent invocation. /// The to monitor for cancellation requests. The default is . - /// A tuple containing the thread, chat options, messages and continuation token. + /// A tuple containing the session, chat options, messages and continuation token. private async Task <( - ChatClientAgentThread AgentThread, + ChatClientAgentSession AgentSession, ChatOptions? ChatOptions, List InputMessagesForChatClient, IList? AIContextProviderMessages, IList? ChatHistoryProviderMessages, ChatClientAgentContinuationToken? ContinuationToken - )> PrepareThreadAndMessagesAsync( - AgentThread? thread, + )> PrepareSessionAndMessagesAsync( + AgentSession? session, IEnumerable inputMessages, AgentRunOptions? runOptions, CancellationToken cancellationToken) { (ChatOptions? chatOptions, ChatClientAgentContinuationToken? continuationToken) = this.CreateConfiguredChatOptions(runOptions); - // Supplying a thread for background responses is required to prevent inconsistent experience - // for callers if they forget to provide the thread for initial or follow-up runs. - if (chatOptions?.AllowBackgroundResponses is true && thread is null) + // Supplying a session for background responses is required to prevent inconsistent experience + // for callers if they forget to provide the session for initial or follow-up runs. + if (chatOptions?.AllowBackgroundResponses is true && session is null) { - throw new InvalidOperationException("A thread must be provided when continuing a background response with a continuation token."); + throw new InvalidOperationException("A session must be provided when continuing a background response with a continuation token."); } - thread ??= await this.GetNewThreadAsync(cancellationToken).ConfigureAwait(false); - if (thread is not ChatClientAgentThread typedThread) + session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false); + if (session is not ChatClientAgentSession typedSession) { - throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used."); + throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be used."); } // Supplying messages when continuing a background response is not allowed. @@ -705,12 +705,12 @@ public sealed partial class ChatClientAgent : AIAgent IList? aiContextProviderMessages = null; IList? chatHistoryProviderMessages = null; - // Populate the thread messages only if we are not continuing an existing response as it's not allowed + // Populate the session messages only if we are not continuing an existing response as it's not allowed if (chatOptions?.ContinuationToken is null) { - ChatHistoryProvider? chatHistoryProvider = ResolveChatHistoryProvider(typedThread, chatOptions); + ChatHistoryProvider? chatHistoryProvider = ResolveChatHistoryProvider(typedSession, chatOptions); - // Add any existing messages from the thread to the messages to be sent to the chat client. + // Add any existing messages from the session to the messages to be sent to the chat client. if (chatHistoryProvider is not null) { var invokingContext = new ChatHistoryProvider.InvokingContext(inputMessages); @@ -724,10 +724,10 @@ public sealed partial class ChatClientAgent : AIAgent // If we have an AIContextProvider, we should get context from it, and update our // messages and options with the additional context. - if (typedThread.AIContextProvider is not null) + if (typedSession.AIContextProvider is not null) { var invokingContext = new AIContextProvider.InvokingContext(inputMessages); - var aiContext = await typedThread.AIContextProvider.InvokingAsync(invokingContext, cancellationToken).ConfigureAwait(false); + var aiContext = await typedSession.AIContextProvider.InvokingAsync(invokingContext, cancellationToken).ConfigureAwait(false); if (aiContext.Messages is { Count: > 0 }) { inputMessagesForChatClient.AddRange(aiContext.Messages); @@ -752,55 +752,55 @@ public sealed partial class ChatClientAgent : AIAgent } } - // If a user provided two different thread ids, via the thread object and options, we should throw + // If a user provided two different session ids, via the session object and options, we should throw // since we don't know which one to use. - if (!string.IsNullOrWhiteSpace(typedThread.ConversationId) && !string.IsNullOrWhiteSpace(chatOptions?.ConversationId) && typedThread.ConversationId != chatOptions!.ConversationId) + if (!string.IsNullOrWhiteSpace(typedSession.ConversationId) && !string.IsNullOrWhiteSpace(chatOptions?.ConversationId) && typedSession.ConversationId != chatOptions!.ConversationId) { throw new InvalidOperationException( $""" - The {nameof(chatOptions.ConversationId)} provided via {nameof(this.ChatOptions)} is different to the id of the provided {nameof(AgentThread)}. + The {nameof(chatOptions.ConversationId)} provided via {nameof(this.ChatOptions)} is different to the id of the provided {nameof(AgentSession)}. Only one id can be used for a run. """); } - // Only create or update ChatOptions if we have an id on the thread and we don't have the same one already in ChatOptions. - if (!string.IsNullOrWhiteSpace(typedThread.ConversationId) && typedThread.ConversationId != chatOptions?.ConversationId) + // Only create or update ChatOptions if we have an id on the session and we don't have the same one already in ChatOptions. + if (!string.IsNullOrWhiteSpace(typedSession.ConversationId) && typedSession.ConversationId != chatOptions?.ConversationId) { chatOptions ??= new(); - chatOptions.ConversationId = typedThread.ConversationId; + chatOptions.ConversationId = typedSession.ConversationId; } - return (typedThread, chatOptions, inputMessagesForChatClient, aiContextProviderMessages, chatHistoryProviderMessages, continuationToken); + return (typedSession, chatOptions, inputMessagesForChatClient, aiContextProviderMessages, chatHistoryProviderMessages, continuationToken); } - private async Task UpdateThreadWithTypeAndConversationIdAsync(ChatClientAgentThread thread, string? responseConversationId, CancellationToken cancellationToken) + private async Task UpdateSessionWithTypeAndConversationIdAsync(ChatClientAgentSession session, string? responseConversationId, CancellationToken cancellationToken) { - if (string.IsNullOrWhiteSpace(responseConversationId) && !string.IsNullOrWhiteSpace(thread.ConversationId)) + if (string.IsNullOrWhiteSpace(responseConversationId) && !string.IsNullOrWhiteSpace(session.ConversationId)) { - // We were passed an AgentThread that has an id for service managed chat history, but we got no conversation id back from the chat client, - // meaning the service doesn't support service managed chat history, so the thread cannot be used with this service. - throw new InvalidOperationException("Service did not return a valid conversation id when using an AgentThread with service managed chat history."); + // We were passed an AgentSession that has an id for service managed chat history, but we got no conversation id back from the chat client, + // meaning the service doesn't support service managed chat history, so the session cannot be used with this service. + throw new InvalidOperationException("Service did not return a valid conversation id when using an AgentSession with service managed chat history."); } if (!string.IsNullOrWhiteSpace(responseConversationId)) { - // If we got a conversation id back from the chat client, it means that the service supports server side thread storage - // so we should update the thread with the new id. - thread.ConversationId = responseConversationId; + // If we got a conversation id back from the chat client, it means that the service supports server side session storage + // so we should update the session with the new id. + session.ConversationId = responseConversationId; } else { // If the service doesn't use service side chat history storage (i.e. we got no id back from invocation), and - // the thread has no ChatHistoryProvider yet, we should update the thread with the custom ChatHistoryProvider or + // the session has no ChatHistoryProvider yet, we should update the session with the custom ChatHistoryProvider or // default InMemoryChatHistoryProvider so that it has somewhere to store the chat history. - thread.ChatHistoryProvider ??= this._agentOptions?.ChatHistoryProviderFactory is not null + session.ChatHistoryProvider ??= this._agentOptions?.ChatHistoryProviderFactory is not null ? await this._agentOptions.ChatHistoryProviderFactory.Invoke(new() { SerializedState = default, JsonSerializerOptions = null }, cancellationToken).ConfigureAwait(false) : new InMemoryChatHistoryProvider(); } } private static Task NotifyChatHistoryProviderOfFailureAsync( - ChatClientAgentThread thread, + ChatClientAgentSession session, Exception ex, IEnumerable requestMessages, IEnumerable? chatHistoryProviderMessages, @@ -808,7 +808,7 @@ public sealed partial class ChatClientAgent : AIAgent ChatOptions? chatOptions, CancellationToken cancellationToken) { - ChatHistoryProvider? provider = ResolveChatHistoryProvider(thread, chatOptions); + ChatHistoryProvider? provider = ResolveChatHistoryProvider(session, chatOptions); // Only notify the provider if we have one. // If we don't have one, it means that the chat history is service managed and the underlying service is responsible for storing messages. @@ -827,7 +827,7 @@ public sealed partial class ChatClientAgent : AIAgent } private static Task NotifyChatHistoryProviderOfNewMessagesAsync( - ChatClientAgentThread thread, + ChatClientAgentSession session, IEnumerable requestMessages, IEnumerable? chatHistoryProviderMessages, IEnumerable? aiContextProviderMessages, @@ -835,7 +835,7 @@ public sealed partial class ChatClientAgent : AIAgent ChatOptions? chatOptions, CancellationToken cancellationToken) { - ChatHistoryProvider? provider = ResolveChatHistoryProvider(thread, chatOptions); + ChatHistoryProvider? provider = ResolveChatHistoryProvider(session, chatOptions); // Only notify the provider if we have one. // If we don't have one, it means that the chat history is service managed and the underlying service is responsible for storing messages. @@ -852,11 +852,11 @@ public sealed partial class ChatClientAgent : AIAgent return Task.CompletedTask; } - private static ChatHistoryProvider? ResolveChatHistoryProvider(ChatClientAgentThread thread, ChatOptions? chatOptions) + private static ChatHistoryProvider? ResolveChatHistoryProvider(ChatClientAgentSession session, ChatOptions? chatOptions) { - ChatHistoryProvider? provider = thread.ChatHistoryProvider; + ChatHistoryProvider? provider = session.ChatHistoryProvider; - // If someone provided an override ChatHistoryProvider via AdditionalProperties, we should use that instead of the one on the thread. + // If someone provided an override ChatHistoryProvider via AdditionalProperties, we should use that instead of the one on the session. if (chatOptions?.AdditionalProperties?.TryGetValue(out ChatHistoryProvider? overrideProvider) is true) { provider = overrideProvider; @@ -874,7 +874,7 @@ public sealed partial class ChatClientAgent : AIAgent return new(continuationToken) { - // Save input messages to the continuation token so they can be added to the thread and + // Save input messages to the continuation token so they can be added to the session and // provided to the context provider in the last successful streaming resumption run. // That's necessary for scenarios where initial streaming run is interrupted and streaming is resumed later. InputMessages = inputMessages?.Any() is true ? inputMessages : null, diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentCustomOptions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentCustomOptions.cs index c5502ad916..25ae5a903e 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentCustomOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentCustomOptions.cs @@ -14,151 +14,151 @@ namespace Microsoft.Agents.AI; public partial class ChatClientAgent { /// - /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread. + /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the session. /// - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with any response messages generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task RunAsync( - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunAsync(thread, (AgentRunOptions?)options, cancellationToken); + this.RunAsync(session, (AgentRunOptions?)options, cancellationToken); /// /// Runs the agent with a text message from the user. /// /// The user message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task RunAsync( string message, - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunAsync(message, thread, (AgentRunOptions?)options, cancellationToken); + this.RunAsync(message, session, (AgentRunOptions?)options, cancellationToken); /// /// Runs the agent with a single chat message. /// /// The chat message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task RunAsync( ChatMessage message, - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunAsync(message, thread, (AgentRunOptions?)options, cancellationToken); + this.RunAsync(message, session, (AgentRunOptions?)options, cancellationToken); /// /// Runs the agent with a collection of chat messages. /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response messages generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task RunAsync( IEnumerable messages, - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunAsync(messages, thread, (AgentRunOptions?)options, cancellationToken); + this.RunAsync(messages, session, (AgentRunOptions?)options, cancellationToken); /// /// Runs the agent in streaming mode without providing new input messages, relying on existing context and instructions. /// - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with any response messages generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// An asynchronous enumerable of instances representing the streaming response. public IAsyncEnumerable RunStreamingAsync( - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunStreamingAsync(thread, (AgentRunOptions?)options, cancellationToken); + this.RunStreamingAsync(session, (AgentRunOptions?)options, cancellationToken); /// /// Runs the agent in streaming mode with a text message from the user. /// /// The user message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// An asynchronous enumerable of instances representing the streaming response. public IAsyncEnumerable RunStreamingAsync( string message, - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunStreamingAsync(message, thread, (AgentRunOptions?)options, cancellationToken); + this.RunStreamingAsync(message, session, (AgentRunOptions?)options, cancellationToken); /// /// Runs the agent in streaming mode with a single chat message. /// /// The chat message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// An asynchronous enumerable of instances representing the streaming response. public IAsyncEnumerable RunStreamingAsync( ChatMessage message, - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunStreamingAsync(message, thread, (AgentRunOptions?)options, cancellationToken); + this.RunStreamingAsync(message, session, (AgentRunOptions?)options, cancellationToken); /// /// Runs the agent in streaming mode with a collection of chat messages. /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response updates generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response updates generated during invocation. /// /// Configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . /// An asynchronous enumerable of instances representing the streaming response. public IAsyncEnumerable RunStreamingAsync( IEnumerable messages, - AgentThread? thread, + AgentSession? session, ChatClientAgentRunOptions? options, CancellationToken cancellationToken = default) => - this.RunStreamingAsync(messages, thread, (AgentRunOptions?)options, cancellationToken); + this.RunStreamingAsync(messages, session, (AgentRunOptions?)options, cancellationToken); /// - /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread, and requesting a response of the specified type . + /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the session, and requesting a response of the specified type . /// - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with any response messages generated during invocation. /// /// The JSON serialization options to use. /// Configuration parameters for controlling the agent's invocation behavior. @@ -169,20 +169,20 @@ public partial class ChatClientAgent /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task> RunAsync( - AgentThread? thread, + AgentSession? session, JsonSerializerOptions? serializerOptions, ChatClientAgentRunOptions? options, bool? useJsonSchemaResponseFormat = null, CancellationToken cancellationToken = default) => - this.RunAsync(thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); + this.RunAsync(session, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); /// /// Runs the agent with a text message from the user, requesting a response of the specified type . /// /// The user message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// The JSON serialization options to use. /// Configuration parameters for controlling the agent's invocation behavior. @@ -194,20 +194,20 @@ public partial class ChatClientAgent /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task> RunAsync( string message, - AgentThread? thread, + AgentSession? session, JsonSerializerOptions? serializerOptions, ChatClientAgentRunOptions? options, bool? useJsonSchemaResponseFormat = null, CancellationToken cancellationToken = default) => - this.RunAsync(message, thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); + this.RunAsync(message, session, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); /// /// Runs the agent with a single chat message, requesting a response of the specified type . /// /// The chat message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// The JSON serialization options to use. /// Configuration parameters for controlling the agent's invocation behavior. @@ -219,20 +219,20 @@ public partial class ChatClientAgent /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task> RunAsync( ChatMessage message, - AgentThread? thread, + AgentSession? session, JsonSerializerOptions? serializerOptions, ChatClientAgentRunOptions? options, bool? useJsonSchemaResponseFormat = null, CancellationToken cancellationToken = default) => - this.RunAsync(message, thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); + this.RunAsync(message, session, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); /// /// Runs the agent with a collection of chat messages, requesting a response of the specified type . /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response messages generated during invocation. /// /// The JSON serialization options to use. /// Configuration parameters for controlling the agent's invocation behavior. @@ -244,10 +244,10 @@ public partial class ChatClientAgent /// A task that represents the asynchronous operation. The task result contains an with the agent's output. public Task> RunAsync( IEnumerable messages, - AgentThread? thread, + AgentSession? session, JsonSerializerOptions? serializerOptions, ChatClientAgentRunOptions? options, bool? useJsonSchemaResponseFormat = null, CancellationToken cancellationToken = default) => - this.RunAsync(messages, thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); + this.RunAsync(messages, session, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken); } diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentThread.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentSession.cs similarity index 84% rename from dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentThread.cs rename to dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentSession.cs index a604a0d085..e4b28caf30 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentSession.cs @@ -13,14 +13,14 @@ namespace Microsoft.Agents.AI; /// Provides a thread implementation for use with . /// [DebuggerDisplay("{DebuggerDisplay,nq}")] -public sealed class ChatClientAgentThread : AgentThread +public sealed class ChatClientAgentSession : AgentSession { private ChatHistoryProvider? _chatHistoryProvider; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - internal ChatClientAgentThread() + internal ChatClientAgentSession() { } @@ -59,8 +59,8 @@ public sealed class ChatClientAgentThread : AgentThread if (this._chatHistoryProvider is not null) { - // If we have a ChatHistoryProvider already, we shouldn't switch the thread to use a conversation id - // since it means that the thread contents will essentially be deleted, and the thread will not work + // If we have a ChatHistoryProvider already, we shouldn't switch the session to use a conversation id + // since it means that the session contents will essentially be deleted, and the session will not work // with the original agent anymore. throw new InvalidOperationException("Only the ConversationId or ChatHistoryProvider may be set, but not both and switching from one to another is not supported."); } @@ -98,8 +98,8 @@ public sealed class ChatClientAgentThread : AgentThread if (!string.IsNullOrWhiteSpace(this.ConversationId)) { - // If we have a conversation id already, we shouldn't switch the thread to use a ChatHistoryProvider - // since it means that the thread will not work with the original agent anymore. + // If we have a conversation id already, we shouldn't switch the session to use a ChatHistoryProvider + // since it means that the session will not work with the original agent anymore. throw new InvalidOperationException("Only the ConversationId or ChatHistoryProvider may be set, but not both and switching from one to another is not supported."); } @@ -113,9 +113,9 @@ public sealed class ChatClientAgentThread : AgentThread public AIContextProvider? AIContextProvider { get; internal set; } /// - /// Creates a new instance of the class from previously serialized state. + /// Creates a new instance of the class from previously serialized state. /// - /// A representing the serialized state of the thread. + /// A representing the serialized state of the session. /// Optional settings for customizing the JSON deserialization process. /// /// An optional factory function to create a custom from its serialized state. @@ -126,42 +126,42 @@ public sealed class ChatClientAgentThread : AgentThread /// If not provided, no context provider will be configured. /// /// The to monitor for cancellation requests. - /// A task representing the asynchronous operation. The task result contains the deserialized . - internal static async Task DeserializeAsync( - JsonElement serializedThreadState, + /// A task representing the asynchronous operation. The task result contains the deserialized . + internal static async Task DeserializeAsync( + JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null, Func>? chatHistoryProviderFactory = null, Func>? aiContextProviderFactory = null, CancellationToken cancellationToken = default) { - if (serializedThreadState.ValueKind != JsonValueKind.Object) + if (serializedSessionState.ValueKind != JsonValueKind.Object) { - throw new ArgumentException("The serialized thread state must be a JSON object.", nameof(serializedThreadState)); + throw new ArgumentException("The serialized session state must be a JSON object.", nameof(serializedSessionState)); } - var state = serializedThreadState.Deserialize( - AgentJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ThreadState))) as ThreadState; + var state = serializedSessionState.Deserialize( + AgentJsonUtilities.DefaultOptions.GetTypeInfo(typeof(SessionState))) as SessionState; - var thread = new ChatClientAgentThread(); + var session = new ChatClientAgentSession(); - thread.AIContextProvider = aiContextProviderFactory is not null + session.AIContextProvider = aiContextProviderFactory is not null ? await aiContextProviderFactory.Invoke(state?.AIContextProviderState ?? default, jsonSerializerOptions, cancellationToken).ConfigureAwait(false) : null; - if (state?.ConversationId is string threadId) + if (state?.ConversationId is string sessionId) { - thread.ConversationId = threadId; + session.ConversationId = sessionId; // Since we have an ID, we should not have a ChatHistoryProvider and we can return here. - return thread; + return session; } - thread._chatHistoryProvider = + session._chatHistoryProvider = chatHistoryProviderFactory is not null ? await chatHistoryProviderFactory.Invoke(state?.ChatHistoryProviderState ?? default, jsonSerializerOptions, cancellationToken).ConfigureAwait(false) : new InMemoryChatHistoryProvider(state?.ChatHistoryProviderState ?? default, jsonSerializerOptions); // default to an in-memory ChatHistoryProvider - return thread; + return session; } /// @@ -171,14 +171,14 @@ public sealed class ChatClientAgentThread : AgentThread JsonElement? aiContextProviderState = this.AIContextProvider?.Serialize(jsonSerializerOptions); - var state = new ThreadState + var state = new SessionState { ConversationId = this.ConversationId, ChatHistoryProviderState = chatHistoryProviderState is { ValueKind: not JsonValueKind.Undefined } ? chatHistoryProviderState : null, AIContextProviderState = aiContextProviderState is { ValueKind: not JsonValueKind.Undefined } ? aiContextProviderState : null, }; - return JsonSerializer.SerializeToElement(state, AgentJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ThreadState))); + return JsonSerializer.SerializeToElement(state, AgentJsonUtilities.DefaultOptions.GetTypeInfo(typeof(SessionState))); } /// @@ -194,7 +194,7 @@ public sealed class ChatClientAgentThread : AgentThread this._chatHistoryProvider is { } chatHistoryProvider ? $"ChatHistoryProvider = {chatHistoryProvider.GetType().Name}" : "Count = 0"; - internal sealed class ThreadState + internal sealed class SessionState { public string? ConversationId { get; set; } diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentStructuredOutput.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentStructuredOutput.cs index 6bd62e85a2..d933939884 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentStructuredOutput.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentStructuredOutput.cs @@ -16,11 +16,11 @@ namespace Microsoft.Agents.AI; public sealed partial class ChatClientAgent { /// - /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread, and requesting a response of the specified type . + /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the session, and requesting a response of the specified type . /// - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with any response messages generated during invocation. /// /// The JSON serialization options to use. /// Optional configuration parameters for controlling the agent's invocation behavior. @@ -31,24 +31,24 @@ public sealed partial class ChatClientAgent /// The to monitor for cancellation requests. The default is . /// A task that represents the asynchronous operation. The task result contains an with the agent's output. /// - /// This overload is useful when the agent has sufficient context from previous messages in the thread + /// This overload is useful when the agent has sufficient context from previous messages in the session /// or from its initial configuration to generate a meaningful response without additional input. /// public Task> RunAsync( - AgentThread? thread = null, + AgentSession? session = null, JsonSerializerOptions? serializerOptions = null, AgentRunOptions? options = null, bool? useJsonSchemaResponseFormat = null, CancellationToken cancellationToken = default) => - this.RunAsync([], thread, serializerOptions, options, useJsonSchemaResponseFormat, cancellationToken); + this.RunAsync([], session, serializerOptions, options, useJsonSchemaResponseFormat, cancellationToken); /// /// Runs the agent with a text message from the user, requesting a response of the specified type . /// /// The user message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// The JSON serialization options to use. /// Optional configuration parameters for controlling the agent's invocation behavior. @@ -65,7 +65,7 @@ public sealed partial class ChatClientAgent /// public Task> RunAsync( string message, - AgentThread? thread = null, + AgentSession? session = null, JsonSerializerOptions? serializerOptions = null, AgentRunOptions? options = null, bool? useJsonSchemaResponseFormat = null, @@ -73,16 +73,16 @@ public sealed partial class ChatClientAgent { _ = Throw.IfNullOrWhitespace(message); - return this.RunAsync(new ChatMessage(ChatRole.User, message), thread, serializerOptions, options, useJsonSchemaResponseFormat, cancellationToken); + return this.RunAsync(new ChatMessage(ChatRole.User, message), session, serializerOptions, options, useJsonSchemaResponseFormat, cancellationToken); } /// /// Runs the agent with a single chat message, requesting a response of the specified type . /// /// The chat message to send to the agent. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input message and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input message and any response messages generated during invocation. /// /// The JSON serialization options to use. /// Optional configuration parameters for controlling the agent's invocation behavior. @@ -95,7 +95,7 @@ public sealed partial class ChatClientAgent /// is . public Task> RunAsync( ChatMessage message, - AgentThread? thread = null, + AgentSession? session = null, JsonSerializerOptions? serializerOptions = null, AgentRunOptions? options = null, bool? useJsonSchemaResponseFormat = null, @@ -103,16 +103,16 @@ public sealed partial class ChatClientAgent { _ = Throw.IfNull(message); - return this.RunAsync([message], thread, serializerOptions, options, useJsonSchemaResponseFormat, cancellationToken); + return this.RunAsync([message], session, serializerOptions, options, useJsonSchemaResponseFormat, cancellationToken); } /// /// Runs the agent with a collection of chat messages, requesting a response of the specified type . /// /// The collection of messages to send to the agent for processing. - /// - /// The conversation thread to use for this invocation. If , a new thread will be created. - /// The thread will be updated with the input messages and any response messages generated during invocation. + /// + /// The conversation session to use for this invocation. If , a new session will be created. + /// The session will be updated with the input messages and any response messages generated during invocation. /// /// The JSON serialization options to use. /// Optional configuration parameters for controlling the agent's invocation behavior. @@ -131,12 +131,12 @@ public sealed partial class ChatClientAgent /// /// /// The messages are processed in the order provided and become part of the conversation history. - /// The agent's response will also be added to if one is provided. + /// The agent's response will also be added to if one is provided. /// /// public Task> RunAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, JsonSerializerOptions? serializerOptions = null, AgentRunOptions? options = null, bool? useJsonSchemaResponseFormat = null, @@ -160,6 +160,6 @@ public sealed partial class ChatClientAgent }; } - return this.RunCoreAsync(GetResponseAsync, CreateResponse, messages, thread, options, cancellationToken); + return this.RunCoreAsync(GetResponseAsync, CreateResponse, messages, session, options, cancellationToken); } } diff --git a/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs b/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs index 0604ef17d8..bf34dfdeed 100644 --- a/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs @@ -21,11 +21,11 @@ internal sealed class FunctionInvocationDelegatingAgent : DelegatingAIAgent this._delegateFunc = delegateFunc; } - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => this.InnerAgent.RunAsync(messages, thread, this.AgentRunOptionsWithFunctionMiddleware(options), cancellationToken); + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + => this.InnerAgent.RunAsync(messages, session, this.AgentRunOptionsWithFunctionMiddleware(options), cancellationToken); - protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => this.InnerAgent.RunStreamingAsync(messages, thread, this.AgentRunOptionsWithFunctionMiddleware(options), cancellationToken); + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + => this.InnerAgent.RunStreamingAsync(messages, session, this.AgentRunOptionsWithFunctionMiddleware(options), cancellationToken); // Decorate options to add the middleware function private AgentRunOptions? AgentRunOptionsWithFunctionMiddleware(AgentRunOptions? options) diff --git a/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs b/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs index 258ea55ed7..8bb50344cf 100644 --- a/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs @@ -56,7 +56,7 @@ public sealed partial class LoggingAgent : DelegatingAIAgent /// protected override async Task RunCoreAsync( - IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { if (this._logger.IsEnabled(LogLevel.Debug)) { @@ -72,7 +72,7 @@ public sealed partial class LoggingAgent : DelegatingAIAgent try { - AgentResponse response = await base.RunCoreAsync(messages, thread, options, cancellationToken).ConfigureAwait(false); + AgentResponse response = await base.RunCoreAsync(messages, session, options, cancellationToken).ConfigureAwait(false); if (this._logger.IsEnabled(LogLevel.Debug)) { @@ -102,7 +102,7 @@ public sealed partial class LoggingAgent : DelegatingAIAgent /// protected override async IAsyncEnumerable RunCoreStreamingAsync( - IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (this._logger.IsEnabled(LogLevel.Debug)) { @@ -119,7 +119,7 @@ public sealed partial class LoggingAgent : DelegatingAIAgent IAsyncEnumerator e; try { - e = base.RunCoreStreamingAsync(messages, thread, options, cancellationToken).GetAsyncEnumerator(cancellationToken); + e = base.RunCoreStreamingAsync(messages, session, options, cancellationToken).GetAsyncEnumerator(cancellationToken); } catch (OperationCanceledException) { diff --git a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs index 6384d36d9f..87adc9fd7a 100644 --- a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs @@ -164,7 +164,7 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable new VectorStoreDataProperty("ApplicationId", typeof(string)) { IsIndexed = true }, new VectorStoreDataProperty("AgentId", typeof(string)) { IsIndexed = true }, new VectorStoreDataProperty("UserId", typeof(string)) { IsIndexed = true }, - new VectorStoreDataProperty("ThreadId", typeof(string)) { IsIndexed = true }, + new VectorStoreDataProperty("SessionId", typeof(string)) { IsIndexed = true }, new VectorStoreDataProperty("Content", typeof(string)) { IsFullTextIndexed = true }, new VectorStoreDataProperty("CreatedAt", typeof(string)) { IsIndexed = true }, new VectorStoreVectorProperty("ContentEmbedding", typeof(string), Throw.IfLessThan(vectorDimensions, 1)) @@ -216,10 +216,10 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable { this._logger.LogError( ex, - "ChatHistoryMemoryProvider: Failed to search for chat history due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + "ChatHistoryMemoryProvider: Failed to search for chat history due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', SessionId: '{SessionId}', UserId: '{UserId}'.", this._searchScope.ApplicationId, this._searchScope.AgentId, - this._searchScope.ThreadId, + this._searchScope.SessionId, this.SanitizeLogData(this._searchScope.UserId)); } @@ -254,7 +254,7 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable ["ApplicationId"] = this._storageScope?.ApplicationId, ["AgentId"] = this._storageScope?.AgentId, ["UserId"] = this._storageScope?.UserId, - ["ThreadId"] = this._storageScope?.ThreadId, + ["SessionId"] = this._storageScope?.SessionId, ["Content"] = message.Text, ["CreatedAt"] = message.CreatedAt?.ToString("O") ?? DateTimeOffset.UtcNow.ToString("O"), ["ContentEmbedding"] = message.Text, @@ -272,10 +272,10 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable { this._logger.LogError( ex, - "ChatHistoryMemoryProvider: Failed to add messages to chat history vector store due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + "ChatHistoryMemoryProvider: Failed to add messages to chat history vector store due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', SessionId: '{SessionId}', UserId: '{UserId}'.", this._searchScope.ApplicationId, this._searchScope.AgentId, - this._searchScope.ThreadId, + this._searchScope.SessionId, this.SanitizeLogData(this._searchScope.UserId)); } } @@ -312,12 +312,12 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable if (this._logger?.IsEnabled(LogLevel.Trace) is true) { this._logger.LogTrace( - "ChatHistoryMemoryProvider: Search Results\nInput:{Input}\nOutput:{MessageText}\n ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + "ChatHistoryMemoryProvider: Search Results\nInput:{Input}\nOutput:{MessageText}\n ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', SessionId: '{SessionId}', UserId: '{UserId}'.", this.SanitizeLogData(userQuestion), this.SanitizeLogData(formatted), this._searchScope.ApplicationId, this._searchScope.AgentId, - this._searchScope.ThreadId, + this._searchScope.SessionId, this.SanitizeLogData(this._searchScope.UserId)); } @@ -346,7 +346,7 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable string? applicationId = this._searchScope.ApplicationId; string? agentId = this._searchScope.AgentId; string? userId = this._searchScope.UserId; - string? threadId = this._searchScope.ThreadId; + string? sessionId = this._searchScope.SessionId; Expression, bool>>? filter = null; if (applicationId != null) @@ -370,11 +370,11 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable filter.Parameters); } - if (threadId != null) + if (sessionId != null) { - Expression, bool>> threadIdFilter = x => (string?)x["ThreadId"] == threadId; - filter = filter == null ? threadIdFilter : Expression.Lambda, bool>>( - Expression.AndAlso(filter.Body, threadIdFilter.Body), + Expression, bool>> sessionIdFilter = x => (string?)x["SessionId"] == sessionId; + filter = filter == null ? sessionIdFilter : Expression.Lambda, bool>>( + Expression.AndAlso(filter.Body, sessionIdFilter.Body), filter.Parameters); } @@ -397,11 +397,11 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable if (this._logger?.IsEnabled(LogLevel.Information) is true) { this._logger.LogInformation( - "ChatHistoryMemoryProvider: Retrieved {Count} search results. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + "ChatHistoryMemoryProvider: Retrieved {Count} search results. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', SessionId: '{SessionId}', UserId: '{UserId}'.", results.Count, this._searchScope.ApplicationId, this._searchScope.AgentId, - this._searchScope.ThreadId, + this._searchScope.SessionId, this.SanitizeLogData(this._searchScope.UserId)); } diff --git a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderScope.cs b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderScope.cs index 2715ed2e20..fa1ab58b39 100644 --- a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderScope.cs +++ b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderScope.cs @@ -24,7 +24,7 @@ public sealed class ChatHistoryMemoryProviderScope this.ApplicationId = sourceScope.ApplicationId; this.AgentId = sourceScope.AgentId; - this.ThreadId = sourceScope.ThreadId; + this.SessionId = sourceScope.SessionId; this.UserId = sourceScope.UserId; } @@ -41,9 +41,9 @@ public sealed class ChatHistoryMemoryProviderScope public string? AgentId { get; set; } /// - /// Gets or sets an optional ID for the thread to scope chat history to. + /// Gets or sets an optional ID for the session to scope chat history to. /// - public string? ThreadId { get; set; } + public string? SessionId { get; set; } /// /// Gets or sets an optional ID for the user to scope chat history to. diff --git a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs index 07dadf4e0b..7ec8a53161 100644 --- a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs @@ -79,9 +79,9 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable /// protected override async Task RunCoreAsync( - IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - ChatOptions co = new ForwardedOptions(options, thread, Activity.Current); + ChatOptions co = new ForwardedOptions(options, session, Activity.Current); var response = await this._otelClient.GetResponseAsync(messages, co, cancellationToken).ConfigureAwait(false); @@ -90,9 +90,9 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable /// protected override async IAsyncEnumerable RunCoreStreamingAsync( - IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - ChatOptions co = new ForwardedOptions(options, thread, Activity.Current); + ChatOptions co = new ForwardedOptions(options, session, Activity.Current); await foreach (var update in this._otelClient.GetStreamingResponseAsync(messages, co, cancellationToken).ConfigureAwait(false)) { @@ -142,17 +142,17 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable /// State passed from this instance into the inner agent, circumventing the intermediate . private sealed class ForwardedOptions : ChatOptions { - public ForwardedOptions(AgentRunOptions? options, AgentThread? thread, Activity? currentActivity) : + public ForwardedOptions(AgentRunOptions? options, AgentSession? session, Activity? currentActivity) : base((options as ChatClientAgentRunOptions)?.ChatOptions) { this.Options = options; - this.Thread = thread; + this.Session = session; this.CurrentActivity = currentActivity; } public AgentRunOptions? Options { get; } - public AgentThread? Thread { get; } + public AgentSession? Session { get; } public Activity? CurrentActivity { get; } } @@ -170,7 +170,7 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable parentAgent.UpdateCurrentActivity(fo?.CurrentActivity); // Invoke the inner agent. - var response = await parentAgent.InnerAgent.RunAsync(messages, fo?.Thread, fo?.Options, cancellationToken).ConfigureAwait(false); + var response = await parentAgent.InnerAgent.RunAsync(messages, fo?.Session, fo?.Options, cancellationToken).ConfigureAwait(false); // Wrap the response in a ChatResponse so we can pass it back through OpenTelemetryChatClient. return response.AsChatResponse(); @@ -185,7 +185,7 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable parentAgent.UpdateCurrentActivity(fo?.CurrentActivity); // Invoke the inner agent. - await foreach (var update in parentAgent.InnerAgent.RunStreamingAsync(messages, fo?.Thread, fo?.Options, cancellationToken).ConfigureAwait(false)) + await foreach (var update in parentAgent.InnerAgent.RunStreamingAsync(messages, fo?.Session, fo?.Options, cancellationToken).ConfigureAwait(false)) { // Wrap the response updates in ChatResponseUpdates so we can pass them back through OpenTelemetryChatClient. yield return update.AsChatResponseUpdate(); diff --git a/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunStreamingTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunStreamingTests.cs index 2d8d6787aa..a32fa3e0ea 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunStreamingTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunStreamingTests.cs @@ -22,12 +22,12 @@ public abstract class ChatClientAgentRunStreamingTests(Func x.Text)); @@ -53,14 +53,14 @@ public abstract class ChatClientAgentRunStreamingTests(Func x.Text)); diff --git a/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunTests.cs index 80fd7106ac..be51b9683d 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunTests.cs @@ -21,12 +21,12 @@ public abstract class ChatClientAgentRunTests(Func { // Arrange var agent = await this.Fixture.CreateChatClientAgentAsync(instructions: "ALWAYS RESPOND WITH 'Computer says no', even if there was no user input."); - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); await using var agentCleanup = new AgentCleanup(agent, this.Fixture); - await using var threadCleanup = new ThreadCleanup(thread, this.Fixture); + await using var sessionCleanup = new SessionCleanup(session, this.Fixture); // Act - var response = await agent.RunAsync(thread); + var response = await agent.RunAsync(session); // Assert Assert.NotNull(response); @@ -53,14 +53,14 @@ public abstract class ChatClientAgentRunTests(Func AIFunctionFactory.Create(MenuPlugin.GetSpecials), AIFunctionFactory.Create(MenuPlugin.GetItemPrice) ]); - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); foreach (var questionAndAnswer in questionsAndAnswers) { // Act var result = await agent.RunAsync( new ChatMessage(ChatRole.User, questionAndAnswer.Question), - thread); + session); // Assert Assert.NotNull(result); diff --git a/dotnet/tests/AgentConformance.IntegrationTests/IAgentFixture.cs b/dotnet/tests/AgentConformance.IntegrationTests/IAgentFixture.cs index 7e1a637b1f..96b40d561b 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/IAgentFixture.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/IAgentFixture.cs @@ -15,7 +15,7 @@ public interface IAgentFixture : IAsyncLifetime { AIAgent Agent { get; } - Task> GetChatHistoryAsync(AgentThread thread); + Task> GetChatHistoryAsync(AgentSession session); - Task DeleteThreadAsync(AgentThread thread); + Task DeleteSessionAsync(AgentSession session); } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs index d5c85b1866..2290e20cb4 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs @@ -24,11 +24,11 @@ public abstract class RunStreamingTests(Func creat { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act - var chatResponses = await agent.RunStreamingAsync(thread, await this.AgentRunOptionsFactory.Invoke()).ToListAsync(); + var chatResponses = await agent.RunStreamingAsync(session, await this.AgentRunOptionsFactory.Invoke()).ToListAsync(); } [RetryFact(Constants.RetryCount, Constants.RetryDelay)] @@ -36,11 +36,11 @@ public abstract class RunStreamingTests(Func creat { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act - var responseUpdates = await agent.RunStreamingAsync("What is the capital of France.", thread, await this.AgentRunOptionsFactory.Invoke()).ToListAsync(); + var responseUpdates = await agent.RunStreamingAsync("What is the capital of France.", session, await this.AgentRunOptionsFactory.Invoke()).ToListAsync(); // Assert var chatResponseText = string.Concat(responseUpdates.Select(x => x.Text)); @@ -52,11 +52,11 @@ public abstract class RunStreamingTests(Func creat { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act - var responseUpdates = await agent.RunStreamingAsync(new ChatMessage(ChatRole.User, "What is the capital of France."), thread, await this.AgentRunOptionsFactory.Invoke()).ToListAsync(); + var responseUpdates = await agent.RunStreamingAsync(new ChatMessage(ChatRole.User, "What is the capital of France."), session, await this.AgentRunOptionsFactory.Invoke()).ToListAsync(); // Assert var chatResponseText = string.Concat(responseUpdates.Select(x => x.Text)); @@ -68,8 +68,8 @@ public abstract class RunStreamingTests(Func creat { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act var responseUpdates = await agent.RunStreamingAsync( @@ -77,7 +77,7 @@ public abstract class RunStreamingTests(Func creat new ChatMessage(ChatRole.User, "Hello."), new ChatMessage(ChatRole.User, "What is the capital of France.") ], - thread, + session, await this.AgentRunOptionsFactory.Invoke()).ToListAsync(); // Assert @@ -86,19 +86,19 @@ public abstract class RunStreamingTests(Func creat } [RetryFact(Constants.RetryCount, Constants.RetryDelay)] - public virtual async Task ThreadMaintainsHistoryAsync() + public virtual async Task SessionMaintainsHistoryAsync() { // Arrange const string Q1 = "What is the capital of France."; const string Q2 = "And Austria?"; var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act var options = await this.AgentRunOptionsFactory.Invoke(); - var responseUpdates1 = await agent.RunStreamingAsync(Q1, thread, options).ToListAsync(); - var responseUpdates2 = await agent.RunStreamingAsync(Q2, thread, options).ToListAsync(); + var responseUpdates1 = await agent.RunStreamingAsync(Q1, session, options).ToListAsync(); + var responseUpdates2 = await agent.RunStreamingAsync(Q2, session, options).ToListAsync(); // Assert var response1Text = string.Concat(responseUpdates1.Select(x => x.Text)); @@ -106,7 +106,7 @@ public abstract class RunStreamingTests(Func creat Assert.Contains("Paris", response1Text); Assert.Contains("Vienna", response2Text); - var chatHistory = await this.Fixture.GetChatHistoryAsync(thread); + var chatHistory = await this.Fixture.GetChatHistoryAsync(session); Assert.Equal(4, chatHistory.Count); Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.User)); Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.Assistant)); diff --git a/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs index be98bbd2bf..be1fa0f2f3 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs @@ -24,11 +24,11 @@ public abstract class RunTests(Func createAgentFix { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act - var chatResponse = await agent.RunAsync(thread); + var chatResponse = await agent.RunAsync(session); // Assert Assert.NotNull(chatResponse); @@ -39,11 +39,11 @@ public abstract class RunTests(Func createAgentFix { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act - var response = await agent.RunAsync("What is the capital of France.", thread, await this.AgentRunOptionsFactory.Invoke()); + var response = await agent.RunAsync("What is the capital of France.", session, await this.AgentRunOptionsFactory.Invoke()); // Assert Assert.NotNull(response); @@ -57,11 +57,11 @@ public abstract class RunTests(Func createAgentFix { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act - var response = await agent.RunAsync(new ChatMessage(ChatRole.User, "What is the capital of France."), thread, await this.AgentRunOptionsFactory.Invoke()); + var response = await agent.RunAsync(new ChatMessage(ChatRole.User, "What is the capital of France."), session, await this.AgentRunOptionsFactory.Invoke()); // Assert Assert.NotNull(response); @@ -74,8 +74,8 @@ public abstract class RunTests(Func createAgentFix { // Arrange var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act var response = await agent.RunAsync( @@ -83,7 +83,7 @@ public abstract class RunTests(Func createAgentFix new ChatMessage(ChatRole.User, "Hello."), new ChatMessage(ChatRole.User, "What is the capital of France.") ], - thread, + session, await this.AgentRunOptionsFactory.Invoke()); // Assert @@ -93,25 +93,25 @@ public abstract class RunTests(Func createAgentFix } [RetryFact(Constants.RetryCount, Constants.RetryDelay)] - public virtual async Task ThreadMaintainsHistoryAsync() + public virtual async Task SessionMaintainsHistoryAsync() { // Arrange const string Q1 = "What is the capital of France."; const string Q2 = "And Austria?"; var agent = this.Fixture.Agent; - var thread = await agent.GetNewThreadAsync(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); + var session = await agent.GetNewSessionAsync(); + await using var cleanup = new SessionCleanup(session, this.Fixture); // Act var options = await this.AgentRunOptionsFactory.Invoke(); - var result1 = await agent.RunAsync(Q1, thread, options); - var result2 = await agent.RunAsync(Q2, thread, options); + var result1 = await agent.RunAsync(Q1, session, options); + var result2 = await agent.RunAsync(Q2, session, options); // Assert Assert.Contains("Paris", result1.Text); Assert.Contains("Vienna", result2.Text); - var chatHistory = await this.Fixture.GetChatHistoryAsync(thread); + var chatHistory = await this.Fixture.GetChatHistoryAsync(session); Assert.Equal(4, chatHistory.Count); Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.User)); Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.Assistant)); diff --git a/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs b/dotnet/tests/AgentConformance.IntegrationTests/Support/SessionCleanup.cs similarity index 57% rename from dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs rename to dotnet/tests/AgentConformance.IntegrationTests/Support/SessionCleanup.cs index f7443f73ca..c59b999fd2 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/Support/SessionCleanup.cs @@ -7,12 +7,12 @@ using Microsoft.Agents.AI; namespace AgentConformance.IntegrationTests.Support; /// -/// Helper class to delete threads after tests. +/// Helper class to delete sessions after tests. /// -/// The thread to delete. +/// The session to delete. /// The fixture that provides agent specific capabilities. -internal sealed class ThreadCleanup(AgentThread thread, IAgentFixture fixture) : IAsyncDisposable +internal sealed class SessionCleanup(AgentSession session, IAgentFixture fixture) : IAsyncDisposable { public async ValueTask DisposeAsync() => - await fixture.DeleteThreadAsync(thread); + await fixture.DeleteSessionAsync(session); } diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs index e9a3cba95e..555576df9d 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs @@ -35,16 +35,16 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture public IChatClient ChatClient => this._agent.ChatClient; - public async Task> GetChatHistoryAsync(AgentThread thread) + public async Task> GetChatHistoryAsync(AgentSession session) { - var typedThread = (ChatClientAgentThread)thread; + var typedSession = (ChatClientAgentSession)session; - if (typedThread.ChatHistoryProvider is null) + if (typedSession.ChatHistoryProvider is null) { return []; } - return (await typedThread.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); + return (await typedSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); } public Task CreateChatClientAgentAsync( @@ -97,8 +97,8 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture // Chat Completion does not require/support deleting agents, so this is a no-op. Task.CompletedTask; - public Task DeleteThreadAsync(AgentThread thread) => - // Chat Completion does not require/support deleting threads, so this is a no-op. + public Task DeleteSessionAsync(AgentSession session) => + // Chat Completion does not require/support deleting sessions, so this is a no-op. Task.CompletedTask; public async Task InitializeAsync() => diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs index f1bbbe47e9..4ed6d39edb 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs @@ -21,7 +21,7 @@ public abstract class SkipAllRunStreaming(Func f public override Task RunWithStringReturnsExpectedResultAsync() => base.RunWithStringReturnsExpectedResultAsync(); [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task ThreadMaintainsHistoryAsync() => base.ThreadMaintainsHistoryAsync(); + public override Task SessionMaintainsHistoryAsync() => base.SessionMaintainsHistoryAsync(); } public class AnthropicBetaChatCompletionRunStreamingTests() diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs index aadbf747c2..06f2a15804 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs @@ -21,7 +21,7 @@ public abstract class SkipAllRun(Func func) : Ru public override Task RunWithStringReturnsExpectedResultAsync() => base.RunWithStringReturnsExpectedResultAsync(); [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task ThreadMaintainsHistoryAsync() => base.ThreadMaintainsHistoryAsync(); + public override Task SessionMaintainsHistoryAsync() => base.SessionMaintainsHistoryAsync(); } public class AnthropicBetaChatCompletionRunTests() diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs index 41bbe82c5d..4b78d30f1c 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs @@ -33,27 +33,27 @@ public class AIProjectClientFixture : IChatClientAgentFixture return response.Value.Id; } - public async Task> GetChatHistoryAsync(AgentThread thread) + public async Task> GetChatHistoryAsync(AgentSession session) { - var chatClientThread = (ChatClientAgentThread)thread; + var chatClientSession = (ChatClientAgentSession)session; - if (chatClientThread.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true) + if (chatClientSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true) { - // Conversation threads do not persist message history. - return await this.GetChatHistoryFromConversationAsync(chatClientThread.ConversationId); + // Conversation sessions do not persist message history. + return await this.GetChatHistoryFromConversationAsync(chatClientSession.ConversationId); } - if (chatClientThread.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true) + if (chatClientSession.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true) { - return await this.GetChatHistoryFromResponsesChainAsync(chatClientThread.ConversationId); + return await this.GetChatHistoryFromResponsesChainAsync(chatClientSession.ConversationId); } - if (chatClientThread.ChatHistoryProvider is null) + if (chatClientSession.ChatHistoryProvider is null) { return []; } - return (await chatClientThread.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); + return (await chatClientSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); } private async Task> GetChatHistoryFromResponsesChainAsync(string conversationId) @@ -125,16 +125,16 @@ public class AIProjectClientFixture : IChatClientAgentFixture public Task DeleteAgentAsync(ChatClientAgent agent) => this._client.Agents.DeleteAgentAsync(agent.Name); - public async Task DeleteThreadAsync(AgentThread thread) + public async Task DeleteSessionAsync(AgentSession session) { - var typedThread = (ChatClientAgentThread)thread; - if (typedThread.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true) + var typedSession = (ChatClientAgentSession)session; + if (typedSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true) { - await this._client.GetProjectOpenAIClient().GetProjectConversationsClient().DeleteConversationAsync(typedThread.ConversationId); + await this._client.GetProjectOpenAIClient().GetProjectConversationsClient().DeleteConversationAsync(typedSession.ConversationId); } - else if (typedThread.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true) + else if (typedSession.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true) { - await this.DeleteResponseChainAsync(typedThread.ConversationId!); + await this.DeleteResponseChainAsync(typedSession.ConversationId!); } } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs index 0999a64da6..2f59630c38 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs @@ -24,13 +24,13 @@ public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture public AIAgent Agent => this._agent; - public async Task> GetChatHistoryAsync(AgentThread thread) + public async Task> GetChatHistoryAsync(AgentSession session) { List messages = []; - var typedThread = (ChatClientAgentThread)thread; + var typedSession = (ChatClientAgentSession)session; await foreach (var threadMessage in (AsyncPageable)this._persistentAgentsClient.Messages.GetMessagesAsync( - threadId: typedThread.ConversationId, order: ListSortOrder.Ascending)) + threadId: typedSession.ConversationId, order: ListSortOrder.Ascending)) { var message = new ChatMessage { @@ -75,12 +75,12 @@ public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture public Task DeleteAgentAsync(ChatClientAgent agent) => this._persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id); - public Task DeleteThreadAsync(AgentThread thread) + public Task DeleteSessionAsync(AgentSession session) { - var typedThread = (ChatClientAgentThread)thread; - if (typedThread?.ConversationId is not null) + var typedSession = (ChatClientAgentSession)session; + if (typedSession?.ConversationId is not null) { - return this._persistentAgentsClient.Threads.DeleteThreadAsync(typedThread.ConversationId); + return this._persistentAgentsClient.Threads.DeleteThreadAsync(typedSession.ConversationId); } return Task.CompletedTask; diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs index bbe1e6548f..3b3ac7ff7e 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs @@ -20,10 +20,10 @@ public class CopilotStudioFixture : IAgentFixture { public AIAgent Agent { get; private set; } = null!; - public Task> GetChatHistoryAsync(AgentThread thread) => + public Task> GetChatHistoryAsync(AgentSession session) => throw new NotSupportedException("CopilotStudio doesn't allow retrieval of chat history."); - public Task DeleteThreadAsync(AgentThread thread) => + public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting threads, so this is a no-op. Task.CompletedTask; diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs index 4f4a670f4e..076512252b 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs @@ -10,8 +10,8 @@ public class CopilotStudioRunStreamingTests() : RunStreamingTests + [Fact(Skip = "Copilot Studio does not support session history retrieval, so this test is not applicable.")] + public override Task SessionMaintainsHistoryAsync() => Task.CompletedTask; [Fact(Skip = ManualVerification)] diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs index 9a89db2326..bf7bcfcd64 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs @@ -10,8 +10,8 @@ public class CopilotStudioRunTests() : RunTests(() => new( // Set to null to run the tests. private const string ManualVerification = "For manual verification"; - [Fact(Skip = "Copilot Studio does not support thread history retrieval, so this test is not applicable.")] - public override Task ThreadMaintainsHistoryAsync() => + [Fact(Skip = "Copilot Studio does not support session history retrieval, so this test is not applicable.")] + public override Task SessionMaintainsHistoryAsync() => Task.CompletedTask; [Fact(Skip = ManualVerification)] diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentSessionTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentSessionTests.cs new file mode 100644 index 0000000000..66018e0131 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentSessionTests.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Text.Json; + +namespace Microsoft.Agents.AI.A2A.UnitTests; + +/// +/// Unit tests for the class. +/// +public sealed class A2AAgentSessionTests +{ + [Fact] + public void Constructor_RoundTrip_SerializationPreservesState() + { + // Arrange + const string ContextId = "context-rt-001"; + const string TaskId = "task-rt-002"; + + A2AAgentSession originalSession = new() { ContextId = ContextId, TaskId = TaskId }; + + // Act + JsonElement serialized = originalSession.Serialize(); + + A2AAgentSession deserializedSession = new(serialized); + + // Assert + Assert.Equal(originalSession.ContextId, deserializedSession.ContextId); + Assert.Equal(originalSession.TaskId, deserializedSession.TaskId); + } +} diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs index b6002de9ef..fd2453cb0d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs @@ -129,7 +129,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunAsync_WithNewThread_UpdatesThreadConversationIdAsync() + public async Task RunAsync_WithNewSession_UpdatesSessionConversationIdAsync() { // Arrange this._handler.ResponseToReturn = new AgentMessage @@ -148,19 +148,19 @@ public sealed class A2AAgentTests : IDisposable new(ChatRole.User, "Test message") }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act - await this._agent.RunAsync(inputMessages, thread); + await this._agent.RunAsync(inputMessages, session); // Assert - Assert.IsType(thread); - var a2aThread = (A2AAgentThread)thread; - Assert.Equal("new-context-id", a2aThread.ContextId); + Assert.IsType(session); + var a2aSession = (A2AAgentSession)session; + Assert.Equal("new-context-id", a2aSession.ContextId); } [Fact] - public async Task RunAsync_WithExistingThread_SetConversationIdToMessageAsync() + public async Task RunAsync_WithExistingSession_SetConversationIdToMessageAsync() { // Arrange var inputMessages = new List @@ -168,12 +168,12 @@ public sealed class A2AAgentTests : IDisposable new(ChatRole.User, "Test message") }; - var thread = await this._agent.GetNewThreadAsync(); - var a2aThread = (A2AAgentThread)thread; - a2aThread.ContextId = "existing-context-id"; + var session = await this._agent.GetNewSessionAsync(); + var a2aSession = (A2AAgentSession)session; + a2aSession.ContextId = "existing-context-id"; // Act - await this._agent.RunAsync(inputMessages, thread); + await this._agent.RunAsync(inputMessages, session); // Assert var message = this._handler.CapturedMessageSendParams?.Message; @@ -182,7 +182,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunAsync_WithThreadHavingDifferentContextId_ThrowsInvalidOperationExceptionAsync() + public async Task RunAsync_WithSessionHavingDifferentContextId_ThrowsInvalidOperationExceptionAsync() { // Arrange var inputMessages = new List @@ -201,12 +201,12 @@ public sealed class A2AAgentTests : IDisposable ContextId = "different-context" }; - var thread = await this._agent.GetNewThreadAsync(); - var a2aThread = (A2AAgentThread)thread; - a2aThread.ContextId = "existing-context-id"; + var session = await this._agent.GetNewSessionAsync(); + var a2aSession = (A2AAgentSession)session; + a2aSession.ContextId = "existing-context-id"; // Act & Assert - await Assert.ThrowsAsync(() => this._agent.RunAsync(inputMessages, thread)); + await Assert.ThrowsAsync(() => this._agent.RunAsync(inputMessages, session)); } [Fact] @@ -256,7 +256,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunStreamingAsync_WithThread_UpdatesThreadConversationIdAsync() + public async Task RunStreamingAsync_WithSession_UpdatesSessionConversationIdAsync() { // Arrange var inputMessages = new List @@ -272,21 +272,21 @@ public sealed class A2AAgentTests : IDisposable ContextId = "new-stream-context" }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act - await foreach (var _ in this._agent.RunStreamingAsync(inputMessages, thread)) + await foreach (var _ in this._agent.RunStreamingAsync(inputMessages, session)) { // Just iterate through to trigger the logic } // Assert - var a2aThread = (A2AAgentThread)thread; - Assert.Equal("new-stream-context", a2aThread.ContextId); + var a2aSession = (A2AAgentSession)session; + Assert.Equal("new-stream-context", a2aSession.ContextId); } [Fact] - public async Task RunStreamingAsync_WithExistingThread_SetConversationIdToMessageAsync() + public async Task RunStreamingAsync_WithExistingSession_SetConversationIdToMessageAsync() { // Arrange var inputMessages = new List @@ -296,12 +296,12 @@ public sealed class A2AAgentTests : IDisposable this._handler.StreamingResponseToReturn = new AgentMessage(); - var thread = await this._agent.GetNewThreadAsync(); - var a2aThread = (A2AAgentThread)thread; - a2aThread.ContextId = "existing-context-id"; + var session = await this._agent.GetNewSessionAsync(); + var a2aSession = (A2AAgentSession)session; + a2aSession.ContextId = "existing-context-id"; // Act - await foreach (var _ in this._agent.RunStreamingAsync(inputMessages, thread)) + await foreach (var _ in this._agent.RunStreamingAsync(inputMessages, session)) { // Just iterate through to trigger the logic } @@ -313,12 +313,12 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunStreamingAsync_WithThreadHavingDifferentContextId_ThrowsInvalidOperationExceptionAsync() + public async Task RunStreamingAsync_WithSessionHavingDifferentContextId_ThrowsInvalidOperationExceptionAsync() { // Arrange - var thread = await this._agent.GetNewThreadAsync(); - var a2aThread = (A2AAgentThread)thread; - a2aThread.ContextId = "existing-context-id"; + var session = await this._agent.GetNewSessionAsync(); + var a2aSession = (A2AAgentSession)session; + a2aSession.ContextId = "existing-context-id"; var inputMessages = new List { @@ -336,7 +336,7 @@ public sealed class A2AAgentTests : IDisposable // Act await Assert.ThrowsAsync(async () => { - await foreach (var update in this._agent.RunStreamingAsync(inputMessages, thread)) + await foreach (var update in this._agent.RunStreamingAsync(inputMessages, session)) { } }); @@ -430,7 +430,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunAsync_WithTaskInThreadAndMessage_AddTaskAsReferencesToMessageAsync() + public async Task RunAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMessageAsync() { // Arrange this._handler.ResponseToReturn = new AgentMessage @@ -440,13 +440,13 @@ public sealed class A2AAgentTests : IDisposable Parts = [new TextPart { Text = "Response to task" }] }; - var thread = (A2AAgentThread)await this._agent.GetNewThreadAsync(); - thread.TaskId = "task-123"; + var session = (A2AAgentSession)await this._agent.GetNewSessionAsync(); + session.TaskId = "task-123"; var inputMessage = new ChatMessage(ChatRole.User, "Please make the background transparent"); // Act - await this._agent.RunAsync(inputMessage, thread); + await this._agent.RunAsync(inputMessage, session); // Assert var message = this._handler.CapturedMessageSendParams?.Message; @@ -456,7 +456,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunAsync_WithAgentTask_UpdatesThreadTaskIdAsync() + public async Task RunAsync_WithAgentTask_UpdatesSessionTaskIdAsync() { // Arrange this._handler.ResponseToReturn = new AgentTask @@ -466,14 +466,14 @@ public sealed class A2AAgentTests : IDisposable Status = new() { State = TaskState.Submitted } }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act - await this._agent.RunAsync("Start a task", thread); + await this._agent.RunAsync("Start a task", session); // Assert - var a2aThread = (A2AAgentThread)thread; - Assert.Equal("task-456", a2aThread.TaskId); + var a2aSession = (A2AAgentSession)session; + Assert.Equal("task-456", a2aSession.TaskId); } [Fact] @@ -492,10 +492,10 @@ public sealed class A2AAgentTests : IDisposable } }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act - var result = await this._agent.RunAsync("Start a long-running task", thread); + var result = await this._agent.RunAsync("Start a long-running task", session); // Assert - verify task is converted correctly Assert.NotNull(result); @@ -511,10 +511,10 @@ public sealed class A2AAgentTests : IDisposable Assert.IsType(result.ContinuationToken); Assert.Equal("task-789", ((A2AContinuationToken)result.ContinuationToken).TaskId); - // Assert - verify thread is updated with context and task IDs - var a2aThread = (A2AAgentThread)thread; - Assert.Equal("context-456", a2aThread.ContextId); - Assert.Equal("task-789", a2aThread.TaskId); + // Assert - verify session is updated with context and task IDs + var a2aSession = (A2AAgentSession)session; + Assert.Equal("context-456", a2aSession.ContextId); + Assert.Equal("task-789", a2aSession.TaskId); // Assert - verify metadata is preserved Assert.NotNull(result.AdditionalProperties); @@ -576,7 +576,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunStreamingAsync_WithTaskInThreadAndMessage_AddTaskAsReferencesToMessageAsync() + public async Task RunStreamingAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMessageAsync() { // Arrange this._handler.StreamingResponseToReturn = new AgentMessage @@ -586,11 +586,11 @@ public sealed class A2AAgentTests : IDisposable Parts = [new TextPart { Text = "Response to task" }] }; - var thread = (A2AAgentThread)await this._agent.GetNewThreadAsync(); - thread.TaskId = "task-123"; + var session = (A2AAgentSession)await this._agent.GetNewSessionAsync(); + session.TaskId = "task-123"; // Act - await foreach (var _ in this._agent.RunStreamingAsync("Please make the background transparent", thread)) + await foreach (var _ in this._agent.RunStreamingAsync("Please make the background transparent", session)) { // Just iterate through to trigger the logic } @@ -603,7 +603,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunStreamingAsync_WithAgentTask_UpdatesThreadTaskIdAsync() + public async Task RunStreamingAsync_WithAgentTask_UpdatesSessionTaskIdAsync() { // Arrange this._handler.StreamingResponseToReturn = new AgentTask @@ -613,17 +613,17 @@ public sealed class A2AAgentTests : IDisposable Status = new() { State = TaskState.Submitted } }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act - await foreach (var _ in this._agent.RunStreamingAsync("Start a task", thread)) + await foreach (var _ in this._agent.RunStreamingAsync("Start a task", session)) { // Just iterate through to trigger the logic } // Assert - var a2aThread = (A2AAgentThread)thread; - Assert.Equal("task-456", a2aThread.TaskId); + var a2aSession = (A2AAgentSession)session; + Assert.Equal("task-456", a2aSession.TaskId); } [Fact] @@ -686,11 +686,11 @@ public sealed class A2AAgentTests : IDisposable ] }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act var updates = new List(); - await foreach (var update in this._agent.RunStreamingAsync("Start long-running task", thread)) + await foreach (var update in this._agent.RunStreamingAsync("Start long-running task", session)) { updates.Add(update); } @@ -705,10 +705,10 @@ public sealed class A2AAgentTests : IDisposable Assert.IsType(update0.RawRepresentation); Assert.Equal(TaskId, ((AgentTask)update0.RawRepresentation!).Id); - // Assert - thread should be updated with context and task IDs - var a2aThread = (A2AAgentThread)thread; - Assert.Equal(ContextId, a2aThread.ContextId); - Assert.Equal(TaskId, a2aThread.TaskId); + // Assert - session should be updated with context and task IDs + var a2aSession = (A2AAgentSession)session; + Assert.Equal(ContextId, a2aSession.ContextId); + Assert.Equal(TaskId, a2aSession.TaskId); } [Fact] @@ -725,11 +725,11 @@ public sealed class A2AAgentTests : IDisposable Status = new() { State = TaskState.Working } }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act var updates = new List(); - await foreach (var update in this._agent.RunStreamingAsync("Check task status", thread)) + await foreach (var update in this._agent.RunStreamingAsync("Check task status", session)) { updates.Add(update); } @@ -743,10 +743,10 @@ public sealed class A2AAgentTests : IDisposable Assert.Equal(this._agent.Id, update0.AgentId); Assert.IsType(update0.RawRepresentation); - // Assert - thread should be updated with context and task IDs - var a2aThread = (A2AAgentThread)thread; - Assert.Equal(ContextId, a2aThread.ContextId); - Assert.Equal(TaskId, a2aThread.TaskId); + // Assert - session should be updated with context and task IDs + var a2aSession = (A2AAgentSession)session; + Assert.Equal(ContextId, a2aSession.ContextId); + Assert.Equal(TaskId, a2aSession.TaskId); } [Fact] @@ -768,11 +768,11 @@ public sealed class A2AAgentTests : IDisposable } }; - var thread = await this._agent.GetNewThreadAsync(); + var session = await this._agent.GetNewSessionAsync(); // Act var updates = new List(); - await foreach (var update in this._agent.RunStreamingAsync("Process artifact", thread)) + await foreach (var update in this._agent.RunStreamingAsync("Process artifact", session)) { updates.Add(update); } @@ -790,14 +790,14 @@ public sealed class A2AAgentTests : IDisposable Assert.NotEmpty(update0.Contents); Assert.Equal(ArtifactContent, update0.Text); - // Assert - thread should be updated with context and task IDs - var a2aThread = (A2AAgentThread)thread; - Assert.Equal(ContextId, a2aThread.ContextId); - Assert.Equal(TaskId, a2aThread.TaskId); + // Assert - session should be updated with context and task IDs + var a2aSession = (A2AAgentSession)session; + Assert.Equal(ContextId, a2aSession.ContextId); + Assert.Equal(TaskId, a2aSession.TaskId); } [Fact] - public async Task RunAsync_WithAllowBackgroundResponsesAndNoThread_ThrowsInvalidOperationExceptionAsync() + public async Task RunAsync_WithAllowBackgroundResponsesAndNoSession_ThrowsInvalidOperationExceptionAsync() { // Arrange var inputMessages = new List @@ -812,7 +812,7 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunStreamingAsync_WithAllowBackgroundResponsesAndNoThread_ThrowsInvalidOperationExceptionAsync() + public async Task RunStreamingAsync_WithAllowBackgroundResponsesAndNoSession_ThrowsInvalidOperationExceptionAsync() { // Arrange var inputMessages = new List @@ -1001,18 +1001,18 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public async Task RunAsync_WithInvalidThreadType_ThrowsInvalidOperationExceptionAsync() + public async Task RunAsync_WithInvalidSessionType_ThrowsInvalidOperationExceptionAsync() { // Arrange - // Create a thread from a different agent type - var invalidThread = new CustomAgentThread(); + // Create a session from a different agent type + var invalidSession = new CustomAgentSession(); // Act & Assert - await Assert.ThrowsAsync(() => this._agent.RunAsync(invalidThread)); + await Assert.ThrowsAsync(() => this._agent.RunAsync(invalidSession)); } [Fact] - public async Task RunStreamingAsync_WithInvalidThreadType_ThrowsInvalidOperationExceptionAsync() + public async Task RunStreamingAsync_WithInvalidSessionType_ThrowsInvalidOperationExceptionAsync() { // Arrange var inputMessages = new List @@ -1020,11 +1020,11 @@ public sealed class A2AAgentTests : IDisposable new(ChatRole.User, "Test message") }; - // Create a thread from a different agent type - var invalidThread = new CustomAgentThread(); + // Create a session from a different agent type + var invalidSession = new CustomAgentSession(); // Act & Assert - await Assert.ThrowsAsync(async () => await this._agent.RunStreamingAsync(inputMessages, invalidThread).ToListAsync()); + await Assert.ThrowsAsync(async () => await this._agent.RunStreamingAsync(inputMessages, invalidSession).ToListAsync()); } public void Dispose() @@ -1034,9 +1034,9 @@ public sealed class A2AAgentTests : IDisposable } /// - /// Custom agent thread class for testing invalid thread type scenario. + /// Custom agent session class for testing invalid session type scenario. /// - private sealed class CustomAgentThread : AgentThread; + private sealed class CustomAgentSession : AgentSession; internal sealed class A2AClientHttpMessageHandlerStub : HttpMessageHandler { diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentThreadTests.cs deleted file mode 100644 index 90b65aa5ac..0000000000 --- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentThreadTests.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Text.Json; - -namespace Microsoft.Agents.AI.A2A.UnitTests; - -/// -/// Unit tests for the class. -/// -public sealed class A2AAgentThreadTests -{ - [Fact] - public void Constructor_RoundTrip_SerializationPreservesState() - { - // Arrange - const string ContextId = "context-rt-001"; - const string TaskId = "task-rt-002"; - - A2AAgentThread originalThread = new() { ContextId = ContextId, TaskId = TaskId }; - - // Act - JsonElement serialized = originalThread.Serialize(); - - A2AAgentThread deserializedThread = new(serialized); - - // Assert - Assert.Equal(originalThread.ContextId, deserializedThread.ContextId); - Assert.Equal(originalThread.TaskId, deserializedThread.TaskId); - } -} diff --git a/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs b/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs index 9109118f73..260e6483dd 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs @@ -81,7 +81,7 @@ public sealed class AGUIAgentTests } [Fact] - public async Task RunAsync_WithNullThread_CreatesNewThreadAsync() + public async Task RunAsync_WithNullSession_CreatesNewSessionAsync() { // Arrange using HttpClient httpClient = this.CreateMockHttpClient( @@ -95,7 +95,7 @@ public sealed class AGUIAgentTests List messages = [new ChatMessage(ChatRole.User, "Test")]; // Act - AgentResponse response = await agent.RunAsync(messages, thread: null); + AgentResponse response = await agent.RunAsync(messages, session: null); // Assert Assert.NotNull(response); @@ -152,7 +152,7 @@ public sealed class AGUIAgentTests } [Fact] - public async Task RunStreamingAsync_WithNullThread_CreatesNewThreadAsync() + public async Task RunStreamingAsync_WithNullSession_CreatesNewSessionAsync() { // Arrange using HttpClient httpClient = this.CreateMockHttpClient( @@ -167,7 +167,7 @@ public sealed class AGUIAgentTests // Act List updates = []; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread: null)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session: null)) { // Consume the stream updates.Add(update); @@ -228,12 +228,12 @@ public sealed class AGUIAgentTests var chatClient = new AGUIChatClient(httpClient, "http://localhost/agent", null, AGUIJsonSerializerContext.Default.Options); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "agent1", description: "Test agent", tools: []); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); List messages = [new ChatMessage(ChatRole.User, "Hello")]; // Act List updates = []; - await foreach (var update in agent.RunStreamingAsync(messages, thread)) + await foreach (var update in agent.RunStreamingAsync(messages, session)) { updates.Add(update); } @@ -244,21 +244,21 @@ public sealed class AGUIAgentTests } [Fact] - public async Task DeserializeThread_WithValidState_ReturnsChatClientAgentThreadAsync() + public async Task DeserializeSession_WithValidState_ReturnsChatClientAgentSessionAsync() { // Arrange using var httpClient = new HttpClient(); var chatClient = new AGUIChatClient(httpClient, "http://localhost/agent", null, AGUIJsonSerializerContext.Default.Options); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "agent1", description: "Test agent", tools: []); - AgentThread originalThread = await agent.GetNewThreadAsync(); - JsonElement serialized = originalThread.Serialize(); + AgentSession originalSession = await agent.GetNewSessionAsync(); + JsonElement serialized = originalSession.Serialize(); // Act - AgentThread deserialized = await agent.DeserializeThreadAsync(serialized); + AgentSession deserialized = await agent.DeserializeSessionAsync(serialized); // Assert Assert.NotNull(deserialized); - Assert.IsType(deserialized); + Assert.IsType(deserialized); } private HttpClient CreateMockHttpClient(BaseEvent[] events) @@ -462,7 +462,7 @@ public sealed class AGUIAgentTests } [Fact] - public async Task RunStreamingAsync_UpdatesThreadWithToolMessages_AfterCompletionAsync() + public async Task RunStreamingAsync_UpdatesSessionWithToolMessages_AfterCompletionAsync() { // Arrange AIFunction testTool = AIFunctionFactory.Create(() => "Result", "TestTool"); @@ -487,12 +487,12 @@ public sealed class AGUIAgentTests var chatClient = new AGUIChatClient(httpClient, "http://localhost/agent", null, AGUIJsonSerializerContext.Default.Options); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "agent1", description: "Test agent", tools: [testTool]); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); List messages = [new ChatMessage(ChatRole.User, "Test")]; // Act List updates = []; - await foreach (var update in agent.RunStreamingAsync(messages, thread)) + await foreach (var update in agent.RunStreamingAsync(messages, session)) { updates.Add(update); } @@ -682,11 +682,11 @@ public sealed class AGUIAgentTests // Arrange using HttpClient httpClient = this.CreateMockHttpClient( [ - new RunStartedEvent { ThreadId = "server-thread-456", RunId = "run1" }, + new RunStartedEvent { ThreadId = "server-session-456", RunId = "run1" }, new TextMessageStartEvent { MessageId = "msg1", Role = AGUIRoles.Assistant }, new TextMessageContentEvent { MessageId = "msg1", Delta = "Hello" }, new TextMessageEndEvent { MessageId = "msg1" }, - new RunFinishedEvent { ThreadId = "server-thread-456", RunId = "run1" } + new RunFinishedEvent { ThreadId = "server-session-456", RunId = "run1" } ]); var chatClient = new AGUIChatClient(httpClient, "http://localhost/agent", null, AGUIJsonSerializerContext.Default.Options); @@ -700,8 +700,8 @@ public sealed class AGUIAgentTests updates.Add(update); } - // Assert - Should use thread ID from server - Assert.All(updates, u => Assert.Equal("server-thread-456", u.ConversationId)); + // Assert - Should use session ID from server + Assert.All(updates, u => Assert.Equal("server-session-456", u.ConversationId)); } [Fact] @@ -802,18 +802,18 @@ public sealed class AGUIAgentTests [Fact] public async Task GetStreamingResponseAsync_UsesServerThreadId_WhenDifferentFromClientAsync() { - // Arrange - Server returns different thread ID + // Arrange - Server returns different session ID using HttpClient httpClient = this.CreateMockHttpClient( [ - new RunStartedEvent { ThreadId = "server-generated-thread", RunId = "run1" }, + new RunStartedEvent { ThreadId = "server-generated-session", RunId = "run1" }, new TextMessageStartEvent { MessageId = "msg1", Role = AGUIRoles.Assistant }, new TextMessageContentEvent { MessageId = "msg1", Delta = "Hello" }, new TextMessageEndEvent { MessageId = "msg1" }, - new RunFinishedEvent { ThreadId = "server-generated-thread", RunId = "run1" } + new RunFinishedEvent { ThreadId = "server-generated-session", RunId = "run1" } ]); var chatClient = new AGUIChatClient(httpClient, "http://localhost/agent", null, AGUIJsonSerializerContext.Default.Options); - var options = new ChatOptions { ConversationId = "client-thread-123" }; + var options = new ChatOptions { ConversationId = "client-session-123" }; List messages = [new ChatMessage(ChatRole.User, "Test")]; // Act @@ -824,7 +824,7 @@ public sealed class AGUIAgentTests } // Assert - Should use client's conversation ID (we provided it explicitly) - Assert.All(updates, u => Assert.Equal("client-thread-123", u.ConversationId)); + Assert.All(updates, u => Assert.Equal("client-session-123", u.ConversationId)); } [Fact] @@ -947,7 +947,7 @@ public sealed class AGUIAgentTests } // Act - Second turn with conversation history including function call - // The thread ID should be extracted from the function call in the conversation history + // The session ID should be extracted from the function call in the conversation history options.ConversationId = conversationId; List secondTurnUpdates = []; await foreach (var update in chatClient.GetStreamingResponseAsync(conversation, options)) @@ -1017,7 +1017,7 @@ public sealed class AGUIAgentTests [Fact] public async Task GetStreamingResponseAsync_HandlesEmptyThreadId_GracefullyAsync() { - // Arrange - Server returns empty thread ID + // Arrange - Server returns empty session ID using HttpClient httpClient = this.CreateMockHttpClient( [ new RunStartedEvent { ThreadId = string.Empty, RunId = "run1" }, @@ -1037,7 +1037,7 @@ public sealed class AGUIAgentTests updates.Add(update); } - // Assert - Should generate a conversation ID even with empty server thread ID + // Assert - Should generate a conversation ID even with empty server session ID Assert.NotEmpty(updates); Assert.All(updates, u => Assert.NotNull(u.ConversationId)); Assert.All(updates, u => Assert.NotEmpty(u.ConversationId!)); @@ -1048,23 +1048,23 @@ public sealed class AGUIAgentTests { // Arrange var handler = new TestDelegatingHandler(); - // First turn: server returns thread-A + // First turn: server returns session-A handler.AddResponse( [ - new RunStartedEvent { ThreadId = "thread-A", RunId = "run1" }, + new RunStartedEvent { ThreadId = "session-A", RunId = "run1" }, new TextMessageStartEvent { MessageId = "msg1", Role = AGUIRoles.Assistant }, new TextMessageContentEvent { MessageId = "msg1", Delta = "First" }, new TextMessageEndEvent { MessageId = "msg1" }, - new RunFinishedEvent { ThreadId = "thread-A", RunId = "run1" } + new RunFinishedEvent { ThreadId = "session-A", RunId = "run1" } ]); - // Second turn: provide thread-A but server returns thread-B + // Second turn: provide session-A but server returns session-B handler.AddResponse( [ - new RunStartedEvent { ThreadId = "thread-B", RunId = "run2" }, + new RunStartedEvent { ThreadId = "session-B", RunId = "run2" }, new TextMessageStartEvent { MessageId = "msg2", Role = AGUIRoles.Assistant }, new TextMessageContentEvent { MessageId = "msg2", Delta = "Second" }, new TextMessageEndEvent { MessageId = "msg2" }, - new RunFinishedEvent { ThreadId = "thread-B", RunId = "run2" } + new RunFinishedEvent { ThreadId = "session-B", RunId = "run2" } ]); using HttpClient httpClient = new(handler); @@ -1087,8 +1087,8 @@ public sealed class AGUIAgentTests } // Assert - Should use client-provided conversation ID, not server's changed ID - Assert.Equal("thread-A", firstConversationId); - Assert.Equal("thread-A", secondConversationId); // Client overrides server's thread-B + Assert.Equal("session-A", firstConversationId); + Assert.Equal("session-A", secondConversationId); // Client overrides server's session-B } [Fact] diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs index 8d5f1b0b87..4b872f0da1 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests; public class AIAgentTests { private readonly Mock _agentMock; - private readonly Mock _agentThreadMock; + private readonly Mock _agentSessionMock; private readonly AgentResponse _invokeResponse; private readonly List _invokeStreamingResponses = []; @@ -27,7 +27,7 @@ public class AIAgentTests /// public AIAgentTests() { - this._agentThreadMock = new Mock(MockBehavior.Strict); + this._agentSessionMock = new Mock(MockBehavior.Strict); this._invokeResponse = new AgentResponse(new ChatMessage(ChatRole.Assistant, "Hi")); this._invokeStreamingResponses.Add(new AgentResponseUpdate(ChatRole.Assistant, "Hi")); @@ -37,7 +37,7 @@ public class AIAgentTests .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(this._invokeResponse); @@ -45,7 +45,7 @@ public class AIAgentTests .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.IsAny>(), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._invokeStreamingResponses)); @@ -63,7 +63,7 @@ public class AIAgentTests var cancellationToken = default(CancellationToken); // Act - var response = await this._agentMock.Object.RunAsync(this._agentThreadMock.Object, options, cancellationToken); + var response = await this._agentMock.Object.RunAsync(this._agentSessionMock.Object, options, cancellationToken); Assert.Equal(this._invokeResponse, response); // Verify that the mocked method was called with the expected parameters @@ -72,7 +72,7 @@ public class AIAgentTests .Verify>("RunCoreAsync", Times.Once(), ItExpr.Is>(messages => !messages.Any()), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.Is(o => o == options), ItExpr.Is(ct => ct == cancellationToken)); } @@ -90,7 +90,7 @@ public class AIAgentTests var cancellationToken = default(CancellationToken); // Act - var response = await this._agentMock.Object.RunAsync(Message, this._agentThreadMock.Object, options, cancellationToken); + var response = await this._agentMock.Object.RunAsync(Message, this._agentSessionMock.Object, options, cancellationToken); Assert.Equal(this._invokeResponse, response); // Verify that the mocked method was called with the expected parameters @@ -99,7 +99,7 @@ public class AIAgentTests .Verify>("RunCoreAsync", Times.Once(), ItExpr.Is>(messages => messages.Count() == 1 && messages.First().Text == Message), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.Is(o => o == options), ItExpr.Is(ct => ct == cancellationToken)); } @@ -117,7 +117,7 @@ public class AIAgentTests var cancellationToken = default(CancellationToken); // Act - var response = await this._agentMock.Object.RunAsync(message, this._agentThreadMock.Object, options, cancellationToken); + var response = await this._agentMock.Object.RunAsync(message, this._agentSessionMock.Object, options, cancellationToken); Assert.Equal(this._invokeResponse, response); // Verify that the mocked method was called with the expected parameters @@ -126,7 +126,7 @@ public class AIAgentTests .Verify>("RunCoreAsync", Times.Once(), ItExpr.Is>(messages => messages.Count() == 1 && messages.First() == message), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.Is(o => o == options), ItExpr.Is(ct => ct == cancellationToken)); } @@ -143,7 +143,7 @@ public class AIAgentTests var cancellationToken = default(CancellationToken); // Act - await foreach (var response in this._agentMock.Object.RunStreamingAsync(this._agentThreadMock.Object, options, cancellationToken)) + await foreach (var response in this._agentMock.Object.RunStreamingAsync(this._agentSessionMock.Object, options, cancellationToken)) { // Assert Assert.Contains(response, this._invokeStreamingResponses); @@ -155,7 +155,7 @@ public class AIAgentTests .Verify>("RunCoreStreamingAsync", Times.Once(), ItExpr.Is>(messages => !messages.Any()), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.Is(o => o == options), ItExpr.Is(ct => ct == cancellationToken)); } @@ -173,7 +173,7 @@ public class AIAgentTests var cancellationToken = default(CancellationToken); // Act - await foreach (var response in this._agentMock.Object.RunStreamingAsync(Message, this._agentThreadMock.Object, options, cancellationToken)) + await foreach (var response in this._agentMock.Object.RunStreamingAsync(Message, this._agentSessionMock.Object, options, cancellationToken)) { // Assert Assert.Contains(response, this._invokeStreamingResponses); @@ -185,7 +185,7 @@ public class AIAgentTests .Verify>("RunCoreStreamingAsync", Times.Once(), ItExpr.Is>(messages => messages.Count() == 1 && messages.First().Text == Message), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.Is(o => o == options), ItExpr.Is(ct => ct == cancellationToken)); } @@ -203,7 +203,7 @@ public class AIAgentTests var cancellationToken = default(CancellationToken); // Act - await foreach (var response in this._agentMock.Object.RunStreamingAsync(message, this._agentThreadMock.Object, options, cancellationToken)) + await foreach (var response in this._agentMock.Object.RunStreamingAsync(message, this._agentSessionMock.Object, options, cancellationToken)) { // Assert Assert.Contains(response, this._invokeStreamingResponses); @@ -215,7 +215,7 @@ public class AIAgentTests .Verify>("RunCoreStreamingAsync", Times.Once(), ItExpr.Is>(messages => messages.Count() == 1 && messages.First() == message), - ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(t => t == this._agentSessionMock.Object), ItExpr.Is(o => o == options), ItExpr.Is(ct => ct == cancellationToken)); } @@ -365,9 +365,9 @@ public class AIAgentTests #endregion /// - /// Typed mock thread. + /// Typed mock session. /// - public abstract class TestAgentThread : AgentThread; + public abstract class TestAgentSession : AgentSession; private sealed class MockAgent : AIAgent { @@ -378,22 +378,22 @@ public class AIAgentTests protected override string? IdCore { get; } - public override async ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override async ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public override async ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override async ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentSessionTests.cs similarity index 59% rename from dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentThreadTests.cs rename to dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentSessionTests.cs index e75cb4caa1..b473b713ab 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentThreadTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentSessionTests.cs @@ -7,52 +7,52 @@ using System; namespace Microsoft.Agents.AI.Abstractions.UnitTests; /// -/// Tests for +/// Tests for /// -public class AgentThreadTests +public class AgentSessionTests { [Fact] public void Serialize_ReturnsDefaultJsonElement() { - var thread = new TestAgentThread(); - var result = thread.Serialize(); + var session = new TestAgentSession(); + var result = session.Serialize(); Assert.Equal(default, result); } #region GetService Method Tests /// - /// Verify that GetService returns the thread itself when requesting the exact thread type. + /// Verify that GetService returns the session itself when requesting the exact session type. /// [Fact] - public void GetService_RequestingExactThreadType_ReturnsThread() + public void GetService_RequestingExactThreadType_ReturnsSession() { // Arrange - var thread = new TestAgentThread(); + var session = new TestAgentSession(); // Act - var result = thread.GetService(typeof(TestAgentThread)); + var result = session.GetService(typeof(TestAgentSession)); // Assert Assert.NotNull(result); - Assert.Same(thread, result); + Assert.Same(session, result); } /// - /// Verify that GetService returns the thread itself when requesting the base AgentThread type. + /// Verify that GetService returns the session itself when requesting the base AgentSession type. /// [Fact] - public void GetService_RequestingAgentThreadType_ReturnsThread() + public void GetService_RequestingAgentSessionType_ReturnsSession() { // Arrange - var thread = new TestAgentThread(); + var session = new TestAgentSession(); // Act - var result = thread.GetService(typeof(AgentThread)); + var result = session.GetService(typeof(AgentSession)); // Assert Assert.NotNull(result); - Assert.Same(thread, result); + Assert.Same(session, result); } /// @@ -62,10 +62,10 @@ public class AgentThreadTests public void GetService_RequestingUnrelatedType_ReturnsNull() { // Arrange - var thread = new TestAgentThread(); + var session = new TestAgentSession(); // Act - var result = thread.GetService(typeof(string)); + var result = session.GetService(typeof(string)); // Assert Assert.Null(result); @@ -78,10 +78,10 @@ public class AgentThreadTests public void GetService_WithServiceKey_ReturnsNull() { // Arrange - var thread = new TestAgentThread(); + var session = new TestAgentSession(); // Act - var result = thread.GetService(typeof(TestAgentThread), "some-key"); + var result = session.GetService(typeof(TestAgentSession), "some-key"); // Assert Assert.Null(result); @@ -94,10 +94,10 @@ public class AgentThreadTests public void GetService_WithNullServiceType_ThrowsArgumentNullException() { // Arrange - var thread = new TestAgentThread(); + var session = new TestAgentSession(); // Act & Assert - Assert.Throws(() => thread.GetService(null!)); + Assert.Throws(() => session.GetService(null!)); } /// @@ -107,14 +107,14 @@ public class AgentThreadTests public void GetService_Generic_ReturnsCorrectType() { // Arrange - var thread = new TestAgentThread(); + var session = new TestAgentSession(); // Act - var result = thread.GetService(); + var result = session.GetService(); // Assert Assert.NotNull(result); - Assert.Same(thread, result); + Assert.Same(session, result); } /// @@ -124,10 +124,10 @@ public class AgentThreadTests public void GetService_Generic_ReturnsNullForUnrelatedType() { // Arrange - var thread = new TestAgentThread(); + var session = new TestAgentSession(); // Act - var result = thread.GetService(); + var result = session.GetService(); // Assert Assert.Null(result); @@ -135,5 +135,5 @@ public class AgentThreadTests #endregion - private sealed class TestAgentThread : AgentThread; + private sealed class TestAgentSession : AgentSession; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs index 8055a95f3a..5be491213b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs @@ -19,7 +19,7 @@ public class DelegatingAIAgentTests private readonly TestDelegatingAIAgent _delegatingAgent; private readonly AgentResponse _testResponse; private readonly List _testStreamingResponses; - private readonly AgentThread _testThread; + private readonly AgentSession _testSession; /// /// Initializes a new instance of the class. @@ -29,19 +29,19 @@ public class DelegatingAIAgentTests this._innerAgentMock = new Mock(); this._testResponse = new AgentResponse(new ChatMessage(ChatRole.Assistant, "Test response")); this._testStreamingResponses = [new AgentResponseUpdate(ChatRole.Assistant, "Test streaming response")]; - this._testThread = new TestAgentThread(); + this._testSession = new TestAgentSession(); // Setup inner agent mock this._innerAgentMock.Protected().SetupGet("IdCore").Returns("test-agent-id"); this._innerAgentMock.Setup(x => x.Name).Returns("Test Agent"); this._innerAgentMock.Setup(x => x.Description).Returns("Test Description"); - this._innerAgentMock.Setup(x => x.GetNewThreadAsync()).ReturnsAsync(this._testThread); + this._innerAgentMock.Setup(x => x.GetNewSessionAsync()).ReturnsAsync(this._testSession); this._innerAgentMock .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(this._testResponse); @@ -50,7 +50,7 @@ public class DelegatingAIAgentTests .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._testStreamingResponses)); @@ -132,17 +132,17 @@ public class DelegatingAIAgentTests #region Method Delegation Tests /// - /// Verify that GetNewThreadAsync delegates to inner agent. + /// Verify that GetNewSessionAsync delegates to inner agent. /// [Fact] - public async Task GetNewThreadAsync_DelegatesToInnerAgentAsync() + public async Task GetNewSessionAsync_DelegatesToInnerAgentAsync() { // Act - var thread = await this._delegatingAgent.GetNewThreadAsync(); + var session = await this._delegatingAgent.GetNewSessionAsync(); // Assert - Assert.Same(this._testThread, thread); - this._innerAgentMock.Verify(x => x.GetNewThreadAsync(), Times.Once); + Assert.Same(this._testSession, session); + this._innerAgentMock.Verify(x => x.GetNewSessionAsync(), Times.Once); } /// @@ -153,7 +153,7 @@ public class DelegatingAIAgentTests { // Arrange var expectedMessages = new[] { new ChatMessage(ChatRole.User, "Test message") }; - var expectedThread = new TestAgentThread(); + var expectedSession = new TestAgentSession(); var expectedOptions = new AgentRunOptions(); var expectedCancellationToken = new CancellationToken(); var expectedResult = new TaskCompletionSource(); @@ -164,7 +164,7 @@ public class DelegatingAIAgentTests .Protected() .Setup>("RunCoreAsync", ItExpr.Is>(m => m == expectedMessages), - ItExpr.Is(t => t == expectedThread), + ItExpr.Is(t => t == expectedSession), ItExpr.Is(o => o == expectedOptions), ItExpr.Is(ct => ct == expectedCancellationToken)) .Returns(expectedResult.Task); @@ -172,7 +172,7 @@ public class DelegatingAIAgentTests var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); // Act - var resultTask = delegatingAgent.RunAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken); + var resultTask = delegatingAgent.RunAsync(expectedMessages, expectedSession, expectedOptions, expectedCancellationToken); // Assert Assert.False(resultTask.IsCompleted); @@ -189,7 +189,7 @@ public class DelegatingAIAgentTests { // Arrange var expectedMessages = new[] { new ChatMessage(ChatRole.User, "Test message") }; - var expectedThread = new TestAgentThread(); + var expectedSession = new TestAgentSession(); var expectedOptions = new AgentRunOptions(); var expectedCancellationToken = new CancellationToken(); AgentResponseUpdate[] expectedResults = @@ -203,7 +203,7 @@ public class DelegatingAIAgentTests .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.Is>(m => m == expectedMessages), - ItExpr.Is(t => t == expectedThread), + ItExpr.Is(t => t == expectedSession), ItExpr.Is(o => o == expectedOptions), ItExpr.Is(ct => ct == expectedCancellationToken)) .Returns(ToAsyncEnumerableAsync(expectedResults)); @@ -211,7 +211,7 @@ public class DelegatingAIAgentTests var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); // Act - var resultAsyncEnumerable = delegatingAgent.RunStreamingAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken); + var resultAsyncEnumerable = delegatingAgent.RunStreamingAsync(expectedMessages, expectedSession, expectedOptions, expectedCancellationToken); // Assert var enumerator = resultAsyncEnumerable.GetAsyncEnumerator(); @@ -314,7 +314,7 @@ public class DelegatingAIAgentTests public new AIAgent InnerAgent => base.InnerAgent; } - private sealed class TestAgentThread : AgentThread; + private sealed class TestAgentSession : AgentSession; #endregion } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryAgentThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryAgentSessionTests.cs similarity index 57% rename from dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryAgentThreadTests.cs rename to dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryAgentSessionTests.cs index c35ff98711..a3a4bf7e0e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryAgentThreadTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryAgentSessionTests.cs @@ -9,9 +9,9 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI.Abstractions.UnitTests; /// -/// Contains tests for . +/// Contains tests for . /// -public class InMemoryAgentThreadTests +public class InMemoryAgentSessionTests { #region Constructor and Property Tests @@ -19,11 +19,11 @@ public class InMemoryAgentThreadTests public void Constructor_SetsDefaultChatHistoryProvider() { // Arrange & Act - var thread = new TestInMemoryAgentThread(); + var session = new TestInMemoryAgentSession(); // Assert - Assert.NotNull(thread.GetChatHistoryProvider()); - Assert.Empty(thread.GetChatHistoryProvider()); + Assert.NotNull(session.GetChatHistoryProvider()); + Assert.Empty(session.GetChatHistoryProvider()); } [Fact] @@ -33,12 +33,12 @@ public class InMemoryAgentThreadTests InMemoryChatHistoryProvider provider = [new(ChatRole.User, "Hello")]; // Act - var thread = new TestInMemoryAgentThread(provider); + var session = new TestInMemoryAgentSession(provider); // Assert - Assert.Same(provider, thread.GetChatHistoryProvider()); - Assert.Single(thread.GetChatHistoryProvider()); - Assert.Equal("Hello", thread.GetChatHistoryProvider()[0].Text); + Assert.Same(provider, session.GetChatHistoryProvider()); + Assert.Single(session.GetChatHistoryProvider()); + Assert.Equal("Hello", session.GetChatHistoryProvider()[0].Text); } [Fact] @@ -48,12 +48,12 @@ public class InMemoryAgentThreadTests var messages = new List { new(ChatRole.User, "Hi") }; // Act - var thread = new TestInMemoryAgentThread(messages); + var session = new TestInMemoryAgentSession(messages); // Assert - Assert.NotNull(thread.GetChatHistoryProvider()); - Assert.Single(thread.GetChatHistoryProvider()); - Assert.Equal("Hi", thread.GetChatHistoryProvider()[0].Text); + Assert.NotNull(session.GetChatHistoryProvider()); + Assert.Single(session.GetChatHistoryProvider()); + Assert.Equal("Hi", session.GetChatHistoryProvider()[0].Text); } [Fact] @@ -62,16 +62,16 @@ public class InMemoryAgentThreadTests // Arrange InMemoryChatHistoryProvider provider = [new(ChatRole.User, "TestMsg")]; var providerState = provider.Serialize(); - var threadStateWrapper = new InMemoryAgentThread.InMemoryAgentThreadState { ChatHistoryProviderState = providerState }; - var json = JsonSerializer.SerializeToElement(threadStateWrapper, TestJsonSerializerContext.Default.InMemoryAgentThreadState); + var sessionStateWrapper = new InMemoryAgentSession.InMemoryAgentSessionState { ChatHistoryProviderState = providerState }; + var json = JsonSerializer.SerializeToElement(sessionStateWrapper, TestJsonSerializerContext.Default.InMemoryAgentSessionState); // Act - var thread = new TestInMemoryAgentThread(json); + var session = new TestInMemoryAgentSession(json); // Assert - Assert.NotNull(thread.GetChatHistoryProvider()); - Assert.Single(thread.GetChatHistoryProvider()); - Assert.Equal("TestMsg", thread.GetChatHistoryProvider()[0].Text); + Assert.NotNull(session.GetChatHistoryProvider()); + Assert.Single(session.GetChatHistoryProvider()); + Assert.Equal("TestMsg", session.GetChatHistoryProvider()[0].Text); } [Fact] @@ -81,7 +81,7 @@ public class InMemoryAgentThreadTests var invalidJson = JsonSerializer.SerializeToElement(42, TestJsonSerializerContext.Default.Int32); // Act & Assert - Assert.Throws(() => new TestInMemoryAgentThread(invalidJson)); + Assert.Throws(() => new TestInMemoryAgentSession(invalidJson)); } #endregion @@ -92,10 +92,10 @@ public class InMemoryAgentThreadTests public void Serialize_ReturnsCorrectJson_WhenMessagesExist() { // Arrange - var thread = new TestInMemoryAgentThread([new(ChatRole.User, "TestContent")]); + var session = new TestInMemoryAgentSession([new(ChatRole.User, "TestContent")]); // Act - var json = thread.Serialize(); + var json = session.Serialize(); // Assert Assert.Equal(JsonValueKind.Object, json.ValueKind); @@ -111,10 +111,10 @@ public class InMemoryAgentThreadTests public void Serialize_ReturnsEmptyMessages_WhenNoMessages() { // Arrange - var thread = new TestInMemoryAgentThread(); + var session = new TestInMemoryAgentSession(); // Act - var json = thread.Serialize(); + var json = session.Serialize(); // Assert Assert.Equal(JsonValueKind.Object, json.ValueKind); @@ -133,23 +133,23 @@ public class InMemoryAgentThreadTests public void GetService_RequestingChatHistoryProvider_ReturnsChatHistoryProvider() { // Arrange - var thread = new TestInMemoryAgentThread(); + var session = new TestInMemoryAgentSession(); // Act & Assert - Assert.NotNull(thread.GetService(typeof(ChatHistoryProvider))); - Assert.Same(thread.GetChatHistoryProvider(), thread.GetService(typeof(ChatHistoryProvider))); - Assert.Same(thread.GetChatHistoryProvider(), thread.GetService(typeof(InMemoryChatHistoryProvider))); + Assert.NotNull(session.GetService(typeof(ChatHistoryProvider))); + Assert.Same(session.GetChatHistoryProvider(), session.GetService(typeof(ChatHistoryProvider))); + Assert.Same(session.GetChatHistoryProvider(), session.GetService(typeof(InMemoryChatHistoryProvider))); } #endregion // Sealed test subclass to expose protected members for testing - private sealed class TestInMemoryAgentThread : InMemoryAgentThread + private sealed class TestInMemoryAgentSession : InMemoryAgentSession { - public TestInMemoryAgentThread() { } - public TestInMemoryAgentThread(InMemoryChatHistoryProvider? provider) : base(provider) { } - public TestInMemoryAgentThread(IEnumerable messages) : base(messages) { } - public TestInMemoryAgentThread(JsonElement serializedThreadState) : base(serializedThreadState) { } + public TestInMemoryAgentSession() { } + public TestInMemoryAgentSession(InMemoryChatHistoryProvider? provider) : base(provider) { } + public TestInMemoryAgentSession(IEnumerable messages) : base(messages) { } + public TestInMemoryAgentSession(JsonElement serializedSessionState) : base(serializedSessionState) { } public InMemoryChatHistoryProvider GetChatHistoryProvider() => this.ChatHistoryProvider; } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ServiceIdAgentSessionTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ServiceIdAgentSessionTests.cs new file mode 100644 index 0000000000..e4a6626f72 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ServiceIdAgentSessionTests.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Text.Json; + +namespace Microsoft.Agents.AI.Abstractions.UnitTests; + +/// +/// Tests for . +/// +public class ServiceIdAgentSessionTests +{ + #region Constructor and Property Tests + + [Fact] + public void Constructor_SetsDefaults() + { + // Arrange & Act + var session = new TestServiceIdAgentSession(); + + // Assert + Assert.Null(session.GetServiceSessionId()); + } + + [Fact] + public void Constructor_WithServiceSessionId_SetsProperty() + { + // Arrange & Act + var session = new TestServiceIdAgentSession("service-id-123"); + + // Assert + Assert.Equal("service-id-123", session.GetServiceSessionId()); + } + + [Fact] + public void Constructor_WithSerializedId_SetsProperty() + { + // Arrange + var serviceSessionWrapper = new ServiceIdAgentSession.ServiceIdAgentSessionState { ServiceSessionId = "service-id-456" }; + var json = JsonSerializer.SerializeToElement(serviceSessionWrapper, TestJsonSerializerContext.Default.ServiceIdAgentSessionState); + + // Act + var session = new TestServiceIdAgentSession(json); + + // Assert + Assert.Equal("service-id-456", session.GetServiceSessionId()); + } + + [Fact] + public void Constructor_WithSerializedUndefinedId_SetsProperty() + { + // Arrange + var emptyObject = new EmptyObject(); + var json = JsonSerializer.SerializeToElement(emptyObject, TestJsonSerializerContext.Default.EmptyObject); + + // Act + var session = new TestServiceIdAgentSession(json); + + // Assert + Assert.Null(session.GetServiceSessionId()); + } + + [Fact] + public void Constructor_WithInvalidJson_ThrowsArgumentException() + { + // Arrange + var invalidJson = JsonSerializer.SerializeToElement(42, TestJsonSerializerContext.Default.Int32); + + // Act & Assert + Assert.Throws(() => new TestServiceIdAgentSession(invalidJson)); + } + + #endregion + + #region SerializeAsync Tests + + [Fact] + public void Serialize_ReturnsCorrectJson_WhenServiceSessionIdIsSet() + { + // Arrange + var session = new TestServiceIdAgentSession("service-id-789"); + + // Act + var json = session.Serialize(); + + // Assert + Assert.Equal(JsonValueKind.Object, json.ValueKind); + Assert.True(json.TryGetProperty("serviceSessionId", out var idProperty)); + Assert.Equal("service-id-789", idProperty.GetString()); + } + + [Fact] + public void Serialize_ReturnsUndefinedServiceSessionId_WhenNotSet() + { + // Arrange + var session = new TestServiceIdAgentSession(); + + // Act + var json = session.Serialize(); + + // Assert + Assert.Equal(JsonValueKind.Object, json.ValueKind); + Assert.False(json.TryGetProperty("serviceSessionId", out _)); + } + + #endregion + + // Sealed test subclass to expose protected members for testing + private sealed class TestServiceIdAgentSession : ServiceIdAgentSession + { + public TestServiceIdAgentSession() { } + public TestServiceIdAgentSession(string serviceSessionId) : base(serviceSessionId) { } + public TestServiceIdAgentSession(JsonElement serializedSessionState) : base(serializedSessionState) { } + public string? GetServiceSessionId() => this.ServiceSessionId; + } + + // Helper class to represent empty objects + internal sealed class EmptyObject; +} diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ServiceIdAgentThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ServiceIdAgentThreadTests.cs deleted file mode 100644 index 1da79344d4..0000000000 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ServiceIdAgentThreadTests.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Text.Json; - -namespace Microsoft.Agents.AI.Abstractions.UnitTests; - -/// -/// Tests for . -/// -public class ServiceIdAgentThreadTests -{ - #region Constructor and Property Tests - - [Fact] - public void Constructor_SetsDefaults() - { - // Arrange & Act - var thread = new TestServiceIdAgentThread(); - - // Assert - Assert.Null(thread.GetServiceThreadId()); - } - - [Fact] - public void Constructor_WithServiceThreadId_SetsProperty() - { - // Arrange & Act - var thread = new TestServiceIdAgentThread("service-id-123"); - - // Assert - Assert.Equal("service-id-123", thread.GetServiceThreadId()); - } - - [Fact] - public void Constructor_WithSerializedId_SetsProperty() - { - // Arrange - var serviceThreadWrapper = new ServiceIdAgentThread.ServiceIdAgentThreadState { ServiceThreadId = "service-id-456" }; - var json = JsonSerializer.SerializeToElement(serviceThreadWrapper, TestJsonSerializerContext.Default.ServiceIdAgentThreadState); - - // Act - var thread = new TestServiceIdAgentThread(json); - - // Assert - Assert.Equal("service-id-456", thread.GetServiceThreadId()); - } - - [Fact] - public void Constructor_WithSerializedUndefinedId_SetsProperty() - { - // Arrange - var emptyObject = new EmptyObject(); - var json = JsonSerializer.SerializeToElement(emptyObject, TestJsonSerializerContext.Default.EmptyObject); - - // Act - var thread = new TestServiceIdAgentThread(json); - - // Assert - Assert.Null(thread.GetServiceThreadId()); - } - - [Fact] - public void Constructor_WithInvalidJson_ThrowsArgumentException() - { - // Arrange - var invalidJson = JsonSerializer.SerializeToElement(42, TestJsonSerializerContext.Default.Int32); - - // Act & Assert - Assert.Throws(() => new TestServiceIdAgentThread(invalidJson)); - } - - #endregion - - #region SerializeAsync Tests - - [Fact] - public void Serialize_ReturnsCorrectJson_WhenServiceThreadIdIsSet() - { - // Arrange - var thread = new TestServiceIdAgentThread("service-id-789"); - - // Act - var json = thread.Serialize(); - - // Assert - Assert.Equal(JsonValueKind.Object, json.ValueKind); - Assert.True(json.TryGetProperty("serviceThreadId", out var idProperty)); - Assert.Equal("service-id-789", idProperty.GetString()); - } - - [Fact] - public void Serialize_ReturnsUndefinedServiceThreadId_WhenNotSet() - { - // Arrange - var thread = new TestServiceIdAgentThread(); - - // Act - var json = thread.Serialize(); - - // Assert - Assert.Equal(JsonValueKind.Object, json.ValueKind); - Assert.False(json.TryGetProperty("serviceThreadId", out _)); - } - - #endregion - - // Sealed test subclass to expose protected members for testing - private sealed class TestServiceIdAgentThread : ServiceIdAgentThread - { - public TestServiceIdAgentThread() { } - public TestServiceIdAgentThread(string serviceThreadId) : base(serviceThreadId) { } - public TestServiceIdAgentThread(JsonElement serializedThreadState) : base(serializedThreadState) { } - public string? GetServiceThreadId() => this.ServiceThreadId; - } - - // Helper class to represent empty objects - internal sealed class EmptyObject; -} diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/TestJsonSerializerContext.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/TestJsonSerializerContext.cs index 397cd48c86..05d13c2e95 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/TestJsonSerializerContext.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/TestJsonSerializerContext.cs @@ -19,8 +19,8 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests; [JsonSerializable(typeof(Dictionary))] [JsonSerializable(typeof(string[]))] [JsonSerializable(typeof(int))] -[JsonSerializable(typeof(InMemoryAgentThread.InMemoryAgentThreadState))] -[JsonSerializable(typeof(ServiceIdAgentThread.ServiceIdAgentThreadState))] -[JsonSerializable(typeof(ServiceIdAgentThreadTests.EmptyObject))] +[JsonSerializable(typeof(InMemoryAgentSession.InMemoryAgentSessionState))] +[JsonSerializable(typeof(ServiceIdAgentSession.ServiceIdAgentSessionState))] +[JsonSerializable(typeof(ServiceIdAgentSessionTests.EmptyObject))] [JsonSerializable(typeof(InMemoryChatHistoryProviderTests.TestAIContent))] internal sealed partial class TestJsonSerializerContext : JsonSerializerContext; diff --git a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs index 8415b09b25..c26be5822c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs @@ -252,10 +252,10 @@ public sealed class AnthropicBetaServiceExtensionsTests defaultMaxTokens: 8192); // Invoke the agent to trigger the request - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); try { - await agent.RunAsync("Test message", thread); + await agent.RunAsync("Test message", session); } catch { diff --git a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs index cd7112fc5b..4a61a699fc 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs @@ -319,10 +319,10 @@ public sealed class AnthropicClientExtensionsTests defaultMaxTokens: 8192); // Invoke the agent to trigger the request - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); try { - await agent.RunAsync("Test message", thread); + await agent.RunAsync("Test message", session); } catch { diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs index 0c93c72172..483b81fa1d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs @@ -53,12 +53,12 @@ public class AzureAIProjectChatClientTests }); // Act - var thread = await agent.GetNewThreadAsync(); - await agent.RunAsync("Hello", thread); + var session = await agent.GetNewSessionAsync(); + await agent.RunAsync("Hello", session); Assert.True(requestTriggered); - var chatClientThread = Assert.IsType(thread); - Assert.Equal("conv_12345", chatClientThread.ConversationId); + var chatClientSession = Assert.IsType(session); + Assert.Equal("conv_12345", chatClientSession.ConversationId); } /// @@ -102,12 +102,12 @@ public class AzureAIProjectChatClientTests }); // Act - var thread = await agent.GetNewThreadAsync(); - await agent.RunAsync("Hello", thread, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } }); + var session = await agent.GetNewSessionAsync(); + await agent.RunAsync("Hello", session, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } }); Assert.True(requestTriggered); - var chatClientThread = Assert.IsType(thread); - Assert.Equal("conv_12345", chatClientThread.ConversationId); + var chatClientSession = Assert.IsType(session); + Assert.Equal("conv_12345", chatClientSession.ConversationId); } /// @@ -151,12 +151,12 @@ public class AzureAIProjectChatClientTests }); // Act - var thread = await agent.GetNewThreadAsync(); - await agent.RunAsync("Hello", thread, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } }); + var session = await agent.GetNewSessionAsync(); + await agent.RunAsync("Hello", session, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } }); Assert.True(requestTriggered); - var chatClientThread = Assert.IsType(thread); - Assert.Equal("conv_12345", chatClientThread.ConversationId); + var chatClientSession = Assert.IsType(session); + Assert.Equal("conv_12345", chatClientSession.ConversationId); } /// @@ -200,11 +200,11 @@ public class AzureAIProjectChatClientTests }); // Act - var thread = await agent.GetNewThreadAsync(); - await agent.RunAsync("Hello", thread, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "resp_0888a" } }); + var session = await agent.GetNewSessionAsync(); + await agent.RunAsync("Hello", session, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "resp_0888a" } }); Assert.True(requestTriggered); - var chatClientThread = Assert.IsType(thread); - Assert.Equal("resp_0888a46cbf2b1ff3006914596e05d08195a77c3f5187b769a7", chatClientThread.ConversationId); + var chatClientSession = Assert.IsType(session); + Assert.Equal("resp_0888a46cbf2b1ff3006914596e05d08195a77c3f5187b769a7", chatClientSession.ConversationId); } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs index 54bc8ebfed..45f5cf05b6 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs @@ -66,22 +66,22 @@ public sealed class AggregatorPromptAgentFactoryTests private sealed class TestAgent : AIAgent { - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs index f0b5caf9bd..87774d9223 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs @@ -51,11 +51,11 @@ public sealed class AgentEntityTests(ITestOutputHelper outputHelper) : IDisposab // A proxy agent is needed to call the hosted test agent AIAgent simpleAgentProxy = simpleAgent.AsDurableAgentProxy(testHelper.Services); - AgentThread thread = await simpleAgentProxy.GetNewThreadAsync(this.TestTimeoutToken); + AgentSession session = await simpleAgentProxy.GetNewSessionAsync(this.TestTimeoutToken); DurableTaskClient client = testHelper.GetClient(); - AgentSessionId sessionId = thread.GetService(); + AgentSessionId sessionId = session.GetService(); EntityInstanceId expectedEntityId = new($"dafx-{simpleAgent.Name}", sessionId.Key); EntityMetadata? entity = await client.Entities.GetEntityAsync(expectedEntityId, false, this.TestTimeoutToken); @@ -65,7 +65,7 @@ public sealed class AgentEntityTests(ITestOutputHelper outputHelper) : IDisposab // Act: send a prompt to the agent await simpleAgentProxy.RunAsync( message: "Hello!", - thread, + session, cancellationToken: this.TestTimeoutToken); // Assert: verify the agent state was stored with the correct entity name prefix @@ -98,11 +98,11 @@ public sealed class AgentEntityTests(ITestOutputHelper outputHelper) : IDisposab // A proxy agent is needed to call the hosted test agent AIAgent simpleAgentProxy = simpleAgent.AsDurableAgentProxy(testHelper.Services); - AgentThread thread = await simpleAgentProxy.GetNewThreadAsync(this.TestTimeoutToken); + AgentSession session = await simpleAgentProxy.GetNewSessionAsync(this.TestTimeoutToken); DurableTaskClient client = testHelper.GetClient(); - AgentSessionId sessionId = thread.GetService(); + AgentSessionId sessionId = session.GetService(); EntityInstanceId expectedEntityId = new($"dafx-{simpleAgent.Name}", sessionId.Key); EntityMetadata? entity = await client.Entities.GetEntityAsync(expectedEntityId, false, this.TestTimeoutToken); @@ -184,13 +184,13 @@ public sealed class AgentEntityTests(ITestOutputHelper outputHelper) : IDisposab public override async Task RunAsync(TaskOrchestrationContext context, string input) { DurableAIAgent writer = context.GetAgent("TestAgent"); - AgentThread writerThread = await writer.GetNewThreadAsync(); + AgentSession writerSession = await writer.GetNewSessionAsync(); await writer.RunAsync( message: context.GetInput()!, - thread: writerThread); + session: writerSession); - AgentSessionId sessionId = writerThread.GetService(); + AgentSessionId sessionId = writerSession.GetService(); return sessionId.ToString(); } diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs index 9e266dde00..2683047acb 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs @@ -51,15 +51,15 @@ public sealed class ExternalClientTests(ITestOutputHelper outputHelper) : IDispo AIAgent simpleAgentProxy = simpleAgent.AsDurableAgentProxy(testHelper.Services); // Act: send a prompt to the agent and wait for a response - AgentThread thread = await simpleAgentProxy.GetNewThreadAsync(this.TestTimeoutToken); + AgentSession session = await simpleAgentProxy.GetNewSessionAsync(this.TestTimeoutToken); await simpleAgentProxy.RunAsync( message: "Hello!", - thread, + session, cancellationToken: this.TestTimeoutToken); AgentResponse response = await simpleAgentProxy.RunAsync( message: "Repeat what you just said but say it like a pirate", - thread, + session, cancellationToken: this.TestTimeoutToken); // Assert: verify the agent responded appropriately @@ -156,13 +156,13 @@ public sealed class ExternalClientTests(ITestOutputHelper outputHelper) : IDispo { // 1. Get agent and create a session DurableAIAgent agent = context.GetAgent("SimpleAgent"); - AgentThread thread = await agent.GetNewThreadAsync(this.TestTimeoutToken); + AgentSession session = await agent.GetNewSessionAsync(this.TestTimeoutToken); // 2. Call an agent and tell it my name - await agent.RunAsync($"My name is {name}.", thread); + await agent.RunAsync($"My name is {name}.", session); - // 3. Call the agent again with the same thread (ask it to tell me my name) - AgentResponse response = await agent.RunAsync("What is my name?", thread); + // 3. Call the agent again with the same session (ask it to tell me my name) + AgentResponse response = await agent.RunAsync("What is my name?", session); return response.Text; } @@ -194,16 +194,16 @@ public sealed class ExternalClientTests(ITestOutputHelper outputHelper) : IDispo AIAgent workflowManagerAgentProxy = testHelper.Services.GetDurableAgentProxy("WorkflowAgent"); // Act: send a prompt to the agent - AgentThread thread = await workflowManagerAgentProxy.GetNewThreadAsync(this.TestTimeoutToken); + AgentSession session = await workflowManagerAgentProxy.GetNewSessionAsync(this.TestTimeoutToken); await workflowManagerAgentProxy.RunAsync( message: "Start a greeting workflow for \"John Doe\".", - thread, + session, cancellationToken: this.TestTimeoutToken); // Act: prompt it again to wait for the workflow to complete AgentResponse response = await workflowManagerAgentProxy.RunAsync( message: "Wait for the workflow to complete and tell me the result.", - thread, + session, cancellationToken: this.TestTimeoutToken); // Assert: verify the agent responded appropriately diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs index 5437b7cdfa..4bf787adbd 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs @@ -55,14 +55,14 @@ public sealed class TimeToLiveTests(ITestOutputHelper outputHelper) : IDisposabl }); AIAgent agentProxy = simpleAgent.AsDurableAgentProxy(testHelper.Services); - AgentThread thread = await agentProxy.GetNewThreadAsync(this.TestTimeoutToken); + AgentSession session = await agentProxy.GetNewSessionAsync(this.TestTimeoutToken); DurableTaskClient client = testHelper.GetClient(); - AgentSessionId sessionId = thread.GetService(); + AgentSessionId sessionId = session.GetService(); // Act: Send a message to the agent await agentProxy.RunAsync( message: "Hello!", - thread, + session, cancellationToken: this.TestTimeoutToken); // Verify entity exists and get expiration time @@ -120,14 +120,14 @@ public sealed class TimeToLiveTests(ITestOutputHelper outputHelper) : IDisposabl }); AIAgent agentProxy = simpleAgent.AsDurableAgentProxy(testHelper.Services); - AgentThread thread = await agentProxy.GetNewThreadAsync(this.TestTimeoutToken); + AgentSession session = await agentProxy.GetNewSessionAsync(this.TestTimeoutToken); DurableTaskClient client = testHelper.GetClient(); - AgentSessionId sessionId = thread.GetService(); + AgentSessionId sessionId = session.GetService(); // Act: Send first message await agentProxy.RunAsync( message: "Hello!", - thread, + session, cancellationToken: this.TestTimeoutToken); EntityMetadata? entity = await client.Entities.GetEntityAsync(sessionId, true, this.TestTimeoutToken); @@ -143,7 +143,7 @@ public sealed class TimeToLiveTests(ITestOutputHelper outputHelper) : IDisposabl // Send second message (should reset TTL) await agentProxy.RunAsync( message: "Hello again!", - thread, + session, cancellationToken: this.TestTimeoutToken); // Verify expiration time was updated diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/DurableAgentSessionTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/DurableAgentSessionTests.cs new file mode 100644 index 0000000000..218608b179 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/DurableAgentSessionTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Text.Json; + +namespace Microsoft.Agents.AI.DurableTask.UnitTests; + +public sealed class DurableAgentSessionTests +{ + [Fact] + public void BuiltInSerialization() + { + AgentSessionId sessionId = AgentSessionId.WithRandomKey("test-agent"); + AgentSession session = new DurableAgentSession(sessionId); + + JsonElement serializedSession = session.Serialize(); + + // Expected format: "{\"sessionId\":\"@dafx-test-agent@\"}" + string expectedSerializedSession = $"{{\"sessionId\":\"@dafx-{sessionId.Name}@{sessionId.Key}\"}}"; + Assert.Equal(expectedSerializedSession, serializedSession.ToString()); + + DurableAgentSession deserializedSession = DurableAgentSession.Deserialize(serializedSession); + Assert.Equal(sessionId, deserializedSession.SessionId); + } + + [Fact] + public void STJSerialization() + { + AgentSessionId sessionId = AgentSessionId.WithRandomKey("test-agent"); + AgentSession session = new DurableAgentSession(sessionId); + + // Need to specify the type explicitly because STJ, unlike other serializers, + // does serialization based on the static type of the object, not the runtime type. + string serializedSession = JsonSerializer.Serialize(session, typeof(DurableAgentSession)); + + // Expected format: "{\"sessionId\":\"@dafx-test-agent@\"}" + string expectedSerializedSession = $"{{\"sessionId\":\"@dafx-{sessionId.Name}@{sessionId.Key}\"}}"; + Assert.Equal(expectedSerializedSession, serializedSession); + + DurableAgentSession? deserializedSession = JsonSerializer.Deserialize(serializedSession); + Assert.NotNull(deserializedSession); + Assert.Equal(sessionId, deserializedSession.SessionId); + } +} diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/DurableAgentThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/DurableAgentThreadTests.cs deleted file mode 100644 index 7e5a776beb..0000000000 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/DurableAgentThreadTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Text.Json; - -namespace Microsoft.Agents.AI.DurableTask.UnitTests; - -public sealed class DurableAgentThreadTests -{ - [Fact] - public void BuiltInSerialization() - { - AgentSessionId sessionId = AgentSessionId.WithRandomKey("test-agent"); - AgentThread thread = new DurableAgentThread(sessionId); - - JsonElement serializedThread = thread.Serialize(); - - // Expected format: "{\"sessionId\":\"@dafx-test-agent@\"}" - string expectedSerializedThread = $"{{\"sessionId\":\"@dafx-{sessionId.Name}@{sessionId.Key}\"}}"; - Assert.Equal(expectedSerializedThread, serializedThread.ToString()); - - DurableAgentThread deserializedThread = DurableAgentThread.Deserialize(serializedThread); - Assert.Equal(sessionId, deserializedThread.SessionId); - } - - [Fact] - public void STJSerialization() - { - AgentSessionId sessionId = AgentSessionId.WithRandomKey("test-agent"); - AgentThread thread = new DurableAgentThread(sessionId); - - // Need to specify the type explicitly because STJ, unlike other serializers, - // does serialization based on the static type of the object, not the runtime type. - string serializedThread = JsonSerializer.Serialize(thread, typeof(DurableAgentThread)); - - // Expected format: "{\"sessionId\":\"@dafx-test-agent@\"}" - string expectedSerializedThread = $"{{\"sessionId\":\"@dafx-{sessionId.Name}@{sessionId.Key}\"}}"; - Assert.Equal(expectedSerializedThread, serializedThread); - - DurableAgentThread? deserializedThread = JsonSerializer.Deserialize(serializedThread); - Assert.NotNull(deserializedThread); - Assert.Equal(sessionId, deserializedThread.SessionId); - } -} diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/AIAgentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/AIAgentExtensionsTests.cs index 271e80b966..4b9cbce2c4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/AIAgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/AIAgentExtensionsTests.cs @@ -175,15 +175,15 @@ public sealed class AIAgentExtensionsTests { Mock agentMock = new() { CallBase = true }; agentMock.SetupGet(x => x.Name).Returns("TestAgent"); - agentMock.Setup(x => x.GetNewThreadAsync()).ReturnsAsync(new TestAgentThread()); + agentMock.Setup(x => x.GetNewSessionAsync()).ReturnsAsync(new TestAgentSession()); agentMock .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) - .Callback, AgentThread?, AgentRunOptions?, CancellationToken>( + .Callback, AgentSession?, AgentRunOptions?, CancellationToken>( (_, _, options, _) => optionsCallback(options)) .ReturnsAsync(new AgentResponse([new ChatMessage(ChatRole.Assistant, "Test response")])); @@ -194,12 +194,12 @@ public sealed class AIAgentExtensionsTests { Mock agentMock = new() { CallBase = true }; agentMock.SetupGet(x => x.Name).Returns("TestAgent"); - agentMock.Setup(x => x.GetNewThreadAsync()).ReturnsAsync(new TestAgentThread()); + agentMock.Setup(x => x.GetNewSessionAsync()).ReturnsAsync(new TestAgentSession()); agentMock .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(response); @@ -214,5 +214,5 @@ public sealed class AIAgentExtensionsTests return await handler.Invoke(messageSendParams, CancellationToken.None); } - private sealed class TestAgentThread : AgentThread; + private sealed class TestAgentSession : AgentSession; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs index 12d0daffc7..da139ea1a7 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs @@ -31,19 +31,19 @@ public sealed class BasicStreamingTests : IAsyncDisposable await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "hello"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } // Assert - thread.Should().NotBeNull(); + session.Should().NotBeNull(); updates.Should().NotBeEmpty(); updates.Should().AllSatisfy(u => u.Role.Should().Be(ChatRole.Assistant)); @@ -62,13 +62,13 @@ public sealed class BasicStreamingTests : IAsyncDisposable await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "test"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -106,11 +106,11 @@ public sealed class BasicStreamingTests : IAsyncDisposable await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "hello"); // Act - AgentResponse response = await agent.RunAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None); + AgentResponse response = await agent.RunAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None); // Assert response.Messages.Should().NotBeEmpty(); @@ -119,18 +119,18 @@ public sealed class BasicStreamingTests : IAsyncDisposable } [Fact] - public async Task MultiTurnConversationPreservesAllMessagesInThreadAsync() + public async Task MultiTurnConversationPreservesAllMessagesInSessionAsync() { // Arrange await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread chatClientThread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession chatClientSession = (ChatClientAgentSession)await agent.GetNewSessionAsync(); ChatMessage firstUserMessage = new(ChatRole.User, "First question"); // Act - First turn List firstTurnUpdates = []; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([firstUserMessage], chatClientThread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([firstUserMessage], chatClientSession, new AgentRunOptions(), CancellationToken.None)) { firstTurnUpdates.Add(update); } @@ -141,7 +141,7 @@ public sealed class BasicStreamingTests : IAsyncDisposable // Act - Second turn with another message ChatMessage secondUserMessage = new(ChatRole.User, "Second question"); List secondTurnUpdates = []; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([secondUserMessage], chatClientThread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([secondUserMessage], chatClientSession, new AgentRunOptions(), CancellationToken.None)) { secondTurnUpdates.Add(update); } @@ -169,13 +169,13 @@ public sealed class BasicStreamingTests : IAsyncDisposable await this.SetupTestServerAsync(useMultiMessageAgent: true); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread chatClientThread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession chatClientSession = (ChatClientAgentSession)await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Tell me a story"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], chatClientThread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], chatClientSession, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -201,7 +201,7 @@ public sealed class BasicStreamingTests : IAsyncDisposable await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread chatClientThread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession chatClientSession = (ChatClientAgentSession)await agent.GetNewSessionAsync(); // Multiple user messages sent in one turn ChatMessage[] userMessages = @@ -214,7 +214,7 @@ public sealed class BasicStreamingTests : IAsyncDisposable List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(userMessages, chatClientThread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(userMessages, chatClientSession, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -280,20 +280,20 @@ internal sealed class FakeChatClientAgent : AIAgent public override string? Description => "A fake agent for testing"; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions)); protected override async Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { List updates = []; - await foreach (AgentResponseUpdate update in this.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (AgentResponseUpdate update in this.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { updates.Add(update); } @@ -303,7 +303,7 @@ internal sealed class FakeChatClientAgent : AIAgent protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -323,15 +323,15 @@ internal sealed class FakeChatClientAgent : AIAgent } } - private sealed class FakeInMemoryAgentThread : InMemoryAgentThread + private sealed class FakeInMemoryAgentSession : InMemoryAgentSession { - public FakeInMemoryAgentThread() + public FakeInMemoryAgentSession() : base() { } - public FakeInMemoryAgentThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) - : base(serializedThread, jsonSerializerOptions) + public FakeInMemoryAgentSession(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedSession, jsonSerializerOptions) { } } @@ -344,20 +344,20 @@ internal sealed class FakeMultiMessageAgent : AIAgent public override string? Description => "A fake agent that sends multiple messages for testing"; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions)); protected override async Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { List updates = []; - await foreach (AgentResponseUpdate update in this.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false)) + await foreach (AgentResponseUpdate update in this.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false)) { updates.Add(update); } @@ -367,7 +367,7 @@ internal sealed class FakeMultiMessageAgent : AIAgent protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -414,15 +414,15 @@ internal sealed class FakeMultiMessageAgent : AIAgent } } - private sealed class FakeInMemoryAgentThread : InMemoryAgentThread + private sealed class FakeInMemoryAgentSession : InMemoryAgentSession { - public FakeInMemoryAgentThread() + public FakeInMemoryAgentSession() : base() { } - public FakeInMemoryAgentThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) - : base(serializedThread, jsonSerializerOptions) + public FakeInMemoryAgentSession(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedSession, jsonSerializerOptions) { } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs index 2009fdb91b..d66a63b854 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs @@ -35,7 +35,7 @@ public sealed class ForwardedPropertiesTests : IAsyncDisposable // Create request JSON with forwardedProps (per AG-UI protocol spec) const string RequestJson = """ { - "threadId": "thread-123", + "threadId": "session-123", "runId": "run-456", "messages": [{ "id": "msg-1", "role": "user", "content": "test forwarded props" }], "forwardedProps": { "customProp": "customValue", "sessionId": "test-session-123" } @@ -63,7 +63,7 @@ public sealed class ForwardedPropertiesTests : IAsyncDisposable const string RequestJson = """ { - "threadId": "thread-123", + "threadId": "session-123", "runId": "run-456", "messages": [{ "id": "msg-1", "role": "user", "content": "test nested props" }], "forwardedProps": { @@ -100,7 +100,7 @@ public sealed class ForwardedPropertiesTests : IAsyncDisposable const string RequestJson = """ { - "threadId": "thread-123", + "threadId": "session-123", "runId": "run-456", "messages": [{ "id": "msg-1", "role": "user", "content": "test array props" }], "forwardedProps": { @@ -137,7 +137,7 @@ public sealed class ForwardedPropertiesTests : IAsyncDisposable const string RequestJson = """ { - "threadId": "thread-123", + "threadId": "session-123", "runId": "run-456", "messages": [{ "id": "msg-1", "role": "user", "content": "test empty props" }], "forwardedProps": {} @@ -162,7 +162,7 @@ public sealed class ForwardedPropertiesTests : IAsyncDisposable const string RequestJson = """ { - "threadId": "thread-123", + "threadId": "session-123", "runId": "run-456", "messages": [{ "id": "msg-1", "role": "user", "content": "test no props" }] } @@ -187,7 +187,7 @@ public sealed class ForwardedPropertiesTests : IAsyncDisposable const string RequestJson = """ { - "threadId": "thread-123", + "threadId": "session-123", "runId": "run-456", "messages": [{ "id": "msg-1", "role": "user", "content": "test response" }], "forwardedProps": { "customProp": "value" } @@ -233,7 +233,7 @@ public sealed class ForwardedPropertiesTests : IAsyncDisposable const string RequestJson = """ { - "threadId": "thread-123", + "threadId": "session-123", "runId": "run-456", "messages": [{ "id": "msg-1", "role": "user", "content": "test mixed types" }], "forwardedProps": { @@ -303,14 +303,14 @@ internal sealed class FakeForwardedPropsAgent : AIAgent public JsonElement ReceivedForwardedProperties { get; private set; } - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -334,21 +334,21 @@ internal sealed class FakeForwardedPropsAgent : AIAgent await Task.CompletedTask; } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions)); - private sealed class FakeInMemoryAgentThread : InMemoryAgentThread + private sealed class FakeInMemoryAgentSession : InMemoryAgentSession { - public FakeInMemoryAgentThread() + public FakeInMemoryAgentSession() : base() { } - public FakeInMemoryAgentThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) - : base(serializedThread, jsonSerializerOptions) + public FakeInMemoryAgentSession(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedSession, jsonSerializerOptions) { } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs index 14675e4019..b80931db23 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs @@ -34,7 +34,7 @@ public sealed class SharedStateTests : IAsyncDisposable await this.SetupTestServerAsync(fakeAgent); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); string stateJson = JsonSerializer.Serialize(initialState); byte[] stateBytes = System.Text.Encoding.UTF8.GetBytes(stateJson); @@ -45,7 +45,7 @@ public sealed class SharedStateTests : IAsyncDisposable List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -77,7 +77,7 @@ public sealed class SharedStateTests : IAsyncDisposable await this.SetupTestServerAsync(fakeAgent); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); string stateJson = JsonSerializer.Serialize(initialState); byte[] stateBytes = System.Text.Encoding.UTF8.GetBytes(stateJson); @@ -88,7 +88,7 @@ public sealed class SharedStateTests : IAsyncDisposable List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -119,7 +119,7 @@ public sealed class SharedStateTests : IAsyncDisposable await this.SetupTestServerAsync(fakeAgent); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); string stateJson = JsonSerializer.Serialize(complexState); byte[] stateBytes = System.Text.Encoding.UTF8.GetBytes(stateJson); @@ -130,7 +130,7 @@ public sealed class SharedStateTests : IAsyncDisposable List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -159,7 +159,7 @@ public sealed class SharedStateTests : IAsyncDisposable await this.SetupTestServerAsync(fakeAgent); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); string stateJson = JsonSerializer.Serialize(initialState); byte[] stateBytes = System.Text.Encoding.UTF8.GetBytes(stateJson); @@ -170,7 +170,7 @@ public sealed class SharedStateTests : IAsyncDisposable List firstRoundUpdates = []; // Act - First round - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], session, new AgentRunOptions(), CancellationToken.None)) { firstRoundUpdates.Add(update); } @@ -185,7 +185,7 @@ public sealed class SharedStateTests : IAsyncDisposable ChatMessage secondUserMessage = new(ChatRole.User, "increment again"); List secondRoundUpdates = []; - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([secondUserMessage, secondStateMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([secondUserMessage, secondStateMessage], session, new AgentRunOptions(), CancellationToken.None)) { secondRoundUpdates.Add(update); } @@ -210,14 +210,14 @@ public sealed class SharedStateTests : IAsyncDisposable await this.SetupTestServerAsync(fakeAgent); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "hello"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -243,7 +243,7 @@ public sealed class SharedStateTests : IAsyncDisposable await this.SetupTestServerAsync(fakeAgent); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); string stateJson = JsonSerializer.Serialize(emptyState); byte[] stateBytes = System.Text.Encoding.UTF8.GetBytes(stateJson); @@ -254,7 +254,7 @@ public sealed class SharedStateTests : IAsyncDisposable List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage, stateMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -280,7 +280,7 @@ public sealed class SharedStateTests : IAsyncDisposable await this.SetupTestServerAsync(fakeAgent); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Sample assistant", tools: []); - ChatClientAgentThread thread = (ChatClientAgentThread)await agent.GetNewThreadAsync(); + ChatClientAgentSession? session = (ChatClientAgentSession)await agent.GetNewSessionAsync(); string stateJson = JsonSerializer.Serialize(initialState); byte[] stateBytes = System.Text.Encoding.UTF8.GetBytes(stateJson); @@ -289,7 +289,7 @@ public sealed class SharedStateTests : IAsyncDisposable ChatMessage userMessage = new(ChatRole.User, "process"); // Act - AgentResponse response = await agent.RunAsync([userMessage, stateMessage], thread, new AgentRunOptions(), CancellationToken.None); + AgentResponse response = await agent.RunAsync([userMessage, stateMessage], session, new AgentRunOptions(), CancellationToken.None); // Assert response.Should().NotBeNull(); @@ -342,14 +342,14 @@ internal sealed class FakeStateAgent : AIAgent { public override string? Description => "Agent for state testing"; - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -417,21 +417,21 @@ internal sealed class FakeStateAgent : AIAgent await Task.CompletedTask; } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => - new(new FakeInMemoryAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => + new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions)); - private sealed class FakeInMemoryAgentThread : InMemoryAgentThread + private sealed class FakeInMemoryAgentSession : InMemoryAgentSession { - public FakeInMemoryAgentThread() + public FakeInMemoryAgentSession() : base() { } - public FakeInMemoryAgentThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) - : base(serializedThread, jsonSerializerOptions) + public FakeInMemoryAgentSession(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedSession, jsonSerializerOptions) { } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs index 5d5f145733..2257d0920b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs @@ -45,13 +45,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(serverTools: [serverTool]); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: []); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Call the server function"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -93,13 +93,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(serverTools: [getWeatherTool, getTimeTool]); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: []); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "What's the weather and time?"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -134,13 +134,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: [clientTool]); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Call the client function"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -182,13 +182,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: [calculateTool, formatTool]); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Calculate 5 + 3 and format 'hello'"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -233,13 +233,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(serverTools: [serverTool]); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: [clientTool]); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Get both server and client data"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); this._output.WriteLine($"Update: {update.Contents.Count} contents"); @@ -298,13 +298,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(serverTools: [testTool]); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: []); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Call the test function"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -342,13 +342,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(serverTools: [func1, func2], triggerParallelCalls: true); var chatClient = new AGUIChatClient(this._client!, "", null); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: []); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Call both functions in parallel"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -428,13 +428,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(serverTools: [serverTool], jsonSerializerOptions: ServerJsonContext.Default.Options); var chatClient = new AGUIChatClient(this._client!, "", null, ServerJsonContext.Default.Options); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: []); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Get server forecast for Seattle for 5 days"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } @@ -474,13 +474,13 @@ public sealed class ToolCallingTests : IAsyncDisposable await this.SetupTestServerAsync(); var chatClient = new AGUIChatClient(this._client!, "", null, ClientJsonContext.Default.Options); AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "assistant", description: "Test assistant", tools: [clientTool]); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage userMessage = new(ChatRole.User, "Get client forecast for Portland with hourly data"); List updates = []; // Act - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], thread, new AgentRunOptions(), CancellationToken.None)) + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([userMessage], session, new AgentRunOptions(), CancellationToken.None)) { updates.Add(update); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs index a98d76d065..50972d3aa6 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs @@ -355,7 +355,7 @@ public sealed class AGUIEndpointRouteBuilderExtensionsTests } [Fact] - public async Task MapAGUIAgent_ProducesCorrectThreadAndRunIds_InAllEventsAsync() + public async Task MapAGUIAgent_ProducesCorrectSessionAndRunIds_InAllEventsAsync() { // Arrange DefaultHttpContext httpContext = new(); @@ -425,20 +425,20 @@ public sealed class AGUIEndpointRouteBuilderExtensionsTests public override string? Description => "Agent that produces multiple text chunks"; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(new TestInMemoryAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(new TestInMemoryAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => - new(new TestInMemoryAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => + new(new TestInMemoryAgentSession(serializedSession, jsonSerializerOptions)); - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -496,15 +496,15 @@ public sealed class AGUIEndpointRouteBuilderExtensionsTests }; } - private sealed class TestInMemoryAgentThread : InMemoryAgentThread + private sealed class TestInMemoryAgentSession : InMemoryAgentSession { - public TestInMemoryAgentThread() + public TestInMemoryAgentSession() : base() { } - public TestInMemoryAgentThread(JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null) - : base(serializedThreadState, jsonSerializerOptions, null) + public TestInMemoryAgentSession(JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedSessionState, jsonSerializerOptions, null) { } } @@ -515,20 +515,20 @@ public sealed class AGUIEndpointRouteBuilderExtensionsTests public override string? Description => "Test agent"; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(new TestInMemoryAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(new TestInMemoryAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => - new(new TestInMemoryAgentThread(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => + new(new TestInMemoryAgentSession(serializedSession, jsonSerializerOptions)); - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs index 0dccceaa1a..b84dad1f38 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs @@ -73,12 +73,12 @@ public sealed class SamplesValidation(ITestOutputHelper outputHelper) : IAsyncLi Assert.NotEmpty(responseText); this._outputHelper.WriteLine($"Agent run response: {responseText}"); - // The response headers should include the agent thread ID, which can be used to continue the conversation. - string? threadId = response.Headers.GetValues("x-ms-thread-id")?.FirstOrDefault(); - Assert.NotNull(threadId); - Assert.NotEmpty(threadId); + // The response headers should include the agent session ID, which can be used to continue the conversation. + string? sessionId = response.Headers.GetValues("x-ms-thread-id")?.FirstOrDefault(); + Assert.NotNull(sessionId); + Assert.NotEmpty(sessionId); - this._outputHelper.WriteLine($"Agent thread ID: {threadId}"); + this._outputHelper.WriteLine($"Agent session ID: {sessionId}"); // Wait for up to 30 seconds to see if the agent response is available in the logs await this.WaitForConditionAsync( @@ -87,7 +87,7 @@ public sealed class SamplesValidation(ITestOutputHelper outputHelper) : IAsyncLi lock (logs) { bool exists = logs.Any( - log => log.Message.Contains("Response:") && log.Message.Contains(threadId)); + log => log.Message.Contains("Response:") && log.Message.Contains(sessionId)); return Task.FromResult(exists); } }, @@ -286,11 +286,11 @@ public sealed class SamplesValidation(ITestOutputHelper outputHelper) : IAsyncLi string startResponseText = await startResponse.Content.ReadAsStringAsync(); this._outputHelper.WriteLine($"Agent response: {startResponseText}"); - // The response should be deserializable as an AgentResponse object and have a valid thread ID + // The response should be deserializable as an AgentResponse object and have a valid session ID startResponse.Headers.TryGetValues("x-ms-thread-id", out IEnumerable? agentIdValues); - string? threadId = agentIdValues?.FirstOrDefault(); - Assert.NotNull(threadId); - Assert.NotEmpty(threadId); + string? sessionId = agentIdValues?.FirstOrDefault(); + Assert.NotNull(sessionId); + Assert.NotEmpty(sessionId); // Wait for the orchestration to report that it's waiting for human approval await this.WaitForConditionAsync( @@ -308,7 +308,7 @@ public sealed class SamplesValidation(ITestOutputHelper outputHelper) : IAsyncLi timeout: TimeSpan.FromSeconds(60)); // Approve the content - Uri approvalUri = new($"{runAgentUri}?thread_id={threadId}"); + Uri approvalUri = new($"{runAgentUri}?thread_id={sessionId}"); using HttpContent approvalContent = new StringContent("Approve the content", Encoding.UTF8, "text/plain"); using HttpResponseMessage approvalResponse = await s_sharedHttpClient.PostAsync(approvalUri, approvalContent); Assert.True(approvalResponse.IsSuccessStatusCode, $"Approve content request failed with status: {approvalResponse.StatusCode}"); @@ -328,7 +328,7 @@ public sealed class SamplesValidation(ITestOutputHelper outputHelper) : IAsyncLi timeout: TimeSpan.FromSeconds(60)); // Verify the final orchestration status by asking the agent for the status - Uri statusUri = new($"{runAgentUri}?thread_id={threadId}"); + Uri statusUri = new($"{runAgentUri}?thread_id={sessionId}"); await this.WaitForConditionAsync( condition: async () => { diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs index 2f8faa320a..fd5753ed7e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs @@ -11,23 +11,23 @@ internal sealed class TestAgent(string name, string description) : AIAgent public override string? Description => description; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => new(new DummyAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => new(new DummyAgentSession()); - public override ValueTask DeserializeThreadAsync( - JsonElement serializedThread, - JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => new(new DummyAgentThread()); + public override ValueTask DeserializeSessionAsync( + JsonElement serializedSession, + JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => new(new DummyAgentSession()); protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => Task.FromResult(new AgentResponse([.. messages])); protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotSupportedException(); - private sealed class DummyAgentThread : AgentThread; + private sealed class DummyAgentSession : AgentSession; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs index d1d48b33aa..832881857d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs @@ -87,12 +87,12 @@ public sealed class Mem0ProviderTests : IDisposable public async Task InvokingAsync_PerformsSearch_AndReturnsContextMessageAsync() { // Arrange - this._handler.EnqueueJsonResponse("[ { \"id\": \"1\", \"memory\": \"Name is Caoimhe\", \"hash\": \"h\", \"metadata\": null, \"score\": 0.9, \"created_at\": \"2023-01-01T00:00:00Z\", \"updated_at\": null, \"user_id\": \"u\", \"app_id\": null, \"agent_id\": \"agent\", \"session_id\": \"thread\" } ]"); + this._handler.EnqueueJsonResponse("[ { \"id\": \"1\", \"memory\": \"Name is Caoimhe\", \"hash\": \"h\", \"metadata\": null, \"score\": 0.9, \"created_at\": \"2023-01-01T00:00:00Z\", \"updated_at\": null, \"user_id\": \"u\", \"app_id\": null, \"agent_id\": \"agent\", \"thread_id\": \"session\" } ]"); var storageScope = new Mem0ProviderScope { ApplicationId = "app", AgentId = "agent", - ThreadId = "thread", + ThreadId = "session", UserId = "user" }; var sut = new Mem0Provider(this._httpClient, storageScope, options: new() { EnableSensitiveTelemetryData = true }, loggerFactory: this._loggerFactoryMock.Object); @@ -106,7 +106,7 @@ public sealed class Mem0ProviderTests : IDisposable using JsonDocument doc = JsonDocument.Parse(searchRequest.RequestBody); Assert.Equal("app", doc.RootElement.GetProperty("app_id").GetString()); Assert.Equal("agent", doc.RootElement.GetProperty("agent_id").GetString()); - Assert.Equal("thread", doc.RootElement.GetProperty("run_id").GetString()); + Assert.Equal("session", doc.RootElement.GetProperty("run_id").GetString()); Assert.Equal("user", doc.RootElement.GetProperty("user_id").GetString()); Assert.Equal("What is my name?", doc.RootElement.GetProperty("query").GetString()); @@ -148,14 +148,14 @@ public sealed class Mem0ProviderTests : IDisposable } else { - this._handler.EnqueueJsonResponse("[ { \"id\": \"1\", \"memory\": \"Name is Caoimhe\", \"hash\": \"h\", \"metadata\": null, \"score\": 0.9, \"created_at\": \"2023-01-01T00:00:00Z\", \"updated_at\": null, \"user_id\": \"u\", \"app_id\": null, \"agent_id\": \"agent\", \"session_id\": \"thread\" } ]"); + this._handler.EnqueueJsonResponse("[ { \"id\": \"1\", \"memory\": \"Name is Caoimhe\", \"hash\": \"h\", \"metadata\": null, \"score\": 0.9, \"created_at\": \"2023-01-01T00:00:00Z\", \"updated_at\": null, \"user_id\": \"u\", \"app_id\": null, \"agent_id\": \"agent\", \"thread_id\": \"session\" } ]"); } var storageScope = new Mem0ProviderScope { ApplicationId = "app", AgentId = "agent", - ThreadId = "thread", + ThreadId = "session", UserId = "user" }; var options = new Mem0ProviderOptions { EnableSensitiveTelemetryData = enableSensitiveTelemetryData }; @@ -295,14 +295,14 @@ public sealed class Mem0ProviderTests : IDisposable } else { - this._handler.EnqueueJsonResponse("[ { \"id\": \"1\", \"memory\": \"Name is Caoimhe\", \"hash\": \"h\", \"metadata\": null, \"score\": 0.9, \"created_at\": \"2023-01-01T00:00:00Z\", \"updated_at\": null, \"user_id\": \"u\", \"app_id\": null, \"agent_id\": \"agent\", \"session_id\": \"thread\" } ]"); + this._handler.EnqueueJsonResponse("[ { \"id\": \"1\", \"memory\": \"Name is Caoimhe\", \"hash\": \"h\", \"metadata\": null, \"score\": 0.9, \"created_at\": \"2023-01-01T00:00:00Z\", \"updated_at\": null, \"user_id\": \"u\", \"app_id\": null, \"agent_id\": \"agent\", \"thread_id\": \"session\" } ]"); } var storageScope = new Mem0ProviderScope { ApplicationId = "app", AgentId = "agent", - ThreadId = "thread", + ThreadId = "session", UserId = "user" }; @@ -339,7 +339,7 @@ public sealed class Mem0ProviderTests : IDisposable public async Task ClearStoredMemoriesAsync_SendsDeleteWithQueryAsync() { // Arrange - var storageScope = new Mem0ProviderScope { ApplicationId = "app", AgentId = "agent", ThreadId = "thread", UserId = "user" }; + var storageScope = new Mem0ProviderScope { ApplicationId = "app", AgentId = "agent", ThreadId = "session", UserId = "user" }; var sut = new Mem0Provider(this._httpClient, storageScope); this._handler.EnqueueEmptyOk(); // for DELETE @@ -348,14 +348,14 @@ public sealed class Mem0ProviderTests : IDisposable // Assert var delete = Assert.Single(this._handler.Requests, r => r.RequestMessage.Method == HttpMethod.Delete); - Assert.Equal("https://localhost/v1/memories/?app_id=app&agent_id=agent&run_id=thread&user_id=user", delete.RequestMessage.RequestUri!.AbsoluteUri); + Assert.Equal("https://localhost/v1/memories/?app_id=app&agent_id=agent&run_id=session&user_id=user", delete.RequestMessage.RequestUri!.AbsoluteUri); } [Fact] public void Serialize_RoundTripsScopes() { // Arrange - var storageScope = new Mem0ProviderScope { ApplicationId = "app", AgentId = "agent", ThreadId = "thread", UserId = "user" }; + var storageScope = new Mem0ProviderScope { ApplicationId = "app", AgentId = "agent", ThreadId = "session", UserId = "user" }; var sut = new Mem0Provider(this._httpClient, storageScope, options: new() { ContextPrompt = "Custom:" }, loggerFactory: this._loggerFactoryMock.Object); // Act @@ -364,7 +364,7 @@ public sealed class Mem0ProviderTests : IDisposable var storageScopeElement = doc.RootElement.GetProperty("storageScope"); Assert.Equal("app", storageScopeElement.GetProperty("applicationId").GetString()); Assert.Equal("agent", storageScopeElement.GetProperty("agentId").GetString()); - Assert.Equal("thread", storageScopeElement.GetProperty("threadId").GetString()); + Assert.Equal("session", storageScopeElement.GetProperty("threadId").GetString()); Assert.Equal("user", storageScopeElement.GetProperty("userId").GetString()); var sut2 = new Mem0Provider(this._httpClient, stateElement); @@ -375,7 +375,7 @@ public sealed class Mem0ProviderTests : IDisposable var storageScopeElement2 = doc2.RootElement.GetProperty("storageScope"); Assert.Equal("app", storageScopeElement2.GetProperty("applicationId").GetString()); Assert.Equal("agent", storageScopeElement2.GetProperty("agentId").GetString()); - Assert.Equal("thread", storageScopeElement2.GetProperty("threadId").GetString()); + Assert.Equal("session", storageScopeElement2.GetProperty("threadId").GetString()); Assert.Equal("user", storageScopeElement2.GetProperty("userId").GetString()); } diff --git a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs index db22ab090b..b00943e920 100644 --- a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs @@ -65,7 +65,7 @@ public sealed class AIAgentWithOpenAIExtensionsTests { // Arrange var mockAgent = new Mock(); - var mockThread = new Mock(); + var mockSession = new Mock(); var options = new AgentRunOptions(); var cancellationToken = new CancellationToken(false); const string TestMessageText = "Hello, assistant!"; @@ -81,13 +81,13 @@ public sealed class AIAgentWithOpenAIExtensionsTests .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(new AgentResponse([responseMessage])); // Act - var result = await mockAgent.Object.RunAsync(openAiMessages, mockThread.Object, options, cancellationToken); + var result = await mockAgent.Object.RunAsync(openAiMessages, mockSession.Object, options, cancellationToken); // Assert mockAgent.Protected() @@ -96,7 +96,7 @@ public sealed class AIAgentWithOpenAIExtensionsTests ItExpr.Is>(msgs => msgs.ToList().Count == 1 && msgs.ToList()[0].Text == TestMessageText), - mockThread.Object, + mockSession.Object, options, cancellationToken ); @@ -150,7 +150,7 @@ public sealed class AIAgentWithOpenAIExtensionsTests { // Arrange var mockAgent = new Mock(); - var mockThread = new Mock(); + var mockSession = new Mock(); var options = new AgentRunOptions(); var cancellationToken = new CancellationToken(false); const string TestMessageText = "Hello, assistant!"; @@ -171,13 +171,13 @@ public sealed class AIAgentWithOpenAIExtensionsTests .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(responseUpdates)); // Act - var result = mockAgent.Object.RunStreamingAsync(openAiMessages, mockThread.Object, options, cancellationToken); + var result = mockAgent.Object.RunStreamingAsync(openAiMessages, mockSession.Object, options, cancellationToken); var updateCount = 0; await foreach (var update in result) { @@ -191,7 +191,7 @@ public sealed class AIAgentWithOpenAIExtensionsTests ItExpr.Is>(msgs => msgs.ToList().Count == 1 && msgs.ToList()[0].Text == TestMessageText), - mockThread.Object, + mockSession.Object, options, cancellationToken ); @@ -254,7 +254,7 @@ public sealed class AIAgentWithOpenAIExtensionsTests { // Arrange var mockAgent = new Mock(); - var mockThread = new Mock(); + var mockSession = new Mock(); var options = new AgentRunOptions(); var cancellationToken = new CancellationToken(false); const string TestMessageText = "Hello, assistant!"; @@ -267,20 +267,20 @@ public sealed class AIAgentWithOpenAIExtensionsTests .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(new AgentResponse([responseMessage])); // Act - ResponseResult result = await mockAgent.Object.RunAsync(responseItemMessages, mockThread.Object, options, cancellationToken); + ResponseResult result = await mockAgent.Object.RunAsync(responseItemMessages, mockSession.Object, options, cancellationToken); // Assert mockAgent.Protected() .Verify("RunCoreAsync", Times.Once(), ItExpr.IsAny>(), - mockThread.Object, + mockSession.Object, options, cancellationToken ); @@ -329,7 +329,7 @@ public sealed class AIAgentWithOpenAIExtensionsTests { // Arrange var mockAgent = new Mock(); - var mockThread = new Mock(); + var mockSession = new Mock(); var options = new AgentRunOptions(); var cancellationToken = new CancellationToken(false); const string TestMessageText = "Hello, assistant!"; @@ -347,13 +347,13 @@ public sealed class AIAgentWithOpenAIExtensionsTests .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(responseUpdates)); // Act - var result = mockAgent.Object.RunStreamingAsync(responseItemMessages, mockThread.Object, options, cancellationToken); + var result = mockAgent.Object.RunStreamingAsync(responseItemMessages, mockSession.Object, options, cancellationToken); var updateCount = 0; await foreach (var update in result) { @@ -365,7 +365,7 @@ public sealed class AIAgentWithOpenAIExtensionsTests .Verify("RunCoreStreamingAsync", Times.Once(), ItExpr.IsAny>(), - mockThread.Object, + mockSession.Object, options, cancellationToken ); diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs index 316ea14057..b4749c5624 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs @@ -282,7 +282,7 @@ public sealed class PurviewWrapperTests : IDisposable mockAgent.Protected().Verify("RunCoreAsync", Times.Never(), ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()); } @@ -301,7 +301,7 @@ public sealed class PurviewWrapperTests : IDisposable mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(innerResponse); @@ -340,7 +340,7 @@ public sealed class PurviewWrapperTests : IDisposable mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(innerResponse); @@ -383,7 +383,7 @@ public sealed class PurviewWrapperTests : IDisposable mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(expectedResponse); @@ -450,7 +450,7 @@ public sealed class PurviewWrapperTests : IDisposable mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(expectedResponse); @@ -492,12 +492,12 @@ public sealed class PurviewWrapperTests : IDisposable mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(expectedResponse); - string? capturedThreadId = null; + string? capturedSessionId = null; this._mockProcessor.Setup(x => x.ProcessMessagesAsync( It.IsAny>(), It.IsAny(), @@ -506,7 +506,7 @@ public sealed class PurviewWrapperTests : IDisposable It.IsAny(), It.IsAny())) .Callback, string, Activity, PurviewSettings, string, CancellationToken>( - (_, threadId, _, _, _, _) => capturedThreadId = threadId) + (_, threadId, _, _, _, _) => capturedSessionId = threadId) .ReturnsAsync((false, "user-123")); // Act @@ -514,8 +514,8 @@ public sealed class PurviewWrapperTests : IDisposable // Assert Assert.NotNull(result); - Assert.NotNull(capturedThreadId); - Assert.True(Guid.TryParse(capturedThreadId, out _), "Generated thread ID should be a valid GUID"); + Assert.NotNull(capturedSessionId); + Assert.True(Guid.TryParse(capturedSessionId, out _), "Generated session ID should be a valid GUID"); } [Fact] @@ -532,7 +532,7 @@ public sealed class PurviewWrapperTests : IDisposable mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(innerResponse); diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs index 3dfd5c9b88..3527cc9884 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs @@ -88,7 +88,7 @@ public sealed class ScopedContentProcessorTests // Act var result = await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, "user-123", CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, "user-123", CancellationToken.None); // Assert Assert.True(result.shouldBlock); @@ -147,7 +147,7 @@ public sealed class ScopedContentProcessorTests // Act var result = await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, "user-123", CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, "user-123", CancellationToken.None); // Assert Assert.True(result.shouldBlock); @@ -206,7 +206,7 @@ public sealed class ScopedContentProcessorTests // Act var result = await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, "user-123", CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, "user-123", CancellationToken.None); // Assert Assert.False(result.shouldBlock); @@ -258,7 +258,7 @@ public sealed class ScopedContentProcessorTests // Act await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, "user-123", CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, "user-123", CancellationToken.None); // Assert this._mockPurviewClient.Verify(x => x.GetProtectionScopesAsync( @@ -315,7 +315,7 @@ public sealed class ScopedContentProcessorTests // Act await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, "user-123", CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, "user-123", CancellationToken.None); // Assert this._mockCacheProvider.Verify(x => x.RemoveAsync( @@ -361,7 +361,7 @@ public sealed class ScopedContentProcessorTests // Act await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, "user-123", CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, "user-123", CancellationToken.None); // Assert // Content activities are now queued as background jobs, not called directly @@ -386,7 +386,7 @@ public sealed class ScopedContentProcessorTests // Act & Assert var exception = await Assert.ThrowsAsync(() => - this._processor.ProcessMessagesAsync(messages, "thread-123", Activity.UploadText, settings, "user-123", CancellationToken.None)); + this._processor.ProcessMessagesAsync(messages, "session-123", Activity.UploadText, settings, "user-123", CancellationToken.None)); Assert.Contains("No tenant id provided or inferred", exception.Message); } @@ -407,7 +407,7 @@ public sealed class ScopedContentProcessorTests // Act & Assert var exception = await Assert.ThrowsAsync(() => - this._processor.ProcessMessagesAsync(messages, "thread-123", Activity.UploadText, settings, null, CancellationToken.None)); + this._processor.ProcessMessagesAsync(messages, "session-123", Activity.UploadText, settings, null, CancellationToken.None)); Assert.Contains("No user id provided or inferred", exception.Message); } @@ -443,7 +443,7 @@ public sealed class ScopedContentProcessorTests // Act var result = await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, null, CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, null, CancellationToken.None); // Assert Assert.Equal("user-from-props", result.userId); @@ -478,7 +478,7 @@ public sealed class ScopedContentProcessorTests // Act var result = await this._processor.ProcessMessagesAsync( - messages, "thread-123", Activity.UploadText, settings, null, CancellationToken.None); + messages, "session-123", Activity.UploadText, settings, null, CancellationToken.None); // Assert Assert.Equal(userId, result.userId); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AIAgentBuilderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AIAgentBuilderTests.cs index 48b2475a2b..5cb3858fbc 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AIAgentBuilderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AIAgentBuilderTests.cs @@ -328,7 +328,7 @@ public class AIAgentBuilderTests // Act & Assert Assert.Throws("sharedFunc", () => - builder.Use((Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>)null!)); + builder.Use((Func, AgentSession?, AgentRunOptions?, Func, AgentSession?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>)null!)); } /// diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs index e0c5417674..eea65444b8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs @@ -382,10 +382,10 @@ public class AgentExtensionsTests this._exceptionToThrow = exceptionToThrow; } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); public override string? Name { get; } @@ -398,7 +398,7 @@ public class AgentExtensionsTests protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -417,11 +417,11 @@ public class AgentExtensionsTests protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var response = await this.RunAsync(messages, thread, options, cancellationToken); + var response = await this.RunAsync(messages, session, options, cancellationToken); foreach (var update in response.ToAgentResponseUpdates()) { yield return update; diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs index 43937a1148..069fb9293f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs @@ -19,7 +19,7 @@ public class AnonymousDelegatingAIAgentTests { private readonly Mock _innerAgentMock; private readonly List _testMessages; - private readonly AgentThread _testThread; + private readonly AgentSession _testSession; private readonly AgentRunOptions _testOptions; private readonly AgentResponse _testResponse; private readonly AgentResponseUpdate[] _testStreamingResponses; @@ -28,7 +28,7 @@ public class AnonymousDelegatingAIAgentTests { this._innerAgentMock = new Mock(); this._testMessages = [new ChatMessage(ChatRole.User, "Test message")]; - this._testThread = new Mock().Object; + this._testSession = new Mock().Object; this._testOptions = new AgentRunOptions(); this._testResponse = new AgentResponse([new ChatMessage(ChatRole.Assistant, "Test response")]); this._testStreamingResponses = [ @@ -40,7 +40,7 @@ public class AnonymousDelegatingAIAgentTests .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(this._testResponse); @@ -49,7 +49,7 @@ public class AnonymousDelegatingAIAgentTests .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._testStreamingResponses)); @@ -165,27 +165,27 @@ public class AnonymousDelegatingAIAgentTests { // Arrange IEnumerable? capturedMessages = null; - AgentThread? capturedThread = null; + AgentSession? capturedSession = null; AgentRunOptions? capturedOptions = null; CancellationToken capturedCancellationToken = default; var expectedCancellationToken = new CancellationToken(true); var agent = new AnonymousDelegatingAIAgent(this._innerAgentMock.Object, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { capturedMessages = messages; - capturedThread = thread; + capturedSession = session; capturedOptions = options; capturedCancellationToken = cancellationToken; - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); }); // Act - await agent.RunAsync(this._testMessages, this._testThread, this._testOptions, expectedCancellationToken); + await agent.RunAsync(this._testMessages, this._testSession, this._testOptions, expectedCancellationToken); // Assert Assert.Same(this._testMessages, capturedMessages); - Assert.Same(this._testThread, capturedThread); + Assert.Same(this._testSession, capturedSession); Assert.Same(this._testOptions, capturedOptions); Assert.Equal(expectedCancellationToken, capturedCancellationToken); @@ -194,7 +194,7 @@ public class AnonymousDelegatingAIAgentTests .Verify>("RunCoreAsync", Times.Once(), ItExpr.Is>(m => m == this._testMessages), - ItExpr.Is(t => t == this._testThread), + ItExpr.Is(t => t == this._testSession), ItExpr.Is(o => o == this._testOptions), ItExpr.Is(ct => ct == expectedCancellationToken)); } @@ -208,15 +208,15 @@ public class AnonymousDelegatingAIAgentTests // Arrange var callCount = 0; var agent = new AnonymousDelegatingAIAgent(this._innerAgentMock.Object, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { callCount++; - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); }); // Act - await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); - var streamingResults = await agent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions).ToListAsync(); + await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); + var streamingResults = await agent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions).ToListAsync(); // Assert Assert.Equal(2, callCount); @@ -238,15 +238,15 @@ public class AnonymousDelegatingAIAgentTests var runFuncCalled = false; var agent = new AnonymousDelegatingAIAgent( this._innerAgentMock.Object, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { runFuncCalled = true; - return innerAgent.RunAsync(messages, thread, options, cancellationToken); + return innerAgent.RunAsync(messages, session, options, cancellationToken); }, null); // Act - var result = await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); + var result = await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert Assert.True(runFuncCalled); @@ -263,15 +263,15 @@ public class AnonymousDelegatingAIAgentTests var runFuncCalled = false; var agent = new AnonymousDelegatingAIAgent( this._innerAgentMock.Object, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { runFuncCalled = true; - return innerAgent.RunAsync(messages, thread, options, cancellationToken); + return innerAgent.RunAsync(messages, session, options, cancellationToken); }, null); // Act - var results = await agent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions).ToListAsync(); + var results = await agent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions).ToListAsync(); // Assert Assert.True(runFuncCalled); @@ -289,14 +289,14 @@ public class AnonymousDelegatingAIAgentTests var agent = new AnonymousDelegatingAIAgent( this._innerAgentMock.Object, null, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { streamingFuncCalled = true; - return innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); + return innerAgent.RunStreamingAsync(messages, session, options, cancellationToken); }); // Act - var result = await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); + var result = await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert Assert.True(streamingFuncCalled); @@ -314,14 +314,14 @@ public class AnonymousDelegatingAIAgentTests var agent = new AnonymousDelegatingAIAgent( this._innerAgentMock.Object, null, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { streamingFuncCalled = true; - return innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); + return innerAgent.RunStreamingAsync(messages, session, options, cancellationToken); }); // Act - var results = await agent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions).ToListAsync(); + var results = await agent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions).ToListAsync(); // Assert Assert.True(streamingFuncCalled); @@ -340,20 +340,20 @@ public class AnonymousDelegatingAIAgentTests var agent = new AnonymousDelegatingAIAgent( this._innerAgentMock.Object, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { runFuncCalled = true; - return innerAgent.RunAsync(messages, thread, options, cancellationToken); + return innerAgent.RunAsync(messages, session, options, cancellationToken); }, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { streamingFuncCalled = true; - return innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); + return innerAgent.RunStreamingAsync(messages, session, options, cancellationToken); }); // Act - await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); - await agent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions).ToListAsync(); + await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); + await agent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions).ToListAsync(); // Assert Assert.True(runFuncCalled); @@ -377,7 +377,7 @@ public class AnonymousDelegatingAIAgentTests // Act & Assert var actualException = await Assert.ThrowsAsync( - () => agent.RunAsync(this._testMessages, this._testThread, this._testOptions)); + () => agent.RunAsync(this._testMessages, this._testSession, this._testOptions)); Assert.Same(expectedException, actualException); } @@ -397,7 +397,7 @@ public class AnonymousDelegatingAIAgentTests // Act & Assert var actualException = await Assert.ThrowsAsync( - () => agent.RunAsync(this._testMessages, this._testThread, this._testOptions)); + () => agent.RunAsync(this._testMessages, this._testSession, this._testOptions)); Assert.Same(expectedException, actualException); } @@ -418,7 +418,7 @@ public class AnonymousDelegatingAIAgentTests // Act & Assert var actualException = await Assert.ThrowsAsync(async () => { - await foreach (var _ in agent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions)) + await foreach (var _ in agent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions)) { // Should throw before yielding any items } @@ -439,7 +439,7 @@ public class AnonymousDelegatingAIAgentTests // Act & Assert var exception = await Assert.ThrowsAsync( - () => agent.RunAsync(this._testMessages, this._testThread, this._testOptions)); + () => agent.RunAsync(this._testMessages, this._testSession, this._testOptions)); Assert.Contains("without producing an AgentResponse", exception.Message); } @@ -459,10 +459,10 @@ public class AnonymousDelegatingAIAgentTests var capturedValue = 0; var agent = new AnonymousDelegatingAIAgent(this._innerAgentMock.Object, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { asyncLocal.Value = 42; - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); capturedValue = asyncLocal.Value; }); @@ -470,7 +470,7 @@ public class AnonymousDelegatingAIAgentTests .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(() => @@ -482,7 +482,7 @@ public class AnonymousDelegatingAIAgentTests // Act Assert.Equal(0, asyncLocal.Value); // Initial value - await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); + await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert Assert.Equal(0, asyncLocal.Value); // Should be reset after call @@ -503,31 +503,31 @@ public class AnonymousDelegatingAIAgentTests var executionOrder = new List(); var outerAgent = new AnonymousDelegatingAIAgent(this._innerAgentMock.Object, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Outer-Pre"); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("Outer-Post"); }); var middleAgent = new AnonymousDelegatingAIAgent(outerAgent, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Middle-Pre"); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("Middle-Post"); }); var innerAgent = new AnonymousDelegatingAIAgent(middleAgent, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Inner-Pre"); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("Inner-Post"); }); // Act - await innerAgent.RunAsync(this._testMessages, this._testThread, this._testOptions); + await innerAgent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert var expectedOrder = new[] { "Inner-Pre", "Middle-Pre", "Outer-Pre", "Outer-Post", "Middle-Post", "Inner-Post" }; @@ -544,32 +544,32 @@ public class AnonymousDelegatingAIAgentTests var executionOrder = new List(); var outerAgent = new AnonymousDelegatingAIAgent(this._innerAgentMock.Object, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { executionOrder.Add("Outer-Run"); - return innerAgent.RunAsync(messages, thread, options, cancellationToken); + return innerAgent.RunAsync(messages, session, options, cancellationToken); }, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { executionOrder.Add("Outer-Streaming"); - return innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); + return innerAgent.RunStreamingAsync(messages, session, options, cancellationToken); }); var middleAgent = new AnonymousDelegatingAIAgent(outerAgent, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { executionOrder.Add("Middle-Run"); - return innerAgent.RunAsync(messages, thread, options, cancellationToken); + return innerAgent.RunAsync(messages, session, options, cancellationToken); }, - (messages, thread, options, innerAgent, cancellationToken) => + (messages, session, options, innerAgent, cancellationToken) => { executionOrder.Add("Middle-Streaming"); - return innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); + return innerAgent.RunStreamingAsync(messages, session, options, cancellationToken); }); // Act - await middleAgent.RunAsync(this._testMessages, this._testThread, this._testOptions); - await middleAgent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions).ToListAsync(); + await middleAgent.RunAsync(this._testMessages, this._testSession, this._testOptions); + await middleAgent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions).ToListAsync(); // Assert Assert.Contains("Middle-Run", executionOrder); @@ -597,24 +597,24 @@ public class AnonymousDelegatingAIAgentTests var executionOrder = new List(); var outerAgent = new AnonymousDelegatingAIAgent(this._innerAgentMock.Object, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Outer-Pre"); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("Outer-Post"); }); var innerAgent = new AnonymousDelegatingAIAgent(outerAgent, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Inner-Pre"); capturedOptions.Add(options); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("Inner-Post"); }); // Act - await innerAgent.RunAsync(this._testMessages, this._testThread, this._testOptions); + await innerAgent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert Assert.Single(capturedOptions); @@ -639,25 +639,25 @@ public class AnonymousDelegatingAIAgentTests var innerExecuted = false; var outerAgent = new AnonymousDelegatingAIAgent(this._innerAgentMock.Object, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { outerExecuted = true; - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); }); var middleAgent = new AnonymousDelegatingAIAgent(outerAgent, (_, _, _, _, _) => throw expectedException); var innerAgent = new AnonymousDelegatingAIAgent(middleAgent, - async (messages, thread, options, next, cancellationToken) => + async (messages, session, options, next, cancellationToken) => { innerExecuted = true; - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); }); // Act & Assert var actualException = await Assert.ThrowsAsync( - () => innerAgent.RunAsync(this._testMessages, this._testThread, this._testOptions)); + () => innerAgent.RunAsync(this._testMessages, this._testSession, this._testOptions)); Assert.Same(expectedException, actualException); Assert.True(innerExecuted); // Inner middleware should execute @@ -679,13 +679,13 @@ public class AnonymousDelegatingAIAgentTests var innerAgent = new AnonymousDelegatingAIAgent(outerAgent, null, - (messages, thread, options, innerAgent, cancellationToken) => - innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken)); + (messages, session, options, innerAgent, cancellationToken) => + innerAgent.RunStreamingAsync(messages, session, options, cancellationToken)); // Act & Assert var actualException = await Assert.ThrowsAsync(async () => { - await foreach (var _ in innerAgent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions)) + await foreach (var _ in innerAgent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions)) { // Should throw before yielding any items } @@ -708,22 +708,22 @@ public class AnonymousDelegatingAIAgentTests var executionOrder = new List(); var agent = new AIAgentBuilder(this._innerAgentMock.Object) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { executionOrder.Add("First-Pre"); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("First-Post"); }) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Second-Pre"); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("Second-Post"); }) .Build(); // Act - await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); + await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert var expectedOrder = new[] { "First-Pre", "Second-Pre", "Second-Post", "First-Post" }; @@ -741,12 +741,12 @@ public class AnonymousDelegatingAIAgentTests var streamingExecutionOrder = new List(); static async IAsyncEnumerable FirstStreamingMiddlewareAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, + IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, [EnumeratorCancellation] CancellationToken cancellationToken, List executionOrder) { executionOrder.Add("First-Streaming-Pre"); - await foreach (var update in innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken)) + await foreach (var update in innerAgent.RunStreamingAsync(messages, session, options, cancellationToken)) { yield return update; } @@ -754,12 +754,12 @@ public class AnonymousDelegatingAIAgentTests } static async IAsyncEnumerable SecondStreamingMiddlewareAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, + IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, [EnumeratorCancellation] CancellationToken cancellationToken, List executionOrder) { executionOrder.Add("Second-Streaming-Pre"); - await foreach (var update in innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken)) + await foreach (var update in innerAgent.RunStreamingAsync(messages, session, options, cancellationToken)) { yield return update; } @@ -768,30 +768,30 @@ public class AnonymousDelegatingAIAgentTests var agent = new AIAgentBuilder(this._innerAgentMock.Object) .Use( - async (messages, thread, options, innerAgent, cancellationToken) => + async (messages, session, options, innerAgent, cancellationToken) => { runExecutionOrder.Add("First-Run-Pre"); - var result = await innerAgent.RunAsync(messages, thread, options, cancellationToken); + var result = await innerAgent.RunAsync(messages, session, options, cancellationToken); runExecutionOrder.Add("First-Run-Post"); return result; }, - (messages, thread, options, innerAgent, cancellationToken) => - FirstStreamingMiddlewareAsync(messages, thread, options, innerAgent, cancellationToken, streamingExecutionOrder)) + (messages, session, options, innerAgent, cancellationToken) => + FirstStreamingMiddlewareAsync(messages, session, options, innerAgent, cancellationToken, streamingExecutionOrder)) .Use( - async (messages, thread, options, innerAgent, cancellationToken) => + async (messages, session, options, innerAgent, cancellationToken) => { runExecutionOrder.Add("Second-Run-Pre"); - var result = await innerAgent.RunAsync(messages, thread, options, cancellationToken); + var result = await innerAgent.RunAsync(messages, session, options, cancellationToken); runExecutionOrder.Add("Second-Run-Post"); return result; }, - (messages, thread, options, innerAgent, cancellationToken) => - SecondStreamingMiddlewareAsync(messages, thread, options, innerAgent, cancellationToken, streamingExecutionOrder)) + (messages, session, options, innerAgent, cancellationToken) => + SecondStreamingMiddlewareAsync(messages, session, options, innerAgent, cancellationToken, streamingExecutionOrder)) .Build(); // Act - await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); - await agent.RunStreamingAsync(this._testMessages, this._testThread, this._testOptions).ToListAsync(); + await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); + await agent.RunStreamingAsync(this._testMessages, this._testSession, this._testOptions).ToListAsync(); // Assert var expectedRunOrder = new[] { "First-Run-Pre", "Second-Run-Pre", "Second-Run-Post", "First-Run-Post" }; @@ -812,24 +812,24 @@ public class AnonymousDelegatingAIAgentTests AgentRunOptions? capturedOptions = null; var agent = new AIAgentBuilder(this._innerAgentMock.Object) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { // Modify messages and options var modifiedMessages = messages.Concat([new ChatMessage(ChatRole.System, "Added by first middleware")]); var modifiedOptions = new AgentRunOptions(); - await next(modifiedMessages, thread, modifiedOptions, cancellationToken); + await next(modifiedMessages, session, modifiedOptions, cancellationToken); }) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { // Capture what the second middleware receives capturedMessages = messages; capturedOptions = options; - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); }) .Build(); // Act - await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); + await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert Assert.NotNull(capturedMessages); @@ -853,12 +853,12 @@ public class AnonymousDelegatingAIAgentTests var executionOrder = new List(); var agent = new AIAgentBuilder(this._innerAgentMock.Object) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { executionOrder.Add("First-Pre"); try { - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); executionOrder.Add("First-Post-Success"); } catch @@ -867,7 +867,7 @@ public class AnonymousDelegatingAIAgentTests throw; } }) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Second-Pre"); throw expectedException; @@ -876,7 +876,7 @@ public class AnonymousDelegatingAIAgentTests // Act & Assert var actualException = await Assert.ThrowsAsync( - () => agent.RunAsync(this._testMessages, this._testThread, this._testOptions)); + () => agent.RunAsync(this._testMessages, this._testSession, this._testOptions)); Assert.Same(expectedException, actualException); var expectedOrder = new[] { "First-Pre", "Second-Pre", "First-Post-Exception" }; @@ -895,12 +895,12 @@ public class AnonymousDelegatingAIAgentTests var agent = new AIAgentBuilder(this._innerAgentMock.Object) .Use( - async (messages, thread, options, innerAgent, cancellationToken) => + async (messages, session, options, innerAgent, cancellationToken) => { executionOrder.Add("Handler-Pre"); try { - return await innerAgent.RunAsync(messages, thread, options, cancellationToken); + return await innerAgent.RunAsync(messages, session, options, cancellationToken); } catch (InvalidOperationException) { @@ -909,7 +909,7 @@ public class AnonymousDelegatingAIAgentTests } }, null) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { executionOrder.Add("Throwing-Pre"); throw new InvalidOperationException("Simulated error"); @@ -917,7 +917,7 @@ public class AnonymousDelegatingAIAgentTests .Build(); // Act - var result = await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); + var result = await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert Assert.Same(fallbackResponse, result); @@ -940,27 +940,27 @@ public class AnonymousDelegatingAIAgentTests .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.Is(ct => ct.IsCancellationRequested)) .ThrowsAsync(new OperationCanceledException()); var agent = new AIAgentBuilder(this._innerAgentMock.Object) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { capturedTokens.Add(cancellationToken); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); }) - .Use(async (messages, thread, options, next, cancellationToken) => + .Use(async (messages, session, options, next, cancellationToken) => { capturedTokens.Add(cancellationToken); - await next(messages, thread, options, cancellationToken); + await next(messages, session, options, cancellationToken); }) .Build(); // Act & Assert await Assert.ThrowsAsync( - () => agent.RunAsync(this._testMessages, this._testThread, this._testOptions, expectedToken)); + () => agent.RunAsync(this._testMessages, this._testSession, this._testOptions, expectedToken)); Assert.All(capturedTokens, token => Assert.Equal(expectedToken, token)); Assert.Equal(2, capturedTokens.Count); @@ -978,16 +978,16 @@ public class AnonymousDelegatingAIAgentTests var agent = new AIAgentBuilder(this._innerAgentMock.Object) .Use( - async (messages, thread, options, innerAgent, cancellationToken) => + async (messages, session, options, innerAgent, cancellationToken) => { executionOrder.Add("First-Pre"); - var result = await innerAgent.RunAsync(messages, thread, options, cancellationToken); + var result = await innerAgent.RunAsync(messages, session, options, cancellationToken); executionOrder.Add("First-Post"); return result; }, null) .Use( - async (messages, thread, options, innerAgent, cancellationToken) => + async (messages, session, options, innerAgent, cancellationToken) => { executionOrder.Add("Second-ShortCircuit"); // Don't call inner agent - short circuit the chain @@ -997,7 +997,7 @@ public class AnonymousDelegatingAIAgentTests .Build(); // Act - var result = await agent.RunAsync(this._testMessages, this._testThread, this._testOptions); + var result = await agent.RunAsync(this._testMessages, this._testSession, this._testOptions); // Assert Assert.Same(shortCircuitResponse, result); @@ -1010,7 +1010,7 @@ public class AnonymousDelegatingAIAgentTests .Verify>("RunCoreAsync", Times.Never(), ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()); } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentSessionTests.cs similarity index 72% rename from dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentThreadTests.cs rename to dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentSessionTests.cs index ef5eb19c37..4001b59090 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentThreadTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentSessionTests.cs @@ -12,7 +12,7 @@ using Moq; namespace Microsoft.Agents.AI.UnitTests; -public class ChatClientAgentThreadTests +public class ChatClientAgentSessionTests { #region Constructor and Property Tests @@ -20,72 +20,72 @@ public class ChatClientAgentThreadTests public void ConstructorSetsDefaults() { // Arrange & Act - var thread = new ChatClientAgentThread(); + var session = new ChatClientAgentSession(); // Assert - Assert.Null(thread.ConversationId); - Assert.Null(thread.ChatHistoryProvider); + Assert.Null(session.ConversationId); + Assert.Null(session.ChatHistoryProvider); } [Fact] public void SetConversationIdRoundtrips() { // Arrange - var thread = new ChatClientAgentThread(); - const string ConversationId = "test-thread-id"; + var session = new ChatClientAgentSession(); + const string ConversationId = "test-session-id"; // Act - thread.ConversationId = ConversationId; + session.ConversationId = ConversationId; // Assert - Assert.Equal(ConversationId, thread.ConversationId); - Assert.Null(thread.ChatHistoryProvider); + Assert.Equal(ConversationId, session.ConversationId); + Assert.Null(session.ChatHistoryProvider); } [Fact] public void SetChatHistoryProviderRoundtrips() { // Arrange - var thread = new ChatClientAgentThread(); + var session = new ChatClientAgentSession(); var chatHistoryProvider = new InMemoryChatHistoryProvider(); // Act - thread.ChatHistoryProvider = chatHistoryProvider; + session.ChatHistoryProvider = chatHistoryProvider; // Assert - Assert.Same(chatHistoryProvider, thread.ChatHistoryProvider); - Assert.Null(thread.ConversationId); + Assert.Same(chatHistoryProvider, session.ChatHistoryProvider); + Assert.Null(session.ConversationId); } [Fact] public void SetConversationIdThrowsWhenChatHistoryProviderIsSet() { // Arrange - var thread = new ChatClientAgentThread + var session = new ChatClientAgentSession { ChatHistoryProvider = new InMemoryChatHistoryProvider() }; // Act & Assert - var exception = Assert.Throws(() => thread.ConversationId = "new-thread-id"); + var exception = Assert.Throws(() => session.ConversationId = "new-session-id"); Assert.Equal("Only the ConversationId or ChatHistoryProvider may be set, but not both and switching from one to another is not supported.", exception.Message); - Assert.NotNull(thread.ChatHistoryProvider); + Assert.NotNull(session.ChatHistoryProvider); } [Fact] public void SetChatHistoryProviderThrowsWhenConversationIdIsSet() { // Arrange - var thread = new ChatClientAgentThread + var session = new ChatClientAgentSession { - ConversationId = "existing-thread-id" + ConversationId = "existing-session-id" }; var provider = new InMemoryChatHistoryProvider(); // Act & Assert - var exception = Assert.Throws(() => thread.ChatHistoryProvider = provider); + var exception = Assert.Throws(() => session.ChatHistoryProvider = provider); Assert.Equal("Only the ConversationId or ChatHistoryProvider may be set, but not both and switching from one to another is not supported.", exception.Message); - Assert.NotNull(thread.ConversationId); + Assert.NotNull(session.ConversationId); } #endregion Constructor and Property Tests @@ -103,12 +103,12 @@ public class ChatClientAgentThreadTests """, TestJsonSerializerContext.Default.JsonElement); // Act. - var thread = await ChatClientAgentThread.DeserializeAsync(json); + var session = await ChatClientAgentSession.DeserializeAsync(json); // Assert - Assert.Null(thread.ConversationId); + Assert.Null(session.ConversationId); - var chatHistoryProvider = thread.ChatHistoryProvider as InMemoryChatHistoryProvider; + var chatHistoryProvider = session.ChatHistoryProvider as InMemoryChatHistoryProvider; Assert.NotNull(chatHistoryProvider); Assert.Single(chatHistoryProvider); Assert.Equal("testAuthor", chatHistoryProvider[0].AuthorName); @@ -125,11 +125,11 @@ public class ChatClientAgentThreadTests """, TestJsonSerializerContext.Default.JsonElement); // Act - var thread = await ChatClientAgentThread.DeserializeAsync(json); + var session = await ChatClientAgentSession.DeserializeAsync(json); // Assert - Assert.Equal("TestConvId", thread.ConversationId); - Assert.Null(thread.ChatHistoryProvider); + Assert.Equal("TestConvId", session.ConversationId); + Assert.Null(session.ChatHistoryProvider); } [Fact] @@ -145,11 +145,11 @@ public class ChatClientAgentThreadTests Mock mockProvider = new(); // Act - var thread = await ChatClientAgentThread.DeserializeAsync(json, aiContextProviderFactory: (_, _, _) => new(mockProvider.Object)); + var session = await ChatClientAgentSession.DeserializeAsync(json, aiContextProviderFactory: (_, _, _) => new(mockProvider.Object)); // Assert - Assert.Null(thread.ChatHistoryProvider); - Assert.Same(thread.AIContextProvider, mockProvider.Object); + Assert.Null(session.ChatHistoryProvider); + Assert.Same(session.AIContextProvider, mockProvider.Object); } [Fact] @@ -157,10 +157,10 @@ public class ChatClientAgentThreadTests { // Arrange var invalidJson = JsonSerializer.Deserialize("[42]", TestJsonSerializerContext.Default.JsonElement); - var thread = new ChatClientAgentThread(); + var session = new ChatClientAgentSession(); // Act & Assert - await Assert.ThrowsAsync(() => ChatClientAgentThread.DeserializeAsync(invalidJson)); + await Assert.ThrowsAsync(() => ChatClientAgentSession.DeserializeAsync(invalidJson)); } #endregion Deserialize Tests @@ -168,16 +168,16 @@ public class ChatClientAgentThreadTests #region Serialize Tests /// - /// Verify thread serialization to JSON when the thread has an id. + /// Verify session serialization to JSON when the session has an id. /// [Fact] - public void VerifyThreadSerializationWithId() + public void VerifySessionSerializationWithId() { // Arrange - var thread = new ChatClientAgentThread { ConversationId = "TestConvId" }; + var session = new ChatClientAgentSession { ConversationId = "TestConvId" }; // Act - var json = thread.Serialize(); + var json = session.Serialize(); // Assert Assert.Equal(JsonValueKind.Object, json.ValueKind); @@ -189,17 +189,17 @@ public class ChatClientAgentThreadTests } /// - /// Verify thread serialization to JSON when the thread has messages. + /// Verify session serialization to JSON when the session has messages. /// [Fact] - public void VerifyThreadSerializationWithMessages() + public void VerifySessionSerializationWithMessages() { // Arrange InMemoryChatHistoryProvider provider = [new(ChatRole.User, "TestContent") { AuthorName = "TestAuthor" }]; - var thread = new ChatClientAgentThread { ChatHistoryProvider = provider }; + var session = new ChatClientAgentSession { ChatHistoryProvider = provider }; // Act - var json = thread.Serialize(); + var json = session.Serialize(); // Assert Assert.Equal(JsonValueKind.Object, json.ValueKind); @@ -224,7 +224,7 @@ public class ChatClientAgentThreadTests } [Fact] - public void VerifyThreadSerializationWithWithAIContextProvider() + public void VerifySessionSerializationWithWithAIContextProvider() { // Arrange Mock mockProvider = new(); @@ -232,13 +232,13 @@ public class ChatClientAgentThreadTests .Setup(m => m.Serialize(It.IsAny())) .Returns(JsonSerializer.SerializeToElement(["CP1"], TestJsonSerializerContext.Default.StringArray)); - var thread = new ChatClientAgentThread + var session = new ChatClientAgentSession { AIContextProvider = mockProvider.Object }; // Act - var json = thread.Serialize(); + var json = session.Serialize(); // Assert Assert.Equal(JsonValueKind.Object, json.ValueKind); @@ -250,13 +250,13 @@ public class ChatClientAgentThreadTests } /// - /// Verify thread serialization to JSON with custom options. + /// Verify session serialization to JSON with custom options. /// [Fact] - public void VerifyThreadSerializationWithCustomOptions() + public void VerifySessionSerializationWithCustomOptions() { // Arrange - var thread = new ChatClientAgentThread(); + var session = new ChatClientAgentSession(); JsonSerializerOptions options = new() { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!); @@ -268,10 +268,10 @@ public class ChatClientAgentThreadTests chatHistoryProviderMock .Setup(m => m.Serialize(options)) .Returns(chatHistoryProviderStateElement); - thread.ChatHistoryProvider = chatHistoryProviderMock.Object; + session.ChatHistoryProvider = chatHistoryProviderMock.Object; // Act - var json = thread.Serialize(options); + var json = session.Serialize(options); // Assert Assert.Equal(JsonValueKind.Object, json.ValueKind); @@ -295,15 +295,15 @@ public class ChatClientAgentThreadTests public void GetService_RequestingAIContextProvider_ReturnsAIContextProvider() { // Arrange - var thread = new ChatClientAgentThread(); + var session = new ChatClientAgentSession(); var mockProvider = new Mock(); mockProvider .Setup(m => m.GetService(It.Is(x => x == typeof(AIContextProvider)), null)) .Returns(mockProvider.Object); - thread.AIContextProvider = mockProvider.Object; + session.AIContextProvider = mockProvider.Object; // Act - var result = thread.GetService(typeof(AIContextProvider)); + var result = session.GetService(typeof(AIContextProvider)); // Assert Assert.NotNull(result); @@ -314,12 +314,12 @@ public class ChatClientAgentThreadTests public void GetService_RequestingChatHistoryProvider_ReturnsChatHistoryProvider() { // Arrange - var thread = new ChatClientAgentThread(); + var session = new ChatClientAgentSession(); var chatHistoryProvider = new InMemoryChatHistoryProvider(); - thread.ChatHistoryProvider = chatHistoryProvider; + session.ChatHistoryProvider = chatHistoryProvider; // Act - var result = thread.GetService(typeof(ChatHistoryProvider)); + var result = session.GetService(typeof(ChatHistoryProvider)); // Assert Assert.NotNull(result); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs index 7395b22ebf..6c2be9689a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs @@ -222,10 +222,10 @@ public partial class ChatClientAgentTests } /// - /// Verify that RunAsync works with existing thread and can retreive messages if the thread has a ChatHistoryProvider. + /// Verify that RunAsync works with existing session and can retreive messages if the session has a ChatHistoryProvider. /// [Fact] - public async Task RunAsyncRetrievesMessagesFromThreadWhenThreadHasChatHistoryProviderAsync() + public async Task RunAsyncRetrievesMessagesFromSessionWhenSessionHasChatHistoryProviderAsync() { // Arrange Mock mockService = new(); @@ -241,11 +241,11 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); - // Create a thread using the agent's GetNewThreadAsync method - var thread = await agent.GetNewThreadAsync(); + // Create a session using the agent's GetNewSessionAsync method + var session = await agent.GetNewSessionAsync(); // Act - await agent.RunAsync([new(ChatRole.User, "new message")], thread: thread); + await agent.RunAsync([new(ChatRole.User, "new message")], session: session); // Assert // Should contain: new message @@ -356,8 +356,8 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { AIContextProviderFactory = (_, _) => new(mockProvider.Object), ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); // Act - var thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - await agent.RunAsync(requestMessages, thread); + var session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + await agent.RunAsync(requestMessages, session); // Assert // Should contain: base instructions, user message, context message, base function, context function @@ -371,8 +371,8 @@ public partial class ChatClientAgentTests Assert.Contains(capturedTools, t => t.Name == "base function"); Assert.Contains(capturedTools, t => t.Name == "context provider function"); - // Verify that the thread was updated with the ai context provider, input and response messages - var chatHistoryProvider = Assert.IsType(thread!.ChatHistoryProvider); + // Verify that the session was updated with the ai context provider, input and response messages + var chatHistoryProvider = Assert.IsType(session!.ChatHistoryProvider); Assert.Equal(3, chatHistoryProvider.Count); Assert.Equal("user message", chatHistoryProvider[0].Text); Assert.Equal("context provider message", chatHistoryProvider[1].Text); @@ -1296,11 +1296,11 @@ public partial class ChatClientAgentTests }); // Act - ChatClientAgentThread? thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - await agent.RunStreamingAsync([new(ChatRole.User, "test")], thread).ToListAsync(); + ChatClientAgentSession? session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + await agent.RunStreamingAsync([new(ChatRole.User, "test")], session).ToListAsync(); // Assert - var chatHistoryProvider = Assert.IsType(thread!.ChatHistoryProvider); + var chatHistoryProvider = Assert.IsType(session!.ChatHistoryProvider); Assert.Equal(2, chatHistoryProvider.Count); Assert.Equal("test", chatHistoryProvider[0].Text); Assert.Equal("what?", chatHistoryProvider[1].Text); @@ -1334,8 +1334,8 @@ public partial class ChatClientAgentTests }); // Act & Assert - ChatClientAgentThread? thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - var exception = await Assert.ThrowsAsync(async () => await agent.RunStreamingAsync([new(ChatRole.User, "test")], thread).ToListAsync()); + ChatClientAgentSession? session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + var exception = await Assert.ThrowsAsync(async () => await agent.RunStreamingAsync([new(ChatRole.User, "test")], session).ToListAsync()); Assert.Equal("Only the ConversationId or ChatHistoryProvider may be set, but not both and switching from one to another is not supported.", exception.Message); } @@ -1391,8 +1391,8 @@ public partial class ChatClientAgentTests }); // Act - var thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - var updates = agent.RunStreamingAsync(requestMessages, thread); + var session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + var updates = agent.RunStreamingAsync(requestMessages, session); _ = await updates.ToAgentResponseAsync(); // Assert @@ -1407,8 +1407,8 @@ public partial class ChatClientAgentTests Assert.Contains(capturedTools, t => t.Name == "base function"); Assert.Contains(capturedTools, t => t.Name == "context provider function"); - // Verify that the thread was updated with the input, ai context provider, and response messages - var chatHistoryProvider = Assert.IsType(thread!.ChatHistoryProvider); + // Verify that the session was updated with the input, ai context provider, and response messages + var chatHistoryProvider = Assert.IsType(session!.ChatHistoryProvider); Assert.Equal(3, chatHistoryProvider.Count); Assert.Equal("user message", chatHistoryProvider[0].Text); Assert.Equal("context provider message", chatHistoryProvider[1].Text); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs index 018da2b6db..87be3fb96e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs @@ -55,10 +55,10 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - ChatClientAgentThread thread = new() { ConversationId = "conversation-id" }; + ChatClientAgentSession? session = new() { ConversationId = "conversation-id" }; // Act - await agent.RunAsync(thread, options: agentRunOptions); + await agent.RunAsync(session, options: agentRunOptions); // Assert Assert.NotNull(capturedChatOptions); @@ -94,12 +94,12 @@ public class ChatClientAgent_BackgroundResponsesTests ContinuationToken = continuationToken2 }; - ChatClientAgentThread thread = new() { ConversationId = "conversation-id" }; + ChatClientAgentSession? session = new() { ConversationId = "conversation-id" }; ChatClientAgent agent = new(mockChatClient.Object); // Act - await agent.RunAsync(thread, options: agentRunOptions); + await agent.RunAsync(session, options: agentRunOptions); // Assert Assert.NotNull(capturedChatOptions); @@ -153,10 +153,10 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - ChatClientAgentThread thread = new() { ConversationId = "conversation-id" }; + ChatClientAgentSession? session = new() { ConversationId = "conversation-id" }; // Act - await foreach (var _ in agent.RunStreamingAsync(thread, options: agentRunOptions)) + await foreach (var _ in agent.RunStreamingAsync(session, options: agentRunOptions)) { } @@ -202,10 +202,10 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - var thread = new ChatClientAgentThread() { ConversationId = "conversation-id" }; + var session = new ChatClientAgentSession() { ConversationId = "conversation-id" }; // Act - await foreach (var _ in agent.RunStreamingAsync(thread, options: agentRunOptions)) + await foreach (var _ in agent.RunStreamingAsync(session, options: agentRunOptions)) { } @@ -231,10 +231,10 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); var runOptions = new ChatClientAgentRunOptions(new ChatOptions { AllowBackgroundResponses = true }); - ChatClientAgentThread thread = new(); + ChatClientAgentSession? session = new(); // Act - var response = await agent.RunAsync([new(ChatRole.User, "hi")], thread, options: runOptions); + var response = await agent.RunAsync([new(ChatRole.User, "hi")], session, options: runOptions); // Assert Assert.Same(continuationToken, (response.ContinuationToken as ChatClientAgentContinuationToken)?.InnerToken); @@ -261,11 +261,11 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - ChatClientAgentThread thread = new(); + ChatClientAgentSession? session = new(); // Act var actualUpdates = new List(); - await foreach (var u in agent.RunStreamingAsync([new(ChatRole.User, "hi")], thread, options: new ChatClientAgentRunOptions(new ChatOptions { AllowBackgroundResponses = true }))) + await foreach (var u in agent.RunStreamingAsync([new(ChatRole.User, "hi")], session, options: new ChatClientAgentRunOptions(new ChatOptions { AllowBackgroundResponses = true }))) { actualUpdates.Add(u); } @@ -331,7 +331,7 @@ public class ChatClientAgent_BackgroundResponsesTests } [Fact] - public async Task RunAsync_WhenContinuationTokenProvided_SkipsThreadMessagePopulationAsync() + public async Task RunAsync_WhenContinuationTokenProvided_SkipsSessionMessagePopulationAsync() { // Arrange List capturedMessages = []; @@ -364,8 +364,8 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - // Create a thread with both chat history provider and AI context provider - ChatClientAgentThread thread = new() + // Create a session with both chat history provider and AI context provider + ChatClientAgentSession? session = new() { ChatHistoryProvider = mockChatHistoryProvider.Object, AIContextProvider = mockContextProvider.Object @@ -377,11 +377,11 @@ public class ChatClientAgent_BackgroundResponsesTests }; // Act - await agent.RunAsync([], thread, options: runOptions); + await agent.RunAsync([], session, options: runOptions); // Assert - // With continuation token, thread message population should be skipped + // With continuation token, session message population should be skipped Assert.Empty(capturedMessages); // Verify that chat history provider was never called due to continuation token @@ -396,7 +396,7 @@ public class ChatClientAgent_BackgroundResponsesTests } [Fact] - public async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsThreadMessagePopulationAsync() + public async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsSessionMessagePopulationAsync() { // Arrange List capturedMessages = []; @@ -429,8 +429,8 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - // Create a thread with both chat history provider and AI context provider - ChatClientAgentThread thread = new() + // Create a session with both chat history provider and AI context provider + ChatClientAgentSession? session = new() { ChatHistoryProvider = mockChatHistoryProvider.Object, AIContextProvider = mockContextProvider.Object @@ -442,10 +442,10 @@ public class ChatClientAgent_BackgroundResponsesTests }; // Act - await agent.RunStreamingAsync(thread, options: runOptions).ToListAsync(); + await agent.RunStreamingAsync(session, options: runOptions).ToListAsync(); // Assert - // With continuation token, thread message population should be skipped + // With continuation token, session message population should be skipped Assert.Empty(capturedMessages); // Verify that chat history provider was never called due to continuation token @@ -460,7 +460,7 @@ public class ChatClientAgent_BackgroundResponsesTests } [Fact] - public async Task RunAsync_WhenNoThreadProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() + public async Task RunAsync_WhenNoSessionProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() { // Arrange Mock mockChatClient = new(); @@ -484,7 +484,7 @@ public class ChatClientAgent_BackgroundResponsesTests } [Fact] - public async Task RunStreamingAsync_WhenNoThreadProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() + public async Task RunStreamingAsync_WhenNoSessionProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() { // Arrange Mock mockChatClient = new(); @@ -532,7 +532,7 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - ChatClientAgentThread thread = new() { ConversationId = "conversation-id" }; + ChatClientAgentSession? session = new() { ConversationId = "conversation-id" }; AgentRunOptions runOptions = new() { @@ -544,7 +544,7 @@ public class ChatClientAgent_BackgroundResponsesTests // Act var updates = new List(); - await foreach (var update in agent.RunStreamingAsync(thread, options: runOptions)) + await foreach (var update in agent.RunStreamingAsync(session, options: runOptions)) { updates.Add(update); } @@ -580,7 +580,7 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - ChatClientAgentThread thread = new() { ConversationId = "conversation-id" }; + ChatClientAgentSession? session = new() { ConversationId = "conversation-id" }; AgentRunOptions runOptions = new() { @@ -592,7 +592,7 @@ public class ChatClientAgent_BackgroundResponsesTests // Act var updates = new List(); - await foreach (var update in agent.RunStreamingAsync(thread, options: runOptions)) + await foreach (var update in agent.RunStreamingAsync(session, options: runOptions)) { updates.Add(update); } @@ -644,7 +644,7 @@ public class ChatClientAgent_BackgroundResponsesTests .Callback((context, ct) => capturedInvokedContext = context) .Returns(new ValueTask()); - ChatClientAgentThread thread = new() + ChatClientAgentSession? session = new() { ChatHistoryProvider = mockChatHistoryProvider.Object, AIContextProvider = mockContextProvider.Object @@ -659,7 +659,7 @@ public class ChatClientAgent_BackgroundResponsesTests }; // Act - await agent.RunStreamingAsync(thread, options: runOptions).ToListAsync(); + await agent.RunStreamingAsync(session, options: runOptions).ToListAsync(); // Assert mockChatHistoryProvider.Verify(ms => ms.InvokedAsync(It.IsAny(), It.IsAny()), Times.Once); @@ -700,7 +700,7 @@ public class ChatClientAgent_BackgroundResponsesTests .Callback((context, ct) => capturedInvokedContext = context) .Returns(new ValueTask()); - ChatClientAgentThread thread = new() + ChatClientAgentSession? session = new() { ChatHistoryProvider = mockChatHistoryProvider.Object, AIContextProvider = mockContextProvider.Object @@ -715,7 +715,7 @@ public class ChatClientAgent_BackgroundResponsesTests }; // Act - await agent.RunStreamingAsync(thread, options: runOptions).ToListAsync(); + await agent.RunStreamingAsync(session, options: runOptions).ToListAsync(); // Assert mockChatHistoryProvider.Verify(ms => ms.InvokedAsync(It.IsAny(), It.IsAny()), Times.Once); @@ -750,7 +750,7 @@ public class ChatClientAgent_BackgroundResponsesTests ChatClientAgent agent = new(mockChatClient.Object); - ChatClientAgentThread thread = new() { }; + ChatClientAgentSession? session = new() { }; List capturedContinuationTokens = []; @@ -759,7 +759,7 @@ public class ChatClientAgent_BackgroundResponsesTests // Act // Do the initial run - await foreach (var update in agent.RunStreamingAsync(userMessage, thread)) + await foreach (var update in agent.RunStreamingAsync(userMessage, session)) { capturedContinuationTokens.Add(Assert.IsType(update.ContinuationToken)); break; @@ -768,7 +768,7 @@ public class ChatClientAgent_BackgroundResponsesTests // Now resume the run using the captured continuation token returnUpdates.RemoveAt(0); // remove the first mock update as it was already processed var options = new AgentRunOptions { ContinuationToken = capturedContinuationTokens[0] }; - await foreach (var update in agent.RunStreamingAsync(thread, options: options)) + await foreach (var update in agent.RunStreamingAsync(session, options: options)) { capturedContinuationTokens.Add(Assert.IsType(update.ContinuationToken)); } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs index 3e995b3cee..e2b7313e7f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs @@ -21,7 +21,7 @@ public class ChatClientAgent_ChatHistoryManagementTests #region ConversationId Tests /// - /// Verify that RunAsync does not throw when providing a ConversationId via both AgentThread and + /// Verify that RunAsync does not throw when providing a ConversationId via both AgentSession and /// via ChatOptions and the two are the same. /// [Fact] @@ -38,15 +38,15 @@ public class ChatClientAgent_ChatHistoryManagementTests ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); - ChatClientAgentThread thread = new() { ConversationId = "ConvId" }; + ChatClientAgentSession? session = new() { ConversationId = "ConvId" }; // Act & Assert - var response = await agent.RunAsync([new(ChatRole.User, "test")], thread, options: new ChatClientAgentRunOptions(chatOptions)); + var response = await agent.RunAsync([new(ChatRole.User, "test")], session, options: new ChatClientAgentRunOptions(chatOptions)); Assert.NotNull(response); } /// - /// Verify that RunAsync throws when providing a ConversationId via both AgentThread and + /// Verify that RunAsync throws when providing a ConversationId via both AgentSession and /// via ChatOptions and the two are different. /// [Fact] @@ -58,14 +58,14 @@ public class ChatClientAgent_ChatHistoryManagementTests ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); - ChatClientAgentThread thread = new() { ConversationId = "ThreadId" }; + ChatClientAgentSession? session = new() { ConversationId = "ThreadId" }; // Act & Assert - await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], thread, options: new ChatClientAgentRunOptions(chatOptions))); + await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], session, options: new ChatClientAgentRunOptions(chatOptions))); } /// - /// Verify that RunAsync clones the ChatOptions when providing a thread with a ConversationId and a ChatOptions. + /// Verify that RunAsync clones the ChatOptions when providing a session with a ConversationId and a ChatOptions. /// [Fact] public async Task RunAsync_ClonesChatOptions_ToAddConversationIdAsync() @@ -81,20 +81,20 @@ public class ChatClientAgent_ChatHistoryManagementTests ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); - ChatClientAgentThread thread = new() { ConversationId = "ConvId" }; + ChatClientAgentSession? session = new() { ConversationId = "ConvId" }; // Act - await agent.RunAsync([new(ChatRole.User, "test")], thread, options: new ChatClientAgentRunOptions(chatOptions)); + await agent.RunAsync([new(ChatRole.User, "test")], session, options: new ChatClientAgentRunOptions(chatOptions)); // Assert Assert.Null(chatOptions.ConversationId); } /// - /// Verify that RunAsync throws if a thread is provided that uses a conversation id already, but the service does not return one on invoke. + /// Verify that RunAsync throws if a session is provided that uses a conversation id already, but the service does not return one on invoke. /// [Fact] - public async Task RunAsync_Throws_ForMissingConversationIdWithConversationIdThreadAsync() + public async Task RunAsync_Throws_ForMissingConversationIdWithConversationIdSessionAsync() { // Arrange Mock mockService = new(); @@ -106,17 +106,17 @@ public class ChatClientAgent_ChatHistoryManagementTests ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); - ChatClientAgentThread thread = new() { ConversationId = "ConvId" }; + ChatClientAgentSession? session = new() { ConversationId = "ConvId" }; // Act & Assert - await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], thread)); + await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], session)); } /// - /// Verify that RunAsync sets the ConversationId on the thread when the service returns one. + /// Verify that RunAsync sets the ConversationId on the session when the service returns one. /// [Fact] - public async Task RunAsync_SetsConversationIdOnThread_WhenReturnedByChatClientAsync() + public async Task RunAsync_SetsConversationIdOnSession_WhenReturnedByChatClientAsync() { // Arrange Mock mockService = new(); @@ -126,13 +126,13 @@ public class ChatClientAgent_ChatHistoryManagementTests It.IsAny(), It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" }); ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); - ChatClientAgentThread thread = new(); + ChatClientAgentSession? session = new(); // Act - await agent.RunAsync([new(ChatRole.User, "test")], thread); + await agent.RunAsync([new(ChatRole.User, "test")], session); // Assert - Assert.Equal("ConvId", thread.ConversationId); + Assert.Equal("ConvId", session.ConversationId); } #endregion @@ -158,11 +158,11 @@ public class ChatClientAgent_ChatHistoryManagementTests }); // Act - ChatClientAgentThread? thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - await agent.RunAsync([new(ChatRole.User, "test")], thread); + ChatClientAgentSession? session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + await agent.RunAsync([new(ChatRole.User, "test")], session); // Assert - InMemoryChatHistoryProvider chatHistoryProvider = Assert.IsType(thread!.ChatHistoryProvider); + InMemoryChatHistoryProvider chatHistoryProvider = Assert.IsType(session!.ChatHistoryProvider); Assert.Equal(2, chatHistoryProvider.Count); Assert.Equal("test", chatHistoryProvider[0].Text); Assert.Equal("response", chatHistoryProvider[1].Text); @@ -200,11 +200,11 @@ public class ChatClientAgent_ChatHistoryManagementTests }); // Act - ChatClientAgentThread? thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - await agent.RunAsync([new(ChatRole.User, "test")], thread); + ChatClientAgentSession? session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + await agent.RunAsync([new(ChatRole.User, "test")], session); // Assert - Assert.IsType(thread!.ChatHistoryProvider, exactMatch: false); + Assert.IsType(session!.ChatHistoryProvider, exactMatch: false); mockService.Verify( x => x.GetResponseAsync( It.Is>(msgs => msgs.Count() == 2 && msgs.Any(m => m.Text == "Existing Chat History") && msgs.Any(m => m.Text == "test")), @@ -248,11 +248,11 @@ public class ChatClientAgent_ChatHistoryManagementTests }); // Act - ChatClientAgentThread? thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], thread)); + ChatClientAgentSession? session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], session)); // Assert - Assert.IsType(thread!.ChatHistoryProvider, exactMatch: false); + Assert.IsType(session!.ChatHistoryProvider, exactMatch: false); mockChatHistoryProvider.Verify(s => s.InvokedAsync( It.Is(x => x.RequestMessages.Count() == 1 && x.ResponseMessages == null && x.InvokeException!.Message == "Test Error"), It.IsAny()), @@ -282,8 +282,8 @@ public class ChatClientAgent_ChatHistoryManagementTests }); // Act & Assert - ChatClientAgentThread? thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; - InvalidOperationException exception = await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], thread)); + ChatClientAgentSession? session = await agent.GetNewSessionAsync() as ChatClientAgentSession; + InvalidOperationException exception = await Assert.ThrowsAsync(() => agent.RunAsync([new(ChatRole.User, "test")], session)); Assert.Equal("Only the ConversationId or ChatHistoryProvider may be set, but not both and switching from one to another is not supported.", exception.Message); } @@ -335,13 +335,13 @@ public class ChatClientAgent_ChatHistoryManagementTests }); // Act - ChatClientAgentThread? thread = await agent.GetNewThreadAsync() as ChatClientAgentThread; + ChatClientAgentSession? session = await agent.GetNewSessionAsync() as ChatClientAgentSession; AdditionalPropertiesDictionary additionalProperties = new(); additionalProperties.Add(mockOverrideChatHistoryProvider.Object); - await agent.RunAsync([new(ChatRole.User, "test")], thread, options: new AgentRunOptions { AdditionalProperties = additionalProperties }); + await agent.RunAsync([new(ChatRole.User, "test")], session, options: new AgentRunOptions { AdditionalProperties = additionalProperties }); // Assert - Assert.Same(mockFactoryChatHistoryProvider.Object, thread!.ChatHistoryProvider); + Assert.Same(mockFactoryChatHistoryProvider.Object, session!.ChatHistoryProvider); mockService.Verify( x => x.GetResponseAsync( It.Is>(msgs => msgs.Count() == 2 && msgs.Any(m => m.Text == "Existing Chat History") && msgs.Any(m => m.Text == "test")), diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeSessionTests.cs similarity index 70% rename from dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeThreadTests.cs rename to dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeSessionTests.cs index 6b9a4c89e2..014cb1483b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeThreadTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeSessionTests.cs @@ -8,12 +8,12 @@ using Moq; namespace Microsoft.Agents.AI.UnitTests; /// -/// Contains unit tests for the ChatClientAgent.DeserializeThread methods. +/// Contains unit tests for the ChatClientAgent.DeserializeSession methods. /// -public class ChatClientAgent_DeserializeThreadTests +public class ChatClientAgent_DeserializeSessionTests { [Fact] - public async Task DeserializeThread_UsesAIContextProviderFactory_IfProvidedAsync() + public async Task DeserializeSession_UsesAIContextProviderFactory_IfProvidedAsync() { // Arrange var mockChatClient = new Mock(); @@ -36,17 +36,17 @@ public class ChatClientAgent_DeserializeThreadTests """, TestJsonSerializerContext.Default.JsonElement); // Act - var thread = await agent.DeserializeThreadAsync(json); + var session = await agent.DeserializeSessionAsync(json); // Assert Assert.True(factoryCalled, "AIContextProviderFactory was not called."); - Assert.IsType(thread); - var typedThread = (ChatClientAgentThread)thread; - Assert.Same(mockContextProvider.Object, typedThread.AIContextProvider); + Assert.IsType(session); + var typedSession = (ChatClientAgentSession)session; + Assert.Same(mockContextProvider.Object, typedSession.AIContextProvider); } [Fact] - public async Task DeserializeThread_UsesChatHistoryProviderFactory_IfProvidedAsync() + public async Task DeserializeSession_UsesChatHistoryProviderFactory_IfProvidedAsync() { // Arrange var mockChatClient = new Mock(); @@ -69,12 +69,12 @@ public class ChatClientAgent_DeserializeThreadTests """, TestJsonSerializerContext.Default.JsonElement); // Act - var thread = await agent.DeserializeThreadAsync(json); + var session = await agent.DeserializeSessionAsync(json); // Assert Assert.True(factoryCalled, "ChatHistoryProviderFactory was not called."); - Assert.IsType(thread); - var typedThread = (ChatClientAgentThread)thread; - Assert.Same(mockChatHistoryProvider.Object, typedThread.ChatHistoryProvider); + Assert.IsType(session); + var typedSession = (ChatClientAgentSession)session; + Assert.Same(mockChatHistoryProvider.Object, typedSession.ChatHistoryProvider); } } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewSessionTests.cs similarity index 58% rename from dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewThreadTests.cs rename to dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewSessionTests.cs index b582465a4d..c48b303fa2 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewThreadTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewSessionTests.cs @@ -7,12 +7,12 @@ using Moq; namespace Microsoft.Agents.AI.UnitTests; /// -/// Contains unit tests for the ChatClientAgent.GetNewThreadAsync methods. +/// Contains unit tests for the ChatClientAgent.GetNewSessionAsync methods. /// -public class ChatClientAgent_GetNewThreadTests +public class ChatClientAgent_GetNewSessionTests { [Fact] - public async Task GetNewThread_UsesAIContextProviderFactory_IfProvidedAsync() + public async Task GetNewSession_UsesAIContextProviderFactory_IfProvidedAsync() { // Arrange var mockChatClient = new Mock(); @@ -29,17 +29,17 @@ public class ChatClientAgent_GetNewThreadTests }); // Act - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); // Assert Assert.True(factoryCalled, "AIContextProviderFactory was not called."); - Assert.IsType(thread); - var typedThread = (ChatClientAgentThread)thread; - Assert.Same(mockContextProvider.Object, typedThread.AIContextProvider); + Assert.IsType(session); + var typedSession = (ChatClientAgentSession)session; + Assert.Same(mockContextProvider.Object, typedSession.AIContextProvider); } [Fact] - public async Task GetNewThread_UsesChatHistoryProviderFactory_IfProvidedAsync() + public async Task GetNewSession_UsesChatHistoryProviderFactory_IfProvidedAsync() { // Arrange var mockChatClient = new Mock(); @@ -56,17 +56,17 @@ public class ChatClientAgent_GetNewThreadTests }); // Act - var thread = await agent.GetNewThreadAsync(); + var session = await agent.GetNewSessionAsync(); // Assert Assert.True(factoryCalled, "ChatHistoryProviderFactory was not called."); - Assert.IsType(thread); - var typedThread = (ChatClientAgentThread)thread; - Assert.Same(mockChatHistoryProvider.Object, typedThread.ChatHistoryProvider); + Assert.IsType(session); + var typedSession = (ChatClientAgentSession)session; + Assert.Same(mockChatHistoryProvider.Object, typedSession.ChatHistoryProvider); } [Fact] - public async Task GetNewThread_UsesChatHistoryProvider_FromTypedOverloadAsync() + public async Task GetNewSession_UsesChatHistoryProvider_FromTypedOverloadAsync() { // Arrange var mockChatClient = new Mock(); @@ -74,16 +74,16 @@ public class ChatClientAgent_GetNewThreadTests var agent = new ChatClientAgent(mockChatClient.Object); // Act - var thread = await agent.GetNewThreadAsync(mockChatHistoryProvider.Object); + var session = await agent.GetNewSessionAsync(mockChatHistoryProvider.Object); // Assert - Assert.IsType(thread); - var typedThread = (ChatClientAgentThread)thread; - Assert.Same(mockChatHistoryProvider.Object, typedThread.ChatHistoryProvider); + Assert.IsType(session); + var typedSession = (ChatClientAgentSession)session; + Assert.Same(mockChatHistoryProvider.Object, typedSession.ChatHistoryProvider); } [Fact] - public async Task GetNewThread_UsesConversationId_FromTypedOverloadAsync() + public async Task GetNewSession_UsesConversationId_FromTypedOverloadAsync() { // Arrange var mockChatClient = new Mock(); @@ -91,11 +91,11 @@ public class ChatClientAgent_GetNewThreadTests var agent = new ChatClientAgent(mockChatClient.Object); // Act - var thread = await agent.GetNewThreadAsync(TestConversationId); + var session = await agent.GetNewSessionAsync(TestConversationId); // Assert - Assert.IsType(thread); - var typedThread = (ChatClientAgentThread)thread; - Assert.Equal(TestConversationId, typedThread.ConversationId); + Assert.IsType(session); + var typedSession = (ChatClientAgentSession)session; + Assert.Equal(TestConversationId, typedSession.ConversationId); } } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_RunWithCustomOptionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_RunWithCustomOptionsTests.cs index 4c85bcbb51..cff5b6ba78 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_RunWithCustomOptionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_RunWithCustomOptionsTests.cs @@ -19,7 +19,7 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests #region RunAsync Tests [Fact] - public async Task RunAsync_WithThreadAndOptions_CallsBaseMethodAsync() + public async Task RunAsync_WithSessionAndOptions_CallsBaseMethodAsync() { // Arrange Mock mockChatClient = new(); @@ -30,11 +30,11 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatClientAgentRunOptions options = new(); // Act - AgentResponse result = await agent.RunAsync(thread, options); + AgentResponse result = await agent.RunAsync(session, options); // Assert Assert.NotNull(result); @@ -59,11 +59,11 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatClientAgentRunOptions options = new(); // Act - AgentResponse result = await agent.RunAsync("Test message", thread, options); + AgentResponse result = await agent.RunAsync("Test message", session, options); // Assert Assert.NotNull(result); @@ -88,12 +88,12 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage message = new(ChatRole.User, "Test message"); ChatClientAgentRunOptions options = new(); // Act - AgentResponse result = await agent.RunAsync(message, thread, options); + AgentResponse result = await agent.RunAsync(message, session, options); // Assert Assert.NotNull(result); @@ -118,12 +118,12 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); IEnumerable messages = [new(ChatRole.User, "Message 1"), new(ChatRole.User, "Message 2")]; ChatClientAgentRunOptions options = new(); // Act - AgentResponse result = await agent.RunAsync(messages, thread, options); + AgentResponse result = await agent.RunAsync(messages, session, options); // Assert Assert.NotNull(result); @@ -168,7 +168,7 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests #region RunStreamingAsync Tests [Fact] - public async Task RunStreamingAsync_WithThreadAndOptions_CallsBaseMethodAsync() + public async Task RunStreamingAsync_WithSessionAndOptions_CallsBaseMethodAsync() { // Arrange Mock mockChatClient = new(); @@ -179,12 +179,12 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).Returns(GetAsyncUpdatesAsync()); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatClientAgentRunOptions options = new(); // Act var updates = new List(); - await foreach (var update in agent.RunStreamingAsync(thread, options)) + await foreach (var update in agent.RunStreamingAsync(session, options)) { updates.Add(update); } @@ -211,12 +211,12 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).Returns(GetAsyncUpdatesAsync()); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatClientAgentRunOptions options = new(); // Act var updates = new List(); - await foreach (var update in agent.RunStreamingAsync("Test message", thread, options)) + await foreach (var update in agent.RunStreamingAsync("Test message", session, options)) { updates.Add(update); } @@ -243,13 +243,13 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).Returns(GetAsyncUpdatesAsync()); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage message = new(ChatRole.User, "Test message"); ChatClientAgentRunOptions options = new(); // Act var updates = new List(); - await foreach (var update in agent.RunStreamingAsync(message, thread, options)) + await foreach (var update in agent.RunStreamingAsync(message, session, options)) { updates.Add(update); } @@ -276,13 +276,13 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).Returns(GetAsyncUpdatesAsync()); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); IEnumerable messages = [new ChatMessage(ChatRole.User, "Message 1"), new ChatMessage(ChatRole.User, "Message 2")]; ChatClientAgentRunOptions options = new(); // Act var updates = new List(); - await foreach (var update in agent.RunStreamingAsync(messages, thread, options)) + await foreach (var update in agent.RunStreamingAsync(messages, session, options)) { updates.Add(update); } @@ -313,7 +313,7 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests #region RunAsync{T} Tests [Fact] - public async Task RunAsyncOfT_WithThreadAndOptions_CallsBaseMethodAsync() + public async Task RunAsyncOfT_WithSessionAndOptions_CallsBaseMethodAsync() { // Arrange Mock mockChatClient = new(); @@ -324,11 +324,11 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatClientAgentRunOptions options = new(); // Act - AgentResponse agentResponse = await agent.RunAsync(thread, JsonContext_WithCustomRunOptions.Default.Options, options); + AgentResponse agentResponse = await agent.RunAsync(session, JsonContext_WithCustomRunOptions.Default.Options, options); // Assert Assert.NotNull(agentResponse); @@ -354,11 +354,11 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatClientAgentRunOptions options = new(); // Act - AgentResponse agentResponse = await agent.RunAsync("Test message", thread, JsonContext_WithCustomRunOptions.Default.Options, options); + AgentResponse agentResponse = await agent.RunAsync("Test message", session, JsonContext_WithCustomRunOptions.Default.Options, options); // Assert Assert.NotNull(agentResponse); @@ -384,12 +384,12 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ChatMessage message = new(ChatRole.User, "Test message"); ChatClientAgentRunOptions options = new(); // Act - AgentResponse agentResponse = await agent.RunAsync(message, thread, JsonContext_WithCustomRunOptions.Default.Options, options); + AgentResponse agentResponse = await agent.RunAsync(message, session, JsonContext_WithCustomRunOptions.Default.Options, options); // Assert Assert.NotNull(agentResponse); @@ -415,12 +415,12 @@ public sealed partial class ChatClientAgent_RunWithCustomOptionsTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")])); ChatClientAgent agent = new(mockChatClient.Object); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); IEnumerable messages = [new(ChatRole.User, "Message 1"), new(ChatRole.User, "Message 2")]; ChatClientAgentRunOptions options = new(); // Act - AgentResponse agentResponse = await agent.RunAsync(messages, thread, JsonContext_WithCustomRunOptions.Default.Options, options); + AgentResponse agentResponse = await agent.RunAsync(messages, session, JsonContext_WithCustomRunOptions.Default.Options, options); // Assert Assert.NotNull(agentResponse); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs index 50955234e5..695f8a4825 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs @@ -717,10 +717,10 @@ public sealed class FunctionInvocationDelegatingAgentTests var innerAgent = new ChatClientAgent(mockChatClient.Object); var messages = new List { new(ChatRole.User, "Test message") }; - async Task RunningMiddlewareCallbackAsync(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) + async Task RunningMiddlewareCallbackAsync(IEnumerable messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken) { executionOrder.Add("Running-Pre"); - var result = await innerAgent.RunAsync(messages, thread, options, cancellationToken); + var result = await innerAgent.RunAsync(messages, session, options, cancellationToken); executionOrder.Add("Running-Post"); return result; } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/LoggingAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/LoggingAgentTests.cs index b5e701cb38..d37d073eac 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/LoggingAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/LoggingAgentTests.cs @@ -80,7 +80,7 @@ public class LoggingAgentTests var innerAgent = new TestAIAgent { - RunAsyncFunc = async (messages, thread, options, cancellationToken) => + RunAsyncFunc = async (messages, session, options, cancellationToken) => { await Task.Yield(); return new AgentResponse(new ChatMessage(ChatRole.Assistant, "Test response")); @@ -123,7 +123,7 @@ public class LoggingAgentTests var innerAgent = new TestAIAgent { - RunAsyncFunc = async (messages, thread, options, cancellationToken) => + RunAsyncFunc = async (messages, session, options, cancellationToken) => { await Task.Yield(); return new AgentResponse(new ChatMessage(ChatRole.Assistant, "Test response")); @@ -165,7 +165,7 @@ public class LoggingAgentTests var innerAgent = new TestAIAgent { - RunAsyncFunc = (messages, thread, options, cancellationToken) => + RunAsyncFunc = (messages, session, options, cancellationToken) => throw new OperationCanceledException() }; @@ -195,7 +195,7 @@ public class LoggingAgentTests var innerAgent = new TestAIAgent { - RunAsyncFunc = (messages, thread, options, cancellationToken) => + RunAsyncFunc = (messages, session, options, cancellationToken) => throw new InvalidOperationException("Test exception") }; @@ -229,7 +229,7 @@ public class LoggingAgentTests }; static async IAsyncEnumerable CallbackAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) + IEnumerable messages, AgentSession? session, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { await Task.Yield(); yield return new AgentResponseUpdate(ChatRole.Assistant, "Test"); @@ -278,7 +278,7 @@ public class LoggingAgentTests }; static async IAsyncEnumerable CallbackAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) + IEnumerable messages, AgentSession? session, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { await Task.Yield(); yield return new AgentResponseUpdate(ChatRole.Assistant, "Update 1"); @@ -318,7 +318,7 @@ public class LoggingAgentTests }; static async IAsyncEnumerable CallbackAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) + IEnumerable messages, AgentSession? session, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { await Task.Yield(); throw new OperationCanceledException(); @@ -365,7 +365,7 @@ public class LoggingAgentTests }; static async IAsyncEnumerable CallbackAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) + IEnumerable messages, AgentSession? session, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { await Task.Yield(); throw new InvalidOperationException("Test exception"); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs index f898147ad0..f46538c8e4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs @@ -106,7 +106,7 @@ public class ChatHistoryMemoryProviderTests { ApplicationId = "app1", AgentId = "agent1", - ThreadId = "thread1", + SessionId = "session1", UserId = "user1" }; @@ -138,7 +138,7 @@ public class ChatHistoryMemoryProviderTests Assert.Equal("2000-01-01T00:00:00.0000000+00:00", stored[0]["CreatedAt"]); Assert.Equal("app1", stored[0]["ApplicationId"]); Assert.Equal("agent1", stored[0]["AgentId"]); - Assert.Equal("thread1", stored[0]["ThreadId"]); + Assert.Equal("session1", stored[0]["SessionId"]); Assert.Equal("user1", stored[0]["UserId"]); Assert.Null(stored[1]["MessageId"]); @@ -147,7 +147,7 @@ public class ChatHistoryMemoryProviderTests Assert.Equal(ChatRole.User.ToString(), stored[1]["Role"]); Assert.Equal("app1", stored[1]["ApplicationId"]); Assert.Equal("agent1", stored[1]["AgentId"]); - Assert.Equal("thread1", stored[1]["ThreadId"]); + Assert.Equal("session1", stored[1]["SessionId"]); Assert.Equal("user1", stored[1]["UserId"]); Assert.Equal("resp-1", stored[2]["MessageId"]); @@ -156,7 +156,7 @@ public class ChatHistoryMemoryProviderTests Assert.Equal(ChatRole.Assistant.ToString(), stored[2]["Role"]); Assert.Equal("app1", stored[2]["ApplicationId"]); Assert.Equal("agent1", stored[2]["AgentId"]); - Assert.Equal("thread1", stored[2]["ThreadId"]); + Assert.Equal("session1", stored[2]["SessionId"]); Assert.Equal("user1", stored[2]["UserId"]); } @@ -357,7 +357,7 @@ public class ChatHistoryMemoryProviderTests { ApplicationId = "app1", AgentId = "agent1", - ThreadId = "thread1", + SessionId = "session1", UserId = "user1" }; @@ -370,7 +370,7 @@ public class ChatHistoryMemoryProviderTests .Callback((string query, int maxResults, VectorSearchOptions> options, CancellationToken ct) => { // Verify that the filter was created correctly - const string ExpectedFilter = "x => ((((x.ApplicationId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).applicationId) AndAlso (x.AgentId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).agentId)) AndAlso (x.UserId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).userId)) AndAlso (x.ThreadId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).threadId))"; + const string ExpectedFilter = "x => ((((x.ApplicationId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).applicationId) AndAlso (x.AgentId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).agentId)) AndAlso (x.UserId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).userId)) AndAlso (x.SessionId == value(Microsoft.Agents.AI.VectorDataMemory.ChatHistoryMemoryProvider+<>c__DisplayClass20_0).sessionId))"; Assert.Equal(ExpectedFilter, options.Filter!.ToString()); }) .Returns(ToAsyncEnumerableAsync(new List>>())); @@ -486,7 +486,7 @@ public class ChatHistoryMemoryProviderTests { ApplicationId = "app", AgentId = "agent", - ThreadId = "thread", + SessionId = "session", UserId = "user" }; @@ -494,7 +494,7 @@ public class ChatHistoryMemoryProviderTests { ApplicationId = "app2", AgentId = "agent2", - ThreadId = "thread2", + SessionId = "session2", UserId = "user2" }; @@ -507,13 +507,13 @@ public class ChatHistoryMemoryProviderTests var storage = doc.RootElement.GetProperty("storageScope"); Assert.Equal("app", storage.GetProperty("applicationId").GetString()); Assert.Equal("agent", storage.GetProperty("agentId").GetString()); - Assert.Equal("thread", storage.GetProperty("threadId").GetString()); + Assert.Equal("session", storage.GetProperty("sessionId").GetString()); Assert.Equal("user", storage.GetProperty("userId").GetString()); var search = doc.RootElement.GetProperty("searchScope"); Assert.Equal("app2", search.GetProperty("applicationId").GetString()); Assert.Equal("agent2", search.GetProperty("agentId").GetString()); - Assert.Equal("thread2", search.GetProperty("threadId").GetString()); + Assert.Equal("session2", search.GetProperty("sessionId").GetString()); Assert.Equal("user2", search.GetProperty("userId").GetString()); // Act - deserialize and serialize again diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs index 84dc1d7a20..4cda58875e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs @@ -79,7 +79,7 @@ public class OpenTelemetryAgentTests NameFunc = () => "TestAgent", DescriptionFunc = () => "This is a test agent.", - RunAsyncFunc = async (messages, thread, options, cancellationToken) => + RunAsyncFunc = async (messages, session, options, cancellationToken) => { await Task.Yield(); return new AgentResponse(new ChatMessage(ChatRole.Assistant, "The blue whale, I think.")) @@ -108,7 +108,7 @@ public class OpenTelemetryAgentTests }; async static IAsyncEnumerable CallbackAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) + IEnumerable messages, AgentSession? session, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { await Task.Yield(); @@ -304,7 +304,7 @@ public class OpenTelemetryAgentTests NameFunc = () => name, DescriptionFunc = () => description, - RunAsyncFunc = async (messages, thread, options, cancellationToken) => + RunAsyncFunc = async (messages, session, options, cancellationToken) => { await Task.Yield(); return new AgentResponse(new ChatMessage(ChatRole.Assistant, "The blue whale, I think.")) @@ -333,7 +333,7 @@ public class OpenTelemetryAgentTests }; async static IAsyncEnumerable CallbackAsync( - IEnumerable messages, AgentThread? thread, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) + IEnumerable messages, AgentSession? session, AgentRunOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { await Task.Yield(); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs index 473c01bb6b..0ef255155a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs @@ -14,27 +14,27 @@ internal sealed class TestAIAgent : AIAgent public Func? NameFunc; public Func? DescriptionFunc; - public Func DeserializeThreadFunc = delegate { throw new NotSupportedException(); }; - public Func GetNewThreadFunc = delegate { throw new NotSupportedException(); }; - public Func, AgentThread?, AgentRunOptions?, CancellationToken, Task> RunAsyncFunc = delegate { throw new NotSupportedException(); }; - public Func, AgentThread?, AgentRunOptions?, CancellationToken, IAsyncEnumerable> RunStreamingAsyncFunc = delegate { throw new NotSupportedException(); }; + public Func DeserializeSessionFunc = delegate { throw new NotSupportedException(); }; + public Func GetNewSessionFunc = delegate { throw new NotSupportedException(); }; + public Func, AgentSession?, AgentRunOptions?, CancellationToken, Task> RunAsyncFunc = delegate { throw new NotSupportedException(); }; + public Func, AgentSession?, AgentRunOptions?, CancellationToken, IAsyncEnumerable> RunStreamingAsyncFunc = delegate { throw new NotSupportedException(); }; public Func? GetServiceFunc; public override string? Name => this.NameFunc?.Invoke() ?? base.Name; public override string? Description => this.DescriptionFunc?.Invoke() ?? base.Description; - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => - new(this.DeserializeThreadFunc(serializedThread, jsonSerializerOptions)); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => + new(this.DeserializeSessionFunc(serializedSession, jsonSerializerOptions)); - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(this.GetNewThreadFunc()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(this.GetNewSessionFunc()); - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - this.RunAsyncFunc(messages, thread, options, cancellationToken); + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + this.RunAsyncFunc(messages, session, options, cancellationToken); - protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - this.RunStreamingAsyncFunc(messages, thread, options, cancellationToken); + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + this.RunStreamingAsyncFunc(messages, session, options, cancellationToken); public override object? GetService(Type serviceType, object? serviceKey = null) => this.GetServiceFunc is { } func ? func(serviceType, serviceKey) : diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs index 4ed540c34d..ac0899e413 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs @@ -135,18 +135,18 @@ public class AgentWorkflowBuilderTests { public override string Name => name; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) - => new(new DoubleEchoAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) + => new(new DoubleEchoAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new DoubleEchoAgentThread()); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new DoubleEchoAgentSession()); protected override Task RunCoreAsync( - IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); protected override async IAsyncEnumerable RunCoreStreamingAsync( - IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { await Task.Yield(); @@ -158,7 +158,7 @@ public class AgentWorkflowBuilderTests } } - private sealed class DoubleEchoAgentThread() : InMemoryAgentThread(); + private sealed class DoubleEchoAgentSession() : InMemoryAgentSession(); [Fact] public async Task BuildConcurrent_AgentsRunInParallelAsync() @@ -410,7 +410,7 @@ public class AgentWorkflowBuilderTests private sealed class DoubleEchoAgentWithBarrier(string name, StrongBox> barrier, StrongBox remaining) : DoubleEchoAgent(name) { protected override async IAsyncEnumerable RunCoreStreamingAsync( - IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (Interlocked.Decrement(ref remaining.Value) == 0) { @@ -419,7 +419,7 @@ public class AgentWorkflowBuilderTests await barrier.Value!.Task.ConfigureAwait(false); - await foreach (var update in base.RunCoreStreamingAsync(messages, thread, options, cancellationToken)) + await foreach (var update in base.RunCoreStreamingAsync(messages, session, options, cancellationToken)) { await Task.Yield(); yield return update; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs index 90b334ff01..b03660d20c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs @@ -144,14 +144,14 @@ public class InProcessExecutionTests public override string Name { get; } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => new(new SimpleTestAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => new(new SimpleTestAgentSession()); - public override ValueTask DeserializeThreadAsync(System.Text.Json.JsonElement serializedThread, - System.Text.Json.JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => new(new SimpleTestAgentThread()); + public override ValueTask DeserializeSessionAsync(System.Text.Json.JsonElement serializedSession, + System.Text.Json.JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => new(new SimpleTestAgentSession()); protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -162,7 +162,7 @@ public class InProcessExecutionTests protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -190,7 +190,7 @@ public class InProcessExecutionTests } /// - /// Simple thread implementation for SimpleTestAgent. + /// Simple session implementation for SimpleTestAgent. /// - private sealed class SimpleTestAgentThread : InMemoryAgentThread; + private sealed class SimpleTestAgentSession : InMemoryAgentSession; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs index a9ca48659e..0ebc58a395 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs @@ -24,16 +24,16 @@ public class RepresentationTests private sealed class TestAgent : AIAgent { - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RoleCheckAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RoleCheckAgent.cs index 6641a6a245..732175c8b6 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RoleCheckAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RoleCheckAgent.cs @@ -16,15 +16,15 @@ internal sealed class RoleCheckAgent(bool allowOtherAssistantRoles, string? id = public override string? Name => name; - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new RoleCheckAgentThread()); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new RoleCheckAgentSession()); - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => new(new RoleCheckAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => new(new RoleCheckAgentSession()); - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + => this.RunStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { foreach (ChatMessage message in messages) { @@ -43,5 +43,5 @@ internal sealed class RoleCheckAgent(bool allowOtherAssistantRoles, string? id = }; } - private sealed class RoleCheckAgentThread : InMemoryAgentThread; + private sealed class RoleCheckAgentSession : InMemoryAgentSession; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs index 772d56bbcc..fd19797a56 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs @@ -60,23 +60,23 @@ internal sealed class HelloAgent(string id = nameof(HelloAgent)) : AIAgent protected override string? IdCore => id; public override string? Name => id; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) - => new(new HelloAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) + => new(new HelloAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new HelloAgentThread()); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new HelloAgentSession()); - protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { IEnumerable update = [ - await this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) + await this.RunCoreStreamingAsync(messages, session, options, cancellationToken) .SingleAsync(cancellationToken) .ConfigureAwait(false)]; return update.ToAgentResponse(); } - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { yield return new(ChatRole.Assistant, "Hello World!") { @@ -87,4 +87,4 @@ internal sealed class HelloAgent(string id = nameof(HelloAgent)) : AIAgent } } -internal sealed class HelloAgentThread() : InMemoryAgentThread(); +internal sealed class HelloAgentSession() : InMemoryAgentSession(); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/07_GroupChat_Workflow_HostAsAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/07_GroupChat_Workflow_HostAsAgent.cs index 71844aff69..215bd52119 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/07_GroupChat_Workflow_HostAsAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/07_GroupChat_Workflow_HostAsAgent.cs @@ -19,8 +19,8 @@ internal static class Step7EntryPoint for (int i = 0; i < numIterations; i++) { - AgentThread thread = await agent.GetNewThreadAsync(); - await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(thread).ConfigureAwait(false)) + AgentSession session = await agent.GetNewSessionAsync(); + await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(session).ConfigureAwait(false)) { if (update.RawRepresentation is WorkflowEvent) { diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/10_Sequential_HostAsAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/10_Sequential_HostAsAgent.cs index 6b87aabd97..280eb8c0d4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/10_Sequential_HostAsAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/10_Sequential_HostAsAgent.cs @@ -21,14 +21,14 @@ internal static class Step10EntryPoint { AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment); - AgentThread thread = await hostAgent.GetNewThreadAsync(); + AgentSession session = await hostAgent.GetNewSessionAsync(); foreach (string input in inputs) { AgentResponse response; ResponseContinuationToken? continuationToken = null; do { - response = await hostAgent.RunAsync(input, thread, new AgentRunOptions { ContinuationToken = continuationToken }); + response = await hostAgent.RunAsync(input, session, new AgentRunOptions { ContinuationToken = continuationToken }); } while ((continuationToken = response.ContinuationToken) is { }); foreach (ChatMessage message in response.Messages) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/11_Concurrent_HostAsAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/11_Concurrent_HostAsAgent.cs index dc939fda8b..cf2566de8d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/11_Concurrent_HostAsAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/11_Concurrent_HostAsAgent.cs @@ -33,14 +33,14 @@ internal static class Step11EntryPoint { AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment); - AgentThread thread = await hostAgent.GetNewThreadAsync(); + AgentSession session = await hostAgent.GetNewSessionAsync(); foreach (string input in inputs) { AgentResponse response; ResponseContinuationToken? continuationToken = null; do { - response = await hostAgent.RunAsync(input, thread, new AgentRunOptions { ContinuationToken = continuationToken }); + response = await hostAgent.RunAsync(input, session, new AgentRunOptions { ContinuationToken = continuationToken }); } while ((continuationToken = response.ContinuationToken) is { }); foreach (ChatMessage message in response.Messages) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs index 5cf4e07120..7e4ccac59f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs @@ -69,14 +69,14 @@ internal static class Step12EntryPoint { AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment); - AgentThread thread = await hostAgent.GetNewThreadAsync(); + AgentSession session = await hostAgent.GetNewSessionAsync(); foreach (string input in inputs) { AgentResponse response; ResponseContinuationToken? continuationToken = null; do { - response = await hostAgent.RunAsync(input, thread, new AgentRunOptions { ContinuationToken = continuationToken }); + response = await hostAgent.RunAsync(input, session, new AgentRunOptions { ContinuationToken = continuationToken }); } while ((continuationToken = response.ContinuationToken) is { }); foreach (ChatMessage message in response.Messages) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/13_Subworkflow_Checkpointing.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/13_Subworkflow_Checkpointing.cs index 113731e679..f04af720c6 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/13_Subworkflow_Checkpointing.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/13_Subworkflow_Checkpointing.cs @@ -28,16 +28,16 @@ internal static class Step13EntryPoint } } - public static async ValueTask RunAsAgentAsync(TextWriter writer, string input, IWorkflowExecutionEnvironment environment, AgentThread? thread) + public static async ValueTask RunAsAgentAsync(TextWriter writer, string input, IWorkflowExecutionEnvironment environment, AgentSession? session) { AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: environment, includeWorkflowOutputsInResponse: true); - thread ??= await hostAgent.GetNewThreadAsync(); + session ??= await hostAgent.GetNewSessionAsync(); AgentResponse response; ResponseContinuationToken? continuationToken = null; do { - response = await hostAgent.RunAsync(input, thread, new AgentRunOptions { ContinuationToken = continuationToken }); + response = await hostAgent.RunAsync(input, session, new AgentRunOptions { ContinuationToken = continuationToken }); } while ((continuationToken = response.ContinuationToken) is { }); foreach (ChatMessage message in response.Messages) @@ -45,7 +45,7 @@ internal static class Step13EntryPoint writer.WriteLine($"{message.AuthorName}: {message.Text}"); } - return thread; + return session; } public static async ValueTask RunAsync(TextWriter writer, string input, IWorkflowExecutionEnvironment environment, CheckpointManager checkpointManager, CheckpointInfo? resumeFrom) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SampleSmokeTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SampleSmokeTest.cs index 214333f11e..11b60a4691 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SampleSmokeTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SampleSmokeTest.cs @@ -412,7 +412,7 @@ public class SampleSmokeTest internal async Task Test_RunSample_Step13aAsync(ExecutionEnvironment environment) { IWorkflowExecutionEnvironment executionEnvironment = environment.ToWorkflowExecutionEnvironment(); - AgentThread? thread = null; + AgentSession? session = null; await RunAndValidateAsync(1); @@ -424,7 +424,7 @@ public class SampleSmokeTest using StringWriter writer = new(); string input = $"[{step}] Hello, World!"; - thread = await Step13EntryPoint.RunAsAgentAsync(writer, input, executionEnvironment, thread); + session = await Step13EntryPoint.RunAsAgentAsync(writer, input, executionEnvironment, session); string result = writer.ToString(); string[] lines = result.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs index cf63843def..f9a4309f86 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs @@ -16,26 +16,26 @@ internal class TestEchoAgent(string? id = null, string? name = null, string? pre protected override string? IdCore => id; public override string? Name => name ?? base.Name; - public override async ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override async ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { - return serializedThread.Deserialize(jsonSerializerOptions) ?? await this.GetNewThreadAsync(cancellationToken); + return serializedSession.Deserialize(jsonSerializerOptions) ?? await this.GetNewSessionAsync(cancellationToken); } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) => - new(new EchoAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) => + new(new EchoAgentSession()); - private static ChatMessage UpdateThread(ChatMessage message, InMemoryAgentThread? thread = null) + private static ChatMessage UpdateSession(ChatMessage message, InMemoryAgentSession? session = null) { - thread?.ChatHistoryProvider.Add(message); + session?.ChatHistoryProvider.Add(message); return message; } - private IEnumerable EchoMessages(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null) + private IEnumerable EchoMessages(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null) { foreach (ChatMessage message in messages) { - UpdateThread(message, thread as InMemoryAgentThread); + UpdateSession(message, session as InMemoryAgentSession); } IEnumerable echoMessages @@ -43,14 +43,14 @@ internal class TestEchoAgent(string? id = null, string? name = null, string? pre where message.Role == ChatRole.User && !string.IsNullOrEmpty(message.Text) select - UpdateThread(new ChatMessage(ChatRole.Assistant, $"{prefix}{message.Text}") + UpdateSession(new ChatMessage(ChatRole.Assistant, $"{prefix}{message.Text}") { AuthorName = this.Name ?? this.Id, CreatedAt = DateTimeOffset.Now, MessageId = Guid.NewGuid().ToString("N") - }, thread as InMemoryAgentThread); + }, session as InMemoryAgentSession); - return echoMessages.Concat(this.GetEpilogueMessages(options).Select(m => UpdateThread(m, thread as InMemoryAgentThread))); + return echoMessages.Concat(this.GetEpilogueMessages(options).Select(m => UpdateSession(m, session as InMemoryAgentSession))); } protected virtual IEnumerable GetEpilogueMessages(AgentRunOptions? options = null) @@ -58,10 +58,10 @@ internal class TestEchoAgent(string? id = null, string? name = null, string? pre return []; } - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { AgentResponse result = - new(this.EchoMessages(messages, thread, options).ToList()) + new(this.EchoMessages(messages, session, options).ToList()) { AgentId = this.Id, CreatedAt = DateTimeOffset.Now, @@ -71,11 +71,11 @@ internal class TestEchoAgent(string? id = null, string? name = null, string? pre return Task.FromResult(result); } - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { string responseId = Guid.NewGuid().ToString("N"); - foreach (ChatMessage message in this.EchoMessages(messages, thread, options).ToList()) + foreach (ChatMessage message in this.EchoMessages(messages, session, options).ToList()) { yield return new(message.Role, message.Contents) @@ -89,5 +89,5 @@ internal class TestEchoAgent(string? id = null, string? name = null, string? pre } } - private sealed class EchoAgentThread : InMemoryAgentThread; + private sealed class EchoAgentSession : InMemoryAgentSession; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestReplayAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestReplayAgent.cs index 05ec6e0f60..82358e1198 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestReplayAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestReplayAgent.cs @@ -45,21 +45,21 @@ public class TestReplayAgent(List? messages = null, string? id = nu return result; } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) - => new(new ReplayAgentThread()); + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) + => new(new ReplayAgentSession()); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - => new(new ReplayAgentThread()); + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + => new(new ReplayAgentSession()); public static TestReplayAgent FromStrings(params string[] messages) => new(ToChatMessages(messages)); public List Messages { get; } = Validate(messages) ?? []; - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + => this.RunStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { string responseId = Guid.NewGuid().ToString("N"); foreach (ChatMessage message in this.Messages) @@ -101,5 +101,5 @@ public class TestReplayAgent(List? messages = null, string? id = nu return candidateMessages; } - private sealed class ReplayAgentThread() : InMemoryAgentThread(); + private sealed class ReplayAgentSession() : InMemoryAgentSession(); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs index 0d7c38b2cd..226f26ad6c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI.Workflows.UnitTests; -internal sealed record TestRequestAgentThreadState(JsonElement ThreadState, Dictionary UnservicedRequests, HashSet ServicedRequests, HashSet PairedRequests); +internal sealed record TestRequestAgentSessionState(JsonElement SessionState, Dictionary UnservicedRequests, HashSet ServicedRequests, HashSet PairedRequests); public enum TestAgentRequestType { @@ -24,29 +24,29 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp { public Random RNG { get; set; } = new Random(HashCode.Combine(requestType, nameof(TestRequestAgent))); - public AgentThread? LastThread { get; set; } + public AgentSession? LastSession { get; set; } protected override string? IdCore => id; public override string? Name => name; - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken) + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken) => new(requestType switch { - TestAgentRequestType.FunctionCall => new TestRequestAgentThread(), - TestAgentRequestType.UserInputRequest => new TestRequestAgentThread(), + TestAgentRequestType.FunctionCall => new TestRequestAgentSession(), + TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), _ => throw new NotSupportedException(), }); - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => new(requestType switch { - TestAgentRequestType.FunctionCall => new TestRequestAgentThread(), - TestAgentRequestType.UserInputRequest => new TestRequestAgentThread(), + TestAgentRequestType.FunctionCall => new TestRequestAgentSession(), + TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), _ => throw new NotSupportedException(), }); - protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentResponseAsync(cancellationToken); + protected override Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + => this.RunStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken); private static int[] SampleIndicies(Random rng, int n, int c) { @@ -67,29 +67,29 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp private async IAsyncEnumerable RunStreamingAsync( IRequestResponseStrategy strategy, IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) where TRequest : AIContent where TResponse : AIContent { - this.LastThread = thread ??= await this.GetNewThreadAsync(cancellationToken); - TestRequestAgentThread traThread = ConvertThread(thread); + this.LastSession = session ??= await this.GetNewSessionAsync(cancellationToken); + TestRequestAgentSession traSessin = ConvertSession(session); - if (traThread.HasSentRequests) + if (traSessin.HasSentRequests) { foreach (TResponse response in messages.SelectMany(message => message.Contents).OfType()) { - strategy.ProcessResponse(response, traThread); + strategy.ProcessResponse(response, traSessin); } - if (traThread.UnservicedRequests.Count == 0) + if (traSessin.UnservicedRequests.Count == 0) { yield return new(ChatRole.Assistant, "Done"); } else { - yield return new(ChatRole.Assistant, $"Remaining: {traThread.UnservicedRequests.Count}"); + yield return new(ChatRole.Assistant, $"Remaining: {traSessin.UnservicedRequests.Count}"); } } else @@ -107,12 +107,12 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp (string id, TRequest request) = requests[i]; if (servicedIndicies.Contains(i)) { - traThread.PairedRequests.Add(id); + traSessin.PairedRequests.Add(id); pairedResponses.Add(strategy.CreatePairedResponse(request)); } else { - traThread.UnservicedRequests.Add(id, request); + traSessin.UnservicedRequests.Add(id, request); } yield return new(ChatRole.Assistant, [request]); @@ -120,20 +120,20 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp yield return new(ChatRole.Assistant, pairedResponses); - traThread.HasSentRequests = true; + traSessin.HasSentRequests = true; } } - private static TestRequestAgentThread ConvertThread(AgentThread thread) + private static TestRequestAgentSession ConvertSession(AgentSession session) where TRequest : AIContent where TResponse : AIContent { - if (thread is not TestRequestAgentThread traThread) + if (session is not TestRequestAgentSession traSession) { - throw new ArgumentException($"Bad AgentThread type: Expected {typeof(TestRequestAgentThread)}, got {thread.GetType()}.", nameof(thread)); + throw new ArgumentException($"Bad AgentSession type: Expected {typeof(TestRequestAgentSession)}, got {session.GetType()}.", nameof(session)); } - return traThread; + return traSession; } private sealed class FunctionCallStrategy : IRequestResponseStrategy @@ -153,19 +153,19 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp } } - public void ProcessResponse(FunctionResultContent response, TestRequestAgentThread thread) + public void ProcessResponse(FunctionResultContent response, TestRequestAgentSession session) { - if (thread.UnservicedRequests.TryGetValue(response.CallId, out FunctionCallContent? request)) + if (session.UnservicedRequests.TryGetValue(response.CallId, out FunctionCallContent? request)) { response.Result.As().Should().Be(request); - thread.ServicedRequests.Add(response.CallId); - thread.UnservicedRequests.Remove(response.CallId); + session.ServicedRequests.Add(response.CallId); + session.UnservicedRequests.Remove(response.CallId); } - else if (thread.ServicedRequests.Contains(response.CallId)) + else if (session.ServicedRequests.Contains(response.CallId)) { throw new InvalidOperationException($"Seeing duplicate response with id {response.CallId}"); } - else if (thread.PairedRequests.Contains(response.CallId)) + else if (session.PairedRequests.Contains(response.CallId)) { throw new InvalidOperationException($"Seeing explicit response to initially paired request with id {response.CallId}"); } @@ -198,9 +198,9 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp } } - public void ProcessResponse(UserInputResponseContent response, TestRequestAgentThread thread) + public void ProcessResponse(UserInputResponseContent response, TestRequestAgentSession session) { - if (thread.UnservicedRequests.TryGetValue(response.Id, out UserInputRequestContent? request)) + if (session.UnservicedRequests.TryGetValue(response.Id, out UserInputRequestContent? request)) { if (request is not FunctionApprovalRequestContent approvalRequest) { @@ -214,14 +214,14 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp approvalResponse.Approved.Should().BeTrue(); approvalResponse.FunctionCall.As().Should().Be(approvalRequest.FunctionCall); - thread.ServicedRequests.Add(response.Id); - thread.UnservicedRequests.Remove(response.Id); + session.ServicedRequests.Add(response.Id); + session.UnservicedRequests.Remove(response.Id); } - else if (thread.ServicedRequests.Contains(response.Id)) + else if (session.ServicedRequests.Contains(response.Id)) { throw new InvalidOperationException($"Seeing duplicate response with id {response.Id}"); } - else if (thread.PairedRequests.Contains(response.Id)) + else if (session.PairedRequests.Contains(response.Id)) { throw new InvalidOperationException($"Seeing explicit response to initially paired request with id {response.Id}"); } @@ -239,15 +239,15 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp IEnumerable<(string, TRequest)> CreateRequests(int count); TResponse CreatePairedResponse(TRequest request); - void ProcessResponse(TResponse response, TestRequestAgentThread thread); + void ProcessResponse(TResponse response, TestRequestAgentSession session); } - protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { return requestType switch { - TestAgentRequestType.FunctionCall => this.RunStreamingAsync(new FunctionCallStrategy(), messages, thread, options, cancellationToken), - TestAgentRequestType.UserInputRequest => this.RunStreamingAsync(new FunctionApprovalStrategy(), messages, thread, options, cancellationToken), + TestAgentRequestType.FunctionCall => this.RunStreamingAsync(new FunctionCallStrategy(), messages, session, options, cancellationToken), + TestAgentRequestType.UserInputRequest => this.RunStreamingAsync(new FunctionApprovalStrategy(), messages, session, options, cancellationToken), _ => throw new NotSupportedException($"Unknown AgentRequestType {requestType}"), }; } @@ -267,14 +267,14 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp where TRequest : AIContent where TResponse : AIContent { - this.LastThread.Should().NotBeNull(); - TestRequestAgentThread traThread = ConvertThread(this.LastThread); + this.LastSession.Should().NotBeNull(); + TestRequestAgentSession traSession = ConvertSession(this.LastSession); - requests.Should().HaveCount(traThread.UnservicedRequests.Count); + requests.Should().HaveCount(traSession.UnservicedRequests.Count); foreach (TRequest request in requests) { string requestId = RetrieveId(request); - traThread.UnservicedRequests.Should().ContainKey(requestId); + traSession.UnservicedRequests.Should().ContainKey(requestId); yield return strategy.CreatePairedResponse(request); } } @@ -327,11 +327,11 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp } } - private sealed class TestRequestAgentThread : InMemoryAgentThread + private sealed class TestRequestAgentSession : InMemoryAgentSession where TRequest : AIContent where TResponse : AIContent { - public TestRequestAgentThread() + public TestRequestAgentSession() { } @@ -341,17 +341,17 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp public HashSet PairedRequests { get; } = new(); private static JsonElement DeserializeAndExtractState(JsonElement serializedState, - out TestRequestAgentThreadState state, + out TestRequestAgentSessionState state, JsonSerializerOptions? jsonSerializerOptions = null) { - state = JsonSerializer.Deserialize(serializedState, jsonSerializerOptions) - ?? throw new ArgumentException("Unable to deserialize thread state."); + state = JsonSerializer.Deserialize(serializedState, jsonSerializerOptions) + ?? throw new ArgumentException("Unable to deserialize session state."); - return state.ThreadState; + return state.SessionState; } - public TestRequestAgentThread(JsonElement element, JsonSerializerOptions? jsonSerializerOptions = null) - : base(DeserializeAndExtractState(element, out TestRequestAgentThreadState state, jsonSerializerOptions)) + public TestRequestAgentSession(JsonElement element, JsonSerializerOptions? jsonSerializerOptions = null) + : base(DeserializeAndExtractState(element, out TestRequestAgentSessionState state, jsonSerializerOptions)) { this.UnservicedRequests = state.UnservicedRequests.ToDictionary( keySelector: item => item.Key, @@ -363,14 +363,14 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { - JsonElement threadState = base.Serialize(jsonSerializerOptions); + JsonElement sessionState = base.Serialize(jsonSerializerOptions); Dictionary portableUnservicedRequests = this.UnservicedRequests.ToDictionary( keySelector: item => item.Key, elementSelector: item => new PortableValue(item.Value)); - TestRequestAgentThreadState state = new(threadState, portableUnservicedRequests, this.ServicedRequests, this.PairedRequests); + TestRequestAgentSessionState state = new(sessionState, portableUnservicedRequests, this.ServicedRequests, this.PairedRequests); return JsonSerializer.SerializeToElement(state, jsonSerializerOptions); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs index eed0d72dac..8c8a543d2c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs @@ -32,32 +32,32 @@ public class WorkflowHostSmokeTests { private sealed class AlwaysFailsAIAgent(bool failByThrowing) : AIAgent { - private sealed class Thread : InMemoryAgentThread + private sealed class Session : InMemoryAgentSession { - public Thread() { } + public Session() { } - public Thread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) - : base(serializedThread, jsonSerializerOptions) + public Session(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedSession, jsonSerializerOptions) { } } - public override ValueTask DeserializeThreadAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) + public override ValueTask DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { - return new(new Thread(serializedThread, jsonSerializerOptions)); + return new(new Session(serializedSession, jsonSerializerOptions)); } - public override ValueTask GetNewThreadAsync(CancellationToken cancellationToken = default) + public override ValueTask GetNewSessionAsync(CancellationToken cancellationToken = default) { - return new(new Thread()); + return new(new Session()); } - protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return await this.RunStreamingAsync(messages, thread, options, cancellationToken) + return await this.RunStreamingAsync(messages, session, options, cancellationToken) .ToAgentResponseAsync(cancellationToken); } - protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { const string ErrorMessage = "Simulated agent failure."; if (failByThrowing) diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs index e2d174c4c9..a90f49f428 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs @@ -23,11 +23,11 @@ public class OpenAIAssistantFixture : IChatClientAgentFixture public IChatClient ChatClient => this._agent.ChatClient; - public async Task> GetChatHistoryAsync(AgentThread thread) + public async Task> GetChatHistoryAsync(AgentSession session) { - var typedThread = (ChatClientAgentThread)thread; + var typedSession = (ChatClientAgentSession)session; List messages = []; - await foreach (var agentMessage in this._assistantClient!.GetMessagesAsync(typedThread.ConversationId, new() { Order = MessageCollectionOrder.Ascending })) + await foreach (var agentMessage in this._assistantClient!.GetMessagesAsync(typedSession.ConversationId, new() { Order = MessageCollectionOrder.Ascending })) { messages.Add(new() { @@ -68,12 +68,12 @@ public class OpenAIAssistantFixture : IChatClientAgentFixture public Task DeleteAgentAsync(ChatClientAgent agent) => this._assistantClient!.DeleteAssistantAsync(agent.Id); - public Task DeleteThreadAsync(AgentThread thread) + public Task DeleteSessionAsync(AgentSession session) { - var typedThread = (ChatClientAgentThread)thread; - if (typedThread?.ConversationId is not null) + var typedSession = (ChatClientAgentSession)session; + if (typedSession?.ConversationId is not null) { - return this._assistantClient!.DeleteThreadAsync(typedThread.ConversationId); + return this._assistantClient!.DeleteThreadAsync(typedSession.ConversationId); } return Task.CompletedTask; diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs index f085187a21..eeb60620c0 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs @@ -28,16 +28,16 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture public IChatClient ChatClient => this._agent.ChatClient; - public async Task> GetChatHistoryAsync(AgentThread thread) + public async Task> GetChatHistoryAsync(AgentSession session) { - var typedThread = (ChatClientAgentThread)thread; + var typedSession = (ChatClientAgentSession)session; - if (typedThread.ChatHistoryProvider is null) + if (typedSession.ChatHistoryProvider is null) { return []; } - return (await typedThread.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); + return (await typedSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); } public Task CreateChatClientAgentAsync( @@ -60,7 +60,7 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture // Chat Completion does not require/support deleting agents, so this is a no-op. Task.CompletedTask; - public Task DeleteThreadAsync(AgentThread thread) => + public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting threads, so this is a no-op. Task.CompletedTask; diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index 9e8db6fb21..2006404239 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -25,14 +25,14 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture public IChatClient ChatClient => this._agent.ChatClient; - public async Task> GetChatHistoryAsync(AgentThread thread) + public async Task> GetChatHistoryAsync(AgentSession session) { - var typedThread = (ChatClientAgentThread)thread; + var typedSession = (ChatClientAgentSession)session; if (store) { - var inputItems = await this._openAIResponseClient.GetResponseInputItemsAsync(typedThread.ConversationId).ToListAsync(); - var response = await this._openAIResponseClient.GetResponseAsync(typedThread.ConversationId); + var inputItems = await this._openAIResponseClient.GetResponseInputItemsAsync(typedSession.ConversationId).ToListAsync(); + var response = await this._openAIResponseClient.GetResponseAsync(typedSession.ConversationId); var responseItem = response.Value.OutputItems.FirstOrDefault()!; // Take the messages that were the chat history leading up to the current response @@ -50,12 +50,12 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture return [.. previousMessages, responseMessage]; } - if (typedThread.ChatHistoryProvider is null) + if (typedSession.ChatHistoryProvider is null) { return []; } - return (await typedThread.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); + return (await typedSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList(); } private static ChatMessage ConvertToChatMessage(ResponseItem item) @@ -90,7 +90,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture // Chat Completion does not require/support deleting agents, so this is a no-op. Task.CompletedTask; - public Task DeleteThreadAsync(AgentThread thread) => + public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting threads, so this is a no-op. Task.CompletedTask;