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 @@ private static async Task HandleCommandsAsync(CancellationToken cancellationToke // 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 @@ private static async Task HandleCommandsAsync(CancellationToken cancellationToke 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 @@ private static async Task HandleCommandsAsync(CancellationToken cancellationToke 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 @@ private static async Task HandleCommandsAsync(CancellationToken cancellationToke // 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 @@ private static async Task HandleCommandsAsync(CancellationToken cancellationToke 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 @@ public AgenticUIAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializerOp 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 @@ public PredictiveStateUpdatesAgent(AIAgent innerAgent, JsonSerializerOptions jso 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 @@ public SharedStateAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializer 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA 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 @@ 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 @@ public A2AAgentClient(ILogger logger, Uri baseUri) 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 @@ public abstract IAsyncEnumerable RunStreamingAsync( 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 @@ public override async IAsyncEnumerable RunStreamingAsync( 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 sealed record TextResponse(string Text); 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 @@ ? 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 async Task RunOrchestrationAsync([OrchestrationTrigger] Ta // 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 async Task RunOrchestrationAsync([OrchestrationTrigger] Ta 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 async Task RunOrchestrationAsync([OrchestrationTrigger] Ta // 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 async Task RunOrchestrationAsync([OrchestrationTrigger] Ta 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 async Task RunOrchestrationAsync( // 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 async Task RunOrchestrationAsync( // 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 @@ The content was rejected by a human reviewer. Please rewrite the article incorpo 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 async Task RunOrchestrationAsync( // 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 async Task RunOrchestrationAsync( // 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 @@ The content was rejected by a human reviewer. Please rewrite the article incorpo 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 @@ public async Task GetWorkflowStatusAsync( { 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 async Task CreateAsync( 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 async ValueTask OnStreamingResponseUpdateAsync( "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.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 @@ { 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 @@ ? 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 @@ when given an improved sentence you polish it further. 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 @@ The content was rejected by a human reviewer. Please rewrite the article incorpo 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 @@ The content was rejected by a human reviewer. Please rewrite the article incorpo 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 @@ static async Task SubmitHumanFeedbackAsync( 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 @@ static async Task SubmitHumanFeedbackAsync( { 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 @@ async Task ReadStreamTask(string conversationId, string? cursor, CancellationTok 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 @@ async Task ReadStreamTask(string conversationId, string? cursor, CancellationTok // 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 async ValueTask OnStreamingResponseUpdateAsync( 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 @@ // 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 @@ 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 @@ 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 @@ // 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 @@ } 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 @@ 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 @@ // 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 @@ } 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 @@ static string GetUserLocation() 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 @@ static string GetUserLocation() // 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 @@ static string GetUserLocation() } 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 @@ 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 @@ 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 @@ public ServerFunctionApprovalClientAgent(AIAgent innerAgent, JsonSerializerOptio 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA // 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 @@ public ServerFunctionApprovalAgent(AIAgent innerAgent, JsonSerializerOptions jso 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA // 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 @@ }; 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 @@ // 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 @@ } 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 @@ public StatefulAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializerOpt /// 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA 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 @@ public SharedStateAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializer 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 @@ stateObj is not JsonElement state || 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 @@ stateObj is not JsonElement state || 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 @@ stateObj is not JsonElement state || // 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 @@ stateObj is not JsonElement state || 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 @@ static async Task GetWeatherAsync([Description("The location to get the .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 @@ static async Task GetWeatherAsync([Description("The location to get the 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 @@ 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 @@ 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 @@ internal sealed class UpperCaseParrotAgent : AIAgent { 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 @@ protected override async Task RunCoreAsync(IEnumerable 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 @@ private static IEnumerable CloneAndToUpperCase(IEnumerable - /// 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 @@ 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 @@ static string GetWeather([Description("The location to get the weather for.")] s .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 @@ 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 @@ { 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 @@ : 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 @@ .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 @@ 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 OpenAIChatClientAgent( /// 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 virtual async Task RunAsync( /// 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 OpenAIResponseClientAgent( /// 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 virtual async Task RunAsync( /// 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 virtual async IAsyncEnumerable RunStreamingAsync } /// - 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 @@ 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("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 @@ .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 @@ 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 @@ 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 @@ 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 @@ .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 @@ static string GetWeather([Description("The location to get the weather for.")] s .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 @@ static string GetWeather([Description("The location to get the weather for.")] s .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 @@ .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 @@ 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 @@ public VectorChatHistoryProvider(VectorStore vectorStore, JsonElement serialized 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 @@ public override async ValueTask> InvokingAsync(Invoking 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 @@ public override async ValueTask InvokedAsync(InvokedContext context, Cancellatio 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 @@ public override async ValueTask InvokedAsync(InvokedContext context, Cancellatio 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 @@ private sealed class ChatHistoryItem 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 @@ /// 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 @@ public async Task RunAsync(CancellationToken cancellationToken) } // 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 @@ 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 @@ // 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 @@ static string GetDateTime() .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 @@ static string GetDateTime() 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 @@ static string GetDateTime() .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 @@ static string GetDateTime() } // 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 @@ static string FilterPii(string content) } // 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 @@ static string FilterContent(string content) } // 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 @@ // 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 @@ // 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 @@ // 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 @@ 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 @@ You remind users of upcoming calendar events when the user interacts with you. }); // 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"); - -// 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(); +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 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 @@ // 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 @@ static string GetWeather([Description("The location to get the weather for.")] s 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 @@ static string GetWeather([Description("The location to get the weather for.")] s // 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 @@ static string GetWeather([Description("The location to get the weather for.")] s .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 @@ 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 @@ .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 @@ /// 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 @@ public async Task RunAsync(CancellationToken cancellationToken) } // 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 @@ 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 @@ static string GetWeather([Description("The location to get the weather for.")] s 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 @@ static string GetDateTime() .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 @@ static string GetDateTime() // 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 @@ static string GetDateTime() } // 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 @@ static string FilterPii(string content) } // 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 @@ static string FilterContent(string content) } // 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 @@ .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 @@ 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 @@ 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 @@ .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 @@ protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => 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 @@ public async ValueTask HandleAsync(FeedbackResult message, IWorkfl 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 @@ public FeedbackExecutor(string id, IChatClient chatClient) : base(id) 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 @@ public override async ValueTask HandleAsync(SloganResult message, IWorkflowConte 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 @@ private static async Task Main() // 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 @@ private static async Task Main() 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 @@ public static async Task Main(string[] args) AIAgent agent = aiProjectClient.AsAIAgent(agentVersion); - AgentThread thread = await agent.GetNewThreadAsync(); + AgentSession session = await agent.GetNewSessionAsync(); ProjectConversation conversation = await aiProjectClient @@ -65,7 +65,7 @@ await aiProjectClient }; 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 @@ private static async Task Main() { 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 @@ private static async Task Main() 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 @@ private async Task MessageActivityAsync(ITurnContext turnContext, ITurnState tur 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 @@ private async Task MessageActivityAsync(ITurnContext turnContext, ITurnState tur } // 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 WeatherForecastAgent(IChatClient chatClient) { } - 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 A2AAgent(A2AClient a2aClient, string? id = null, string? name = null, str } /// - 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 @@ protected override async Task RunCoreAsync(IEnumerable RunCoreAsync(IEnumerable RunCoreAsync(IEnumerable RunCoreAsync(IEnumerable - 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA MessageSendParams sendParams = new() { - Message = CreateA2AMessage(typedThread, messages), + Message = CreateA2AMessage(typedSession, messages), Metadata = options?.AdditionalProperties?.ToA2AMetadata() }; @@ -199,7 +199,7 @@ protected override async IAsyncEnumerable RunCoreStreamingA } } - UpdateThread(typedThread, contextId, taskId); + UpdateSession(typedSession, contextId, taskId); } /// @@ -211,57 +211,57 @@ protected override async IAsyncEnumerable RunCoreStreamingA /// 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 @@ 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 @@ internal A2AAgentThread(JsonElement serializedThreadState, JsonSerializerOptions /// 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 @@ private static JsonSerializerOptions CreateDefaultOptions() 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 override async IAsyncEnumerable GetStreamingResponseA 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 async IAsyncEnumerable GetStreamingResponseAsync( } } - // 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 async IAsyncEnumerable GetStreamingResponseAsync( 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 Task RunAsync( /// 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 Task RunAsync( /// 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 Task RunAsync( /// /// /// 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 Task RunAsync( /// /// /// 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 IAsyncEnumerable RunStreamingAsync( /// 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 IAsyncEnumerable RunStreamingAsync( /// 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 IAsyncEnumerable RunStreamingAsync( /// 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 IAsyncEnumerable RunStreamingAsync( /// 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 @@ private static JsonSerializerOptions CreateDefaultOptions() [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 IList Messages /// /// 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 IList Contents /// 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 AgentRunOptions(AgentRunOptions options) /// /// 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 @@ protected 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 virtual JsonElement Serialize(JsonSerializerOptions? jsonSerializerOption : 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 @@ protected DelegatingAIAgent(AIAgent innerAgent) } /// - 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 @@ 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 override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptio { 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 override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptio [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 @@ 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 @@ protected ServiceIdAgentThread( /// 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 CopilotStudioAgent(CopilotClient client, ILoggerFactory? loggerFactory = } /// - 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 @@ protected override async Task RunCoreAsync( /// 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 @@ public async Task Run(RunRequest request) // 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 @@ internal DurableAIAgent(TaskOrchestrationContext context, string agentName) } /// - /// 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 @@ protected override async Task RunCoreAsync( 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 @@ protected override async Task RunCoreAsync( try { return await this._context.Entities.CallEntityAsync( - durableThread.SessionId, + durableSession.SessionId, nameof(AgentEntity.Run), request); } @@ -126,19 +126,19 @@ protected override async Task RunCoreAsync( /// 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA /// 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA /// 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 async Task> RunAsync( /// 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 async Task> RunAsync( [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 async Task> RunAsync( // 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 @@ protected override async Task RunCoreAsync( } 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 @@ protected override async Task RunCoreAsync( 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 @@ internal 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 @@ internal 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 @@ private static JsonSerializerOptions CreateDefaultOptions() // 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 override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptio { 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 @@ internal static DurableAgentThread Deserialize(JsonElement serializedThread, Jso 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 @@ protected override async Task RunCoreAsync( 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 IEndpointConventionBuilder MapA2A(this IEndpointRouteBuilder endpo 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 IEndpointConventionBuilder MapA2A(this IEndpointRouteBuilder endpo 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 ITaskManager MapA2A( 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 @@ async Task OnMessageReceivedAsync(MessageSendParams messageSendPara /// 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 @@ public static async Task RunAgentHttpAsync( 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 @@ public static async Task RunAgentHttpAsync( { 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 @@ public static async Task RunAgentHttpAsync( // 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 @@ await agentProxy.RunAsync( 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 @@ private static async Task CreateErrorResponseAsync( /// 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 @@ private static async Task CreateSuccessResponseAsync( /// /// 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 @@ private static string GetAgentName(FunctionContext context) /// 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 @@ private sealed record ErrorResponse( /// 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 @@ private sealed record AgentRunSuccessResponse( /// 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 @@ 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 @@ 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 async Task RunAsync(this AIAgent agent, IEnumerabl /// /// 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 async Task RunAsync(this AIAgent agent, IEnumerabl /// 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 AsyncCollectionResult RunStreamingA /// /// 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 AsyncCollectionResult RunStreamingA /// 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 async Task RunAsync(this AIAgent agent, IEnumerabl /// /// 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 async Task RunAsync(this AIAgent agent, IEnumerabl /// 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 @@ public PurviewAgent(AIAgent innerAgent, PurviewWrapper purviewWrapper) } /// - 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 @@ public PurviewWrapper(IScopedContentProcessor scopedProcessor, PurviewSettings p 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 @@ public async Task ProcessChatContentAsync(IEnumerable /// 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 @@ public async Task ProcessAgentContentAsync(IEnumerable - 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 @@ private static bool TryGetUserIdFromPayload(IEnumerable messages, o /// 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 @@ private async Task> MapMessageToPCRequestsAsync(IEnu 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 @@ private ValueTask HandleFunctionResultAsync( 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 @@ private async ValueTask EnsureThreadAsync(IWorkflowContext context, 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 @@ protected internal override async ValueTask OnCheckpointRestoredAsync(IWorkflowC 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 @@ private async ValueTask InvokeAgentAsync(IEnumerable // 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 @@ await this.EnsureThreadAsync(context, cancellationToken).ConfigureAwait(false), { // 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 @@ private async ValueTask ValidateWorkflowAsync() 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 @@ 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 @@ public WorkflowThread(Workflow workflow, string runId, IWorkflowExecutionEnviron 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 @@ public WorkflowThread(Workflow workflow, JsonElement serializedThread, IWorkflow 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 @@ public WorkflowThread(Workflow workflow, JsonElement serializedThread, IWorkflow 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 @@ public WorkflowThread(Workflow workflow, JsonElement serializedThread, IWorkflow 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 @@ public AgentResponseUpdate CreateUpdate(string responseId, object raw, ChatMessa 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 @@ IAsyncEnumerable InvokeStageAsync( /// 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 @@ private static JsonSerializerOptions CreateDefaultOptions() // 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 AIAgentBuilder Use(Func agentFactory) /// /// 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 AIAgentBuilder Use(Func agentFactory) /// 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 AIAgentBuilder Use(Func, AgentThread?, AgentRunO /// /// 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 AIAgentBuilder AsBuilder(this AIAgent innerAgent) /// 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 AIAgentBuilder AsBuilder(this AIAgent innerAgent) /// /// /// 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 @@ async Task InvokeAgentAsync( ? 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 @@ private static JsonSerializerOptions CreateDefaultOptions() 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 @@ public AnonymousDelegatingAIAgent( /// 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 @@ public AnonymousDelegatingAIAgent( /// protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -95,19 +95,19 @@ protected override Task RunCoreAsync( 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 @@ await this._sharedFunc( } 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 @@ await this._sharedFunc( /// protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -150,9 +150,9 @@ async Task ProcessAsync() 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 @@ await this._sharedFunc(messages, thread, options, async (messages, thread, optio } 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 ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options, /// protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -168,7 +168,7 @@ static AgentResponse CreateResponse(ChatResponse chatResponse) }; } - return this.RunCoreAsync(GetResponseAsync, CreateResponse, messages, thread, options, cancellationToken); + return this.RunCoreAsync(GetResponseAsync, CreateResponse, messages, session, options, cancellationToken); } /// @@ -198,19 +198,19 @@ private static IChatClient ApplyRunOptionsTransformations(AgentRunOptions? optio /// 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA } 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA } 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA } 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA : 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 override async ValueTask GetNewThreadAsync(CancellationToken ? 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 override async ValueTask GetNewThreadAsync(CancellationToken } /// - /// 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 override async ValueTask GetNewThreadAsync(CancellationToken /// 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 async ValueTask GetNewThreadAsync(string conversationId, Can } /// - /// 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 async ValueTask GetNewThreadAsync(ChatHistoryProvider chatHi } /// - 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 override async ValueTask DeserializeThreadAsync(JsonElement 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 @@ private async Task RunCoreAsync, 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 @@ private async Task RunCoreAsync ?? 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 @@ private async Task RunCoreAsync RunCoreAsync RunCoreAsync 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 @@ await thread.AIContextProvider.InvokedAsync(new(inputMessages, aiContextProvider /// 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 @@ await thread.AIContextProvider.InvokedAsync(new(inputMessages, aiContextProvider } /// - /// 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 @@ private async Task 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 @@ private async Task // 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 @@ private async Task } } - // 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 @@ private static Task NotifyChatHistoryProviderOfFailureAsync( 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 @@ private static Task NotifyChatHistoryProviderOfFailureAsync( } private static Task NotifyChatHistoryProviderOfNewMessagesAsync( - ChatClientAgentThread thread, + ChatClientAgentSession session, IEnumerable requestMessages, IEnumerable? chatHistoryProviderMessages, IEnumerable? aiContextProviderMessages, @@ -835,7 +835,7 @@ private static Task NotifyChatHistoryProviderOfNewMessagesAsync( 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 @@ private static Task NotifyChatHistoryProviderOfNewMessagesAsync( 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 @@ private static Task NotifyChatHistoryProviderOfNewMessagesAsync( 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 IAsyncEnumerable RunStreamingAsync( /// 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 Task> RunAsync( /// 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 Task> RunAsync( /// 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 Task> RunAsync( /// 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 @@ internal set 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 @@ internal set 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 @@ internal set 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 @@ internal set /// 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 override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptio 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 override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptio 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 Task> RunAsync( /// 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 Task> RunAsync( { _ = 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 Task> RunAsync( /// 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 Task> RunAsync( { _ = 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 Task> RunAsync( /// /// /// 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 @@ static ChatClientAgentResponse CreateResponse(ChatResponse chatResponse) }; } - 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 FunctionInvocationDelegatingAgent(AIAgent innerAgent, Func 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 JsonSerializerOptions JsonSerializerOptions /// 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 @@ protected override async Task RunCoreAsync( 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 @@ protected override async Task RunCoreAsync( /// 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA 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 @@ private ChatHistoryMemoryProvider( 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 override async ValueTask InvokingAsync(InvokingContext context { 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 override async ValueTask InvokedAsync(InvokedContext context, Cancellatio ["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 override async ValueTask InvokedAsync(InvokedContext context, Cancellatio { 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 @@ internal async Task SearchTextAsync(string userQuestion, CancellationTok 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 @@ internal async Task SearchTextAsync(string userQuestion, CancellationTok 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 @@ internal async Task SearchTextAsync(string userQuestion, CancellationTok 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 @@ internal async Task SearchTextAsync(string userQuestion, CancellationTok 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 ChatHistoryMemoryProviderScope(ChatHistoryMemoryProviderScope sourceScope 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 ChatHistoryMemoryProviderScope(ChatHistoryMemoryProviderScope sourceScope 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 bool EnableSensitiveData /// 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 @@ protected override async Task RunCoreAsync( /// 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 @@ private void UpdateCurrentActivity(Activity? previousActivity) /// 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 async Task GetResponseAsync( 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 async IAsyncEnumerable GetStreamingResponseAsync( 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 virtual async Task RunWithInstructionsAndNoMessageReturnsExpectedResultAs { // 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 responseUpdates = await agent.RunStreamingAsync(thread).ToListAsync(); + var responseUpdates = await agent.RunStreamingAsync(session).ToListAsync(); // Assert var chatResponseText = string.Concat(responseUpdates.Select(x => x.Text)); @@ -53,14 +53,14 @@ public virtual async Task RunWithFunctionsInvokesFunctionsAndReturnsExpectedResu 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 responseUpdates = await agent.RunStreamingAsync( new ChatMessage(ChatRole.User, questionAndAnswer.Question), - thread).ToListAsync(); + session).ToListAsync(); // Assert var chatResponseText = string.Concat(responseUpdates.Select(x => 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 virtual async Task RunWithInstructionsAndNoMessageReturnsExpectedResultAs { // 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 virtual async Task RunWithFunctionsInvokesFunctionsAndReturnsExpectedResu 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 virtual async Task RunWithNoMessageDoesNotFailAsync() { // 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 virtual async Task RunWithStringReturnsExpectedResultAsync() { // 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 virtual async Task RunWithChatMessageReturnsExpectedResultAsync() { // 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 virtual async Task RunWithChatMessagesReturnsExpectedResultAsync() { // 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 virtual async Task RunWithChatMessagesReturnsExpectedResultAsync() 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 virtual async Task RunWithChatMessagesReturnsExpectedResultAsync() } [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 virtual async Task ThreadMaintainsHistoryAsync() 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 virtual async Task RunWithNoMessageDoesNotFailAsync() { // 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 virtual async Task RunWithStringReturnsExpectedResultAsync() { // 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 virtual async Task RunWithChatMessageReturnsExpectedResultAsync() { // 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 virtual async Task RunWithChatMessagesReturnsExpectedResultAsync() { // 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 virtual async Task RunWithChatMessagesReturnsExpectedResultAsync() 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 virtual async Task RunWithChatMessagesReturnsExpectedResultAsync() } [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 @@ 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 AnthropicChatCompletionFixture(bool useReasoningChatModel, bool useBeta) 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 Task DeleteAgentAsync(ChatClientAgent agent) => // 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 async Task CreateConversationAsync() 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 static string GenerateUniqueAgentName(string baseName) => 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 async Task CreateChatClientAgentAsync( 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 @@ namespace CopilotStudio.IntegrationTests; // 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/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 @@ namespace CopilotStudio.IntegrationTests; // 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 async Task RunAsync_WithValidUserMessage_RunsSuccessfullyAsync() } [Fact] - public async Task RunAsync_WithNewThread_UpdatesThreadConversationIdAsync() + public async Task RunAsync_WithNewSession_UpdatesSessionConversationIdAsync() { // Arrange this._handler.ResponseToReturn = new AgentMessage @@ -148,19 +148,19 @@ public async Task RunAsync_WithNewThread_UpdatesThreadConversationIdAsync() 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 async Task RunAsync_WithExistingThread_SetConversationIdToMessageAsync() 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 async Task RunAsync_WithExistingThread_SetConversationIdToMessageAsync() } [Fact] - public async Task RunAsync_WithThreadHavingDifferentContextId_ThrowsInvalidOperationExceptionAsync() + public async Task RunAsync_WithSessionHavingDifferentContextId_ThrowsInvalidOperationExceptionAsync() { // Arrange var inputMessages = new List @@ -201,12 +201,12 @@ public async Task RunAsync_WithThreadHavingDifferentContextId_ThrowsInvalidOpera 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 async Task RunStreamingAsync_WithValidUserMessage_YieldsAgentResponseUpda } [Fact] - public async Task RunStreamingAsync_WithThread_UpdatesThreadConversationIdAsync() + public async Task RunStreamingAsync_WithSession_UpdatesSessionConversationIdAsync() { // Arrange var inputMessages = new List @@ -272,21 +272,21 @@ public async Task RunStreamingAsync_WithThread_UpdatesThreadConversationIdAsync( 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 async Task RunStreamingAsync_WithExistingThread_SetConversationIdToMessag 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 async Task RunStreamingAsync_WithExistingThread_SetConversationIdToMessag } [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 async Task RunStreamingAsync_WithThreadHavingDifferentContextId_ThrowsInv // 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 async Task RunAsync_WithContinuationToken_CallsGetTaskAsyncAsync() } [Fact] - public async Task RunAsync_WithTaskInThreadAndMessage_AddTaskAsReferencesToMessageAsync() + public async Task RunAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMessageAsync() { // Arrange this._handler.ResponseToReturn = new AgentMessage @@ -440,13 +440,13 @@ public async Task RunAsync_WithTaskInThreadAndMessage_AddTaskAsReferencesToMessa 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 async Task RunAsync_WithTaskInThreadAndMessage_AddTaskAsReferencesToMessa } [Fact] - public async Task RunAsync_WithAgentTask_UpdatesThreadTaskIdAsync() + public async Task RunAsync_WithAgentTask_UpdatesSessionTaskIdAsync() { // Arrange this._handler.ResponseToReturn = new AgentTask @@ -466,14 +466,14 @@ public async Task RunAsync_WithAgentTask_UpdatesThreadTaskIdAsync() 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 async Task RunAsync_WithAgentTaskResponse_ReturnsTaskResponseCorrectlyAsy } }; - 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 async Task RunAsync_WithAgentTaskResponse_ReturnsTaskResponseCorrectlyAsy 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 @@ await Assert.ThrowsAsync(async () => } [Fact] - public async Task RunStreamingAsync_WithTaskInThreadAndMessage_AddTaskAsReferencesToMessageAsync() + public async Task RunStreamingAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMessageAsync() { // Arrange this._handler.StreamingResponseToReturn = new AgentMessage @@ -586,11 +586,11 @@ public async Task RunStreamingAsync_WithTaskInThreadAndMessage_AddTaskAsReferenc 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 async Task RunStreamingAsync_WithTaskInThreadAndMessage_AddTaskAsReferenc } [Fact] - public async Task RunStreamingAsync_WithAgentTask_UpdatesThreadTaskIdAsync() + public async Task RunStreamingAsync_WithAgentTask_UpdatesSessionTaskIdAsync() { // Arrange this._handler.StreamingResponseToReturn = new AgentTask @@ -613,17 +613,17 @@ public async Task RunStreamingAsync_WithAgentTask_UpdatesThreadTaskIdAsync() 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 async Task RunStreamingAsync_WithAgentTask_YieldsResponseUpdateAsync() ] }; - 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 async Task RunStreamingAsync_WithAgentTask_YieldsResponseUpdateAsync() 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 async Task RunStreamingAsync_WithTaskStatusUpdateEvent_YieldsResponseUpda 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 async Task RunStreamingAsync_WithTaskStatusUpdateEvent_YieldsResponseUpda 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 async Task RunStreamingAsync_WithTaskArtifactUpdateEvent_YieldsResponseUp } }; - 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 async Task RunStreamingAsync_WithTaskArtifactUpdateEvent_YieldsResponseUp 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 async Task RunAsync_WithAllowBackgroundResponsesAndNoThread_ThrowsInvalid } [Fact] - public async Task RunStreamingAsync_WithAllowBackgroundResponsesAndNoThread_ThrowsInvalidOperationExceptionAsync() + public async Task RunStreamingAsync_WithAllowBackgroundResponsesAndNoSession_ThrowsInvalidOperationExceptionAsync() { // Arrange var inputMessages = new List @@ -1001,18 +1001,18 @@ public async Task RunStreamingAsync_WithNullAdditionalProperties_DoesNotSetMetad } [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 async Task RunStreamingAsync_WithInvalidThreadType_ThrowsInvalidOperation 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 void Dispose() } /// - /// 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 async Task RunAsync_WithNullMessages_ThrowsArgumentNullExceptionAsync() } [Fact] - public async Task RunAsync_WithNullThread_CreatesNewThreadAsync() + public async Task RunAsync_WithNullSession_CreatesNewSessionAsync() { // Arrange using HttpClient httpClient = this.CreateMockHttpClient( @@ -95,7 +95,7 @@ public async Task RunAsync_WithNullThread_CreatesNewThreadAsync() 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 @@ await Assert.ThrowsAsync(async () => } [Fact] - public async Task RunStreamingAsync_WithNullThread_CreatesNewThreadAsync() + public async Task RunStreamingAsync_WithNullSession_CreatesNewSessionAsync() { // Arrange using HttpClient httpClient = this.CreateMockHttpClient( @@ -167,7 +167,7 @@ public async Task RunStreamingAsync_WithNullThread_CreatesNewThreadAsync() // 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 async Task RunStreamingAsync_ReturnsStreamingUpdates_AfterCompletionAsync 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 async Task RunStreamingAsync_ReturnsStreamingUpdates_AfterCompletionAsync } [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 async Task RunStreamingAsync_InvokesMultipleTools_InSingleTurnAsync() } [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 async Task RunStreamingAsync_UpdatesThreadWithToolMessages_AfterCompletio 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 async Task GetStreamingResponseAsync_ExtractsThreadId_FromServerResponseA // 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 async Task GetStreamingResponseAsync_ExtractsThreadId_FromServerResponseA 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 async Task GetResponseAsync_PreservesConversationId_ThroughStreamingPathA [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 async Task GetStreamingResponseAsync_UsesServerThreadId_WhenDifferentFrom } // 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 async Task GetStreamingResponseAsync_ExtractsThreadIdFromFunctionCall_OnS } // 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 async Task GetStreamingResponseAsync_MaintainsConsistentThreadId_AcrossMu [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 async Task GetStreamingResponseAsync_HandlesEmptyThreadId_GracefullyAsync 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 async Task GetStreamingResponseAsync_AdaptsToServerThreadIdChange_MidConv { // 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 async Task GetStreamingResponseAsync_AdaptsToServerThreadIdChange_MidConv } // 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 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 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 async Task InvokeWithoutMessageCallsMockedInvokeWithEmptyArrayAsync() 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 async Task InvokeWithoutMessageCallsMockedInvokeWithEmptyArrayAsync() .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 async Task InvokeWithStringMessageCallsMockedInvokeWithMessageInCollectio 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 async Task InvokeWithStringMessageCallsMockedInvokeWithMessageInCollectio .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 async Task InvokeWithSingleMessageCallsMockedInvokeWithMessageInCollectio 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 async Task InvokeWithSingleMessageCallsMockedInvokeWithMessageInCollectio .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 async Task InvokeStreamingWithoutMessageCallsMockedInvokeWithEmptyArrayAs 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 async Task InvokeStreamingWithoutMessageCallsMockedInvokeWithEmptyArrayAs .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 async Task InvokeStreamingWithStringMessageCallsMockedInvokeWithMessageIn 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 async Task InvokeStreamingWithStringMessageCallsMockedInvokeWithMessageIn .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 async Task InvokeStreamingWithSingleMessageCallsMockedInvokeWithMessageIn 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 async Task InvokeStreamingWithSingleMessageCallsMockedInvokeWithMessageIn .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 void GetService_Generic_ReturnsNullForUnrelatedType() #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 MockAgent(string? id = null) 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 @@ 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 void GetService_RequestingAgentThreadType_ReturnsThread() 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 void GetService_RequestingUnrelatedType_ReturnsNull() 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 void GetService_WithServiceKey_ReturnsNull() 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 void GetService_WithNullServiceType_ThrowsArgumentNullException() 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 void GetService_Generic_ReturnsCorrectType() 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 void GetService_Generic_ReturnsNullForUnrelatedType() #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 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 DelegatingAIAgentTests() .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._testStreamingResponses)); @@ -132,17 +132,17 @@ public void Description_DelegatesToInnerAgent() #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 async Task RunAsyncDefaultsToInnerAgentAsync() { // 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 async Task RunAsyncDefaultsToInnerAgentAsync() .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 async Task RunAsyncDefaultsToInnerAgentAsync() 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 async Task RunStreamingAsyncDefaultsToInnerAgentAsync() { // 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 async Task RunStreamingAsyncDefaultsToInnerAgentAsync() .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 async Task RunStreamingAsyncDefaultsToInnerAgentAsync() 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 @@ private sealed class TestDelegatingAIAgent(AIAgent innerAgent) : DelegatingAIAge 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 @@ 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 void Constructor_WithChatHistoryProvider_SetsProperty() 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 void Constructor_WithMessages_SetsProperty() 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 void Constructor_WithSerializedState_SetsProperty() // 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 void Constructor_WithInvalidJson_ThrowsArgumentException() 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 void Constructor_WithInvalidJson_ThrowsArgumentException() 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 void Serialize_ReturnsCorrectJson_WhenMessagesExist() 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 void Serialize_ReturnsEmptyMessages_WhenNoMessages() 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 async Task CreateAIAgent_WithExplicitMaxTokens_UsesProvidedValueAsync() 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 async Task CreateAIAgent_WithExplicitMaxTokens_UsesProvidedValueAsync() 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 async Task ChatClient_UsesDefaultConversationIdAsync() }); // 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 async Task ChatClient_UsesPerRequestConversationId_WhenNoDefaultConversat }); // 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 async Task ChatClient_UsesPerRequestConversationId_EvenWhenDefaultConvers }); // 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 async Task ChatClient_UsesPreviousResponseId_WhenConversationIsNotPrefixe }); // 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 TestAgentFactory(AIAgent? agentToReturn = null) 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 async Task EntityNamePrefixAsync() // 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 async Task EntityNamePrefixAsync() // 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 async Task RunAgentMethodNamesAllWorkAsync(string runAgentMethodName) // 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 @@ private sealed class TestOrchestrator : TaskOrchestrator 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 async Task SimplePromptAsync() 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 @@ async Task RunWorkflowAsync(TaskOrchestrationContext context, string nam { // 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 @@ async Task RunWorkflowAsync(TaskOrchestrationContext context, string nam 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 async Task EntityExpiresAfterTTLAsync() }); 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 async Task EntityTTLResetsOnInteractionAsync() }); 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 @@ await agentProxy.RunAsync( // 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 @@ private static Mock CreateAgentMock(Action optionsCal { 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 @@ private static Mock CreateAgentMockWithResponse(AgentResponse response) { 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 @@ private static async Task InvokeOnMessageReceivedAsync(ITaskManager 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 async Task ClientReceivesStreamedAssistantMessageAsync() 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 async Task ClientReceivesRunLifecycleEventsAsync() 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 async Task RunAsyncAggregatesStreamingUpdatesAsync() 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 async Task RunAsyncAggregatesStreamingUpdatesAsync() } [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 async Task MultiTurnConversationPreservesAllMessagesInThreadAsync() // 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 async Task AgentSendsMultipleMessagesInOneTurnAsync() 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 async Task UserSendsMultipleMessagesAtOnceAsync() 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 async Task UserSendsMultipleMessagesAtOnceAsync() 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 @@ protected override async Task RunCoreAsync( protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -323,15 +323,15 @@ protected override async IAsyncEnumerable RunCoreStreamingA } } - 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 @@ protected override async Task RunCoreAsync( protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -414,15 +414,15 @@ protected override async IAsyncEnumerable RunCoreStreamingA } } - 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 async Task ForwardedProps_AreParsedAndPassedToAgent_WhenProvidedInRequest // 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 async Task ForwardedProps_WithNestedObjects_AreCorrectlyParsedAsync() 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 async Task ForwardedProps_WithArrays_AreCorrectlyParsedAsync() 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 async Task ForwardedProps_WhenEmpty_DoesNotCauseErrorsAsync() 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 async Task ForwardedProps_WhenNotProvided_AgentStillWorksAsync() 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 async Task ForwardedProps_ReturnsValidSSEResponse_WithTextDeltaEventsAsyn 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 async Task ForwardedProps_WithMixedTypes_AreCorrectlyParsedAsync() 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 @@ public FakeForwardedPropsAgent() 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA 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 async Task StateSnapshot_IsReturnedAsDataContent_WithCorrectMediaTypeAsyn 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 async Task StateSnapshot_IsReturnedAsDataContent_WithCorrectMediaTypeAsyn 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 async Task StateSnapshot_HasCorrectAdditionalPropertiesAsync() 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 async Task StateSnapshot_HasCorrectAdditionalPropertiesAsync() 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 async Task ComplexState_WithNestedObjectsAndArrays_RoundTripsCorrectlyAsy 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 async Task ComplexState_WithNestedObjectsAndArrays_RoundTripsCorrectlyAsy 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 async Task StateSnapshot_CanBeUsedInSubsequentRequest_ForStateRoundTripAs 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 async Task StateSnapshot_CanBeUsedInSubsequentRequest_ForStateRoundTripAs 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 async Task StateSnapshot_CanBeUsedInSubsequentRequest_ForStateRoundTripAs 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 async Task WithoutState_AgentBehavesNormally_NoStateSnapshotReturnedAsync 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 async Task EmptyState_DoesNotTriggerStateHandlingAsync() 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 async Task EmptyState_DoesNotTriggerStateHandlingAsync() 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 async Task NonStreamingRunAsync_WithState_ReturnsStateInResponseAsync() 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 async Task NonStreamingRunAsync_WithState_ReturnsStateInResponseAsync() 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 @@ stateObj is JsonElement state && 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 async Task ServerTriggersSingleFunctionCallAsync() 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 async Task ServerTriggersMultipleFunctionCallsAsync() 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 async Task ClientTriggersSingleFunctionCallAsync() 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 async Task ClientTriggersMultipleFunctionCallsAsync() 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 async Task ServerAndClientTriggerFunctionCallsSimultaneouslyAsync() 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 async Task FunctionCallsPreserveCallIdAndNameAsync() 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 async Task ParallelFunctionCallsFromServerAreHandledCorrectlyAsync() 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 async Task ServerToolCallWithCustomArgumentsAsync() 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 async Task ClientToolCallWithCustomArgumentsAsync() 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 @@ static AIAgent CustomAgentFactory(IEnumerable messages, IEnumerable } [Fact] - public async Task MapAGUIAgent_ProducesCorrectThreadAndRunIds_InAllEventsAsync() + public async Task MapAGUIAgent_ProducesCorrectSessionAndRunIds_InAllEventsAsync() { // Arrange DefaultHttpContext httpContext = new(); @@ -425,20 +425,20 @@ private sealed class MultiResponseAgent : AIAgent 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 @@ private RequestDelegate CreateRequestDelegate( }; } - 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 @@ private sealed class TestAgent : AIAgent 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 @@ await this.RunSampleTestAsync(samplePath, async (logs) => 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 @@ await this.WaitForConditionAsync( 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 @@ await this.RunSampleTestAsync(samplePath, async (logs) => 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 @@ await this.WaitForConditionAsync( 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 @@ await this.WaitForConditionAsync( 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 void DeserializingConstructor_Throws_WithEmptyJsonElement() 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 async Task InvokingAsync_PerformsSearch_AndReturnsContextMessageAsync() 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 async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsy } 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 async Task InvokedAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsyn } 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 async Task InvokedAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsyn 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 async Task ClearStoredMemoriesAsync_SendsDeleteWithQueryAsync() // 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 void Serialize_RoundTripsScopes() 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 void Serialize_RoundTripsScopes() 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 async Task RunAsync_CallsUnderlyingAgentAsync() { // 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 async Task RunAsync_CallsUnderlyingAgentAsync() .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 async Task RunAsync_CallsUnderlyingAgentAsync() ItExpr.Is>(msgs => msgs.ToList().Count == 1 && msgs.ToList()[0].Text == TestMessageText), - mockThread.Object, + mockSession.Object, options, cancellationToken ); @@ -150,7 +150,7 @@ public async Task RunStreamingAsync_CallsUnderlyingAgentAsync() { // 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 async Task RunStreamingAsync_CallsUnderlyingAgentAsync() .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 async Task RunStreamingAsync_CallsUnderlyingAgentAsync() ItExpr.Is>(msgs => msgs.ToList().Count == 1 && msgs.ToList()[0].Text == TestMessageText), - mockThread.Object, + mockSession.Object, options, cancellationToken ); @@ -254,7 +254,7 @@ public async Task RunAsync_ResponseItem_CallsUnderlyingAgentAsync() { // 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 async Task RunAsync_ResponseItem_CallsUnderlyingAgentAsync() .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 async Task RunStreamingAsync_ResponseItem_CallsUnderlyingAgentAsync() { // 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 async Task RunStreamingAsync_ResponseItem_CallsUnderlyingAgentAsync() .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 async Task RunStreamingAsync_ResponseItem_CallsUnderlyingAgentAsync() .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 async Task ProcessAgentContentAsync_WithBlockedPrompt_ReturnsBlockedMessa mockAgent.Protected().Verify("RunCoreAsync", Times.Never(), ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()); } @@ -301,7 +301,7 @@ public async Task ProcessAgentContentAsync_WithAllowedPromptAndBlockedResponse_R mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(innerResponse); @@ -340,7 +340,7 @@ public async Task ProcessAgentContentAsync_WithAllowedPromptAndResponse_ReturnsI mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(innerResponse); @@ -383,7 +383,7 @@ public async Task ProcessAgentContentAsync_WithIgnoreExceptions_ContinuesOnError mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(expectedResponse); @@ -450,7 +450,7 @@ public async Task ProcessAgentContentAsync_ExtractsThreadIdFromMessageAdditional mockAgent.Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(expectedResponse); @@ -492,12 +492,12 @@ public async Task ProcessAgentContentAsync_GeneratesThreadId_WhenNotProvidedAsyn 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 async Task ProcessAgentContentAsync_GeneratesThreadId_WhenNotProvidedAsyn 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 async Task ProcessAgentContentAsync_GeneratesThreadId_WhenNotProvidedAsyn // 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 async Task ProcessAgentContentAsync_PassesResolvedUserId_ToResponseProces 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 async Task ProcessMessagesAsync_WithBlockAccessAction_ReturnsShouldBlockT // 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 async Task ProcessMessagesAsync_WithRestrictionActionBlock_ReturnsShouldB // 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 async Task ProcessMessagesAsync_WithNoBlockingActions_ReturnsShouldBlockF // 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 async Task ProcessMessagesAsync_UsesCachedProtectionScopes_WhenAvailableA // 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 async Task ProcessMessagesAsync_InvalidatesCache_WhenProtectionScopeModif // 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 async Task ProcessMessagesAsync_SendsContentActivities_WhenNoApplicableSc // 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 async Task ProcessMessagesAsync_WithNoTenantId_ThrowsPurviewExceptionAsyn // 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 async Task ProcessMessagesAsync_WithNoUserId_ThrowsPurviewExceptionAsync( // 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 async Task ProcessMessagesAsync_ExtractsUserIdFromMessageAdditionalProper // 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 async Task ProcessMessagesAsync_ExtractsUserIdFromMessageAuthorName_WhenV // 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 void Use_WithNullSharedFunc_ThrowsArgumentNullException() // 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 TestAgent(string? name, string? description, Exception exceptionToThrow) 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 override ValueTask DeserializeThreadAsync(JsonElement serial protected override Task RunCoreAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -417,11 +417,11 @@ protected override Task RunCoreAsync( 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 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 AnonymousDelegatingAIAgentTests() .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .ReturnsAsync(this._testResponse); @@ -49,7 +49,7 @@ public AnonymousDelegatingAIAgentTests() .Protected() .Setup>("RunCoreStreamingAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._testStreamingResponses)); @@ -165,27 +165,27 @@ public async Task RunAsync_WithSharedFunc_ContextPropagatedAsync() { // 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 async Task RunAsync_WithSharedFunc_ContextPropagatedAsync() .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 async Task SharedFunc_WorksForBothRunAndStreamingAsync() // 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 async Task RunAsync_WithRunFuncOnly_UsesRunFuncAsync() 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 async Task RunStreamingAsync_WithRunFuncOnly_ConvertsFromRunFuncAsync() 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 async Task RunAsync_WithStreamingFuncOnly_ConvertsFromStreamingFuncAsync( 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 async Task RunStreamingAsync_WithStreamingFuncOnly_UsesStreamingFuncAsync 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 async Task BothDelegates_EachUsesRespectiveImplementationAsync() 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 async Task SharedFunc_ThrowsException_PropagatesExceptionAsync() // 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 async Task RunFunc_ThrowsException_PropagatesExceptionAsync() // 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 async Task StreamingFunc_ThrowsException_PropagatesExceptionAsync() // 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 async Task SharedFunc_DoesNotCallInner_ThrowsInvalidOperationAsync() // 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 async Task AsyncLocalContext_MaintainedAcrossDelegatesAsync() 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 async Task AsyncLocalContext_MaintainedAcrossDelegatesAsync() .Protected() .Setup>("RunCoreAsync", ItExpr.IsAny>(), - ItExpr.IsAny(), + ItExpr.IsAny(), ItExpr.IsAny(), ItExpr.IsAny()) .Returns(() => @@ -482,7 +482,7 @@ public async Task AsyncLocalContext_MaintainedAcrossDelegatesAsync() // 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 async Task MultipleMiddleware_ExecuteInCorrectOrderAsync() 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 async Task MultipleMiddleware_SeparateDelegates_ExecuteInCorrectOrderAsyn 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 async Task MultipleMiddleware_ContextModification_PropagatedAsync() 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 async Task MultipleMiddleware_ExceptionInMiddle_PropagatesAsync() 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 async Task MultipleMiddleware_ExceptionInStreaming_PropagatesAsync() 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 async Task AIAgentBuilder_Use_MultipleMiddleware_ExecutesInCorrectOrderAs 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 async Task AIAgentBuilder_Use_MultipleMiddlewareWithSeparateDelegates_Exe 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 @@ static async IAsyncEnumerable FirstStreamingMiddlewareAsync } 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 @@ static async IAsyncEnumerable SecondStreamingMiddlewareAsyn 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 async Task AIAgentBuilder_Use_MiddlewareModifiesContext_ChangesPropagateA 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 async Task AIAgentBuilder_Use_ExceptionInMiddlewareChain_PropagatesCorrec 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 async Task AIAgentBuilder_Use_ExceptionInMiddlewareChain_PropagatesCorrec 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 async Task AIAgentBuilder_Use_ExceptionInMiddlewareChain_PropagatesCorrec // 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 async Task AIAgentBuilder_Use_MiddlewareHandlesException_RecoveryWorksAsy 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 async Task AIAgentBuilder_Use_MiddlewareHandlesException_RecoveryWorksAsy } }, 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 async Task AIAgentBuilder_Use_MiddlewareHandlesException_RecoveryWorksAsy .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 async Task AIAgentBuilder_Use_CancellationTokenPropagation_WorksCorrectly .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 async Task AIAgentBuilder_Use_MiddlewareShortCircuits_InnerAgentNotCalled 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 async Task AIAgentBuilder_Use_MiddlewareShortCircuits_InnerAgentNotCalled .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 async Task AIAgentBuilder_Use_MiddlewareShortCircuits_InnerAgentNotCalled .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 @@ 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 async Task VerifyDeserializeWithMessagesAsync() """, 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 async Task VerifyDeserializeWithIdAsync() """, 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 async Task VerifyDeserializeWithAIContextProviderAsync() 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 async Task DeserializeWithInvalidJsonThrowsAsync() { // 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 async Task DeserializeWithInvalidJsonThrowsAsync() #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 void VerifyThreadSerializationWithId() } /// - /// 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 void VerifyThreadSerializationWithMessages() } [Fact] - public void VerifyThreadSerializationWithWithAIContextProvider() + public void VerifySessionSerializationWithWithAIContextProvider() { // Arrange Mock mockProvider = new(); @@ -232,13 +232,13 @@ public void VerifyThreadSerializationWithWithAIContextProvider() .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 void VerifyThreadSerializationWithWithAIContextProvider() } /// - /// 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 void VerifyThreadSerializationWithCustomOptions() 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 void VerifyThreadSerializationWithCustomOptions() 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 void GetService_RequestingAIContextProvider_ReturnsAIContextProvider() 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 async Task RunAsyncSetsAuthorNameOnAllResponseMessagesAsync(string? autho } /// - /// 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 async Task RunAsyncRetrievesMessagesFromThreadWhenThreadHasChatHistoryPro 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 async Task RunAsyncInvokesAIContextProviderAndUsesResultAsync() 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 async Task RunAsyncInvokesAIContextProviderAndUsesResultAsync() 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 async Task RunStreamingAsyncUsesChatHistoryProviderWhenNoConversationIdRe }); // 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 async Task RunStreamingAsyncThrowsWhenChatHistoryProviderFactoryProvidedA }); // 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 async Task RunStreamingAsyncInvokesAIContextProviderAndUsesResultAsync() }); // 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 async Task RunStreamingAsyncInvokesAIContextProviderAndUsesResultAsync() 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 async Task RunAsync_PropagatesBackgroundResponsesPropertiesToChatClientAs 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 async Task RunAsync_WhenPropertiesSetInBothLocations_PrioritizesAgentRunO 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 async Task RunStreamingAsync_PropagatesBackgroundResponsesPropertiesToCha 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 async Task RunStreamingAsync_WhenPropertiesSetInBothLocations_Prioritizes 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 async Task RunAsync_WhenContinuationTokenReceivedFromChatResponse_WrapsCo 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 async Task RunStreamingAsync_WhenContinuationTokenReceived_WrapsContinuat 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 @@ await Assert.ThrowsAsync(async () => } [Fact] - public async Task RunAsync_WhenContinuationTokenProvided_SkipsThreadMessagePopulationAsync() + public async Task RunAsync_WhenContinuationTokenProvided_SkipsSessionMessagePopulationAsync() { // Arrange List capturedMessages = []; @@ -364,8 +364,8 @@ public async Task RunAsync_WhenContinuationTokenProvided_SkipsThreadMessagePopul 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 async Task RunAsync_WhenContinuationTokenProvided_SkipsThreadMessagePopul }; // 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 async Task RunAsync_WhenContinuationTokenProvided_SkipsThreadMessagePopul } [Fact] - public async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsThreadMessagePopulationAsync() + public async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsSessionMessagePopulationAsync() { // Arrange List capturedMessages = []; @@ -429,8 +429,8 @@ public async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsThreadMes 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 async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsThreadMes }; // 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 async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsThreadMes } [Fact] - public async Task RunAsync_WhenNoThreadProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() + public async Task RunAsync_WhenNoSessionProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() { // Arrange Mock mockChatClient = new(); @@ -484,7 +484,7 @@ public async Task RunAsync_WhenNoThreadProvidedForBackgroundResponses_ThrowsInva } [Fact] - public async Task RunStreamingAsync_WhenNoThreadProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() + public async Task RunStreamingAsync_WhenNoSessionProvidedForBackgroundResponses_ThrowsInvalidOperationExceptionAsync() { // Arrange Mock mockChatClient = new(); @@ -532,7 +532,7 @@ public async Task RunStreamingAsync_WhenInputMessagesPresentInContinuationToken_ 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 async Task RunStreamingAsync_WhenInputMessagesPresentInContinuationToken_ // 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 async Task RunStreamingAsync_WhenResponseUpdatesPresentInContinuationToke 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 async Task RunStreamingAsync_WhenResponseUpdatesPresentInContinuationToke // 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 async Task RunStreamingAsync_WhenResumingStreaming_UsesUpdatesFromInitial .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 async Task RunStreamingAsync_WhenResumingStreaming_UsesUpdatesFromInitial }; // 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 async Task RunStreamingAsync_WhenResumingStreaming_UsesInputMessagesFromI .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 async Task RunStreamingAsync_WhenResumingStreaming_UsesInputMessagesFromI }; // 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 async Task RunStreamingAsync_WhenResumingStreaming_SavesInputMessagesAndU ChatClientAgent agent = new(mockChatClient.Object); - ChatClientAgentThread thread = new() { }; + ChatClientAgentSession? session = new() { }; List capturedContinuationTokens = []; @@ -759,7 +759,7 @@ public async Task RunStreamingAsync_WhenResumingStreaming_SavesInputMessagesAndU // 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 async Task RunStreamingAsync_WhenResumingStreaming_SavesInputMessagesAndU // 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 async Task RunAsync_DoesNotThrow_WhenSpecifyingTwoSameConversationIdsAsyn 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 async Task RunAsync_Throws_WhenSpecifyingTwoDifferentConversationIdsAsync 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 async Task RunAsync_ClonesChatOptions_ToAddConversationIdAsync() 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 async Task RunAsync_Throws_ForMissingConversationIdWithConversationIdThre 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 async Task RunAsync_SetsConversationIdOnThread_WhenReturnedByChatClientAs 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 async Task RunAsync_UsesDefaultInMemoryChatHistoryProvider_WhenNoConversa }); // 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 async Task RunAsync_UsesChatHistoryProviderFactory_WhenProvidedAndNoConve }); // 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 async Task RunAsync_NotifiesChatHistoryProvider_OnFailureAsync() }); // 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 async Task RunAsync_Throws_WhenChatHistoryProviderFactoryProvidedAndConve }); // 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 async Task RunAsync_UsesOverrideChatHistoryProvider_WhenProvidedViaAdditi }); // 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 @@ 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 async Task DeserializeThread_UsesAIContextProviderFactory_IfProvidedAsync """, 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 async Task DeserializeThread_UsesChatHistoryProviderFactory_IfProvidedAsy """, 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 @@ 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 async Task GetNewThread_UsesAIContextProviderFactory_IfProvidedAsync() }); // 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 async Task GetNewThread_UsesChatHistoryProviderFactory_IfProvidedAsync() }); // 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 async Task GetNewThread_UsesChatHistoryProvider_FromTypedOverloadAsync() 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 async Task GetNewThread_UsesConversationId_FromTypedOverloadAsync() 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 async Task RunAsync_WithThreadAndOptions_CallsBaseMethodAsync() 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 async Task RunAsync_WithStringMessageAndOptions_CallsBaseMethodAsync() 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 async Task RunAsync_WithChatMessageAndOptions_CallsBaseMethodAsync() 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 async Task RunAsync_WithMessagesCollectionAndOptions_CallsBaseMethodAsync 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 async Task RunAsync_WithChatOptionsInRunOptions_UsesChatOptionsAsync() #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 async Task RunStreamingAsync_WithThreadAndOptions_CallsBaseMethodAsync() 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 async Task RunStreamingAsync_WithStringMessageAndOptions_CallsBaseMethodA 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 async Task RunStreamingAsync_WithChatMessageAndOptions_CallsBaseMethodAsy 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 async Task RunStreamingAsync_WithMessagesCollectionAndOptions_CallsBaseMe 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 @@ private static async IAsyncEnumerable GetAsyncUpdatesAsync() #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 async Task RunAsyncOfT_WithThreadAndOptions_CallsBaseMethodAsync() 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 async Task RunAsyncOfT_WithStringMessageAndOptions_CallsBaseMethodAsync() 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 async Task RunAsyncOfT_WithChatMessageAndOptions_CallsBaseMethodAsync() 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 async Task RunAsyncOfT_WithMessagesCollectionAndOptions_CallsBaseMethodAs 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 async Task RunAsync_FunctionMiddlewareWithRunningMiddleware_BothExecuteAs 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 async Task RunAsync_LogsAtDebugLevelAsync() 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 async Task RunAsync_LogsAtTraceLevel_IncludesSensitiveDataAsync() 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 async Task RunAsync_OnCancellation_LogsCanceledAsync() var innerAgent = new TestAIAgent { - RunAsyncFunc = (messages, thread, options, cancellationToken) => + RunAsyncFunc = (messages, session, options, cancellationToken) => throw new OperationCanceledException() }; @@ -195,7 +195,7 @@ public async Task RunAsync_OnException_LogsFailedAsync() var innerAgent = new TestAIAgent { - RunAsyncFunc = (messages, thread, options, cancellationToken) => + RunAsyncFunc = (messages, session, options, cancellationToken) => throw new InvalidOperationException("Test exception") }; @@ -229,7 +229,7 @@ public async Task RunStreamingAsync_LogsAtDebugLevelAsync() }; 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 async Task RunStreamingAsync_LogsUpdatesAtTraceLevelAsync() }; 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 async Task RunStreamingAsync_OnCancellation_LogsCanceledAsync() }; 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 async Task RunStreamingAsync_OnException_LogsFailedAsync() }; 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 async Task InvokedAsync_UpsertsMessages_ToCollectionAsync() { ApplicationId = "app1", AgentId = "agent1", - ThreadId = "thread1", + SessionId = "session1", UserId = "user1" }; @@ -138,7 +138,7 @@ public async Task InvokedAsync_UpsertsMessages_ToCollectionAsync() 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 async Task InvokedAsync_UpsertsMessages_ToCollectionAsync() 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 async Task InvokedAsync_UpsertsMessages_ToCollectionAsync() 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 async Task InvokedAsync_CreatesFilter_WhenSearchScopeProvidedAsync() { ApplicationId = "app1", AgentId = "agent1", - ThreadId = "thread1", + SessionId = "session1", UserId = "user1" }; @@ -370,7 +370,7 @@ public async Task InvokedAsync_CreatesFilter_WhenSearchScopeProvidedAsync() .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 void Serialize_Deserialize_RoundtripsScopes() { ApplicationId = "app", AgentId = "agent", - ThreadId = "thread", + SessionId = "session", UserId = "user" }; @@ -494,7 +494,7 @@ public void Serialize_Deserialize_RoundtripsScopes() { ApplicationId = "app2", AgentId = "agent2", - ThreadId = "thread2", + SessionId = "session2", UserId = "user2" }; @@ -507,13 +507,13 @@ public void Serialize_Deserialize_RoundtripsScopes() 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 async Task WithoutChatOptions_ExpectedInformationLogged_Async(bool enable 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 async Task WithoutChatOptions_ExpectedInformationLogged_Async(bool enable }; 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 async Task WithChatOptions_ExpectedInformationLogged_Async( 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 async Task WithChatOptions_ExpectedInformationLogged_Async( }; 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 @@ private class DoubleEchoAgent(string name) : AIAgent { 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA } } - private sealed class DoubleEchoAgentThread() : InMemoryAgentThread(); + private sealed class DoubleEchoAgentSession() : InMemoryAgentSession(); [Fact] public async Task BuildConcurrent_AgentsRunInParallelAsync() @@ -410,7 +410,7 @@ public async Task BuildGroupChat_AgentsRunInOrderAsync(int maxIterations) 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA 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 SimpleTestAgent(string name) 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 @@ protected override Task RunCoreAsync( protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, - AgentThread? thread = null, + AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -190,7 +190,7 @@ protected override async IAsyncEnumerable RunCoreStreamingA } /// - /// 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 @@ private sealed class TestExecutor() : Executor("TestExecutor") 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA }; } - 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA } } -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 @@ public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvi 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 @@ public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvi { 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 @@ public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvi { 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 @@ public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvi { 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 @@ public static Workflow WorkflowInstance } } - 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 @@ public static async ValueTask RunAsAgentAsync(TextWriter writer, st 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 @@ async ValueTask RunAndValidateAsync(int step) 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 @@ async ValueTask RunAndValidateAsync(int step) 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 @@ IEnumerable echoMessages 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 @@ protected virtual IEnumerable GetEpilogueMessages(AgentRunOptions? 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 @@ protected override Task RunCoreAsync(IEnumerable mes 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA } } - 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 @@ static ChatMessage ToMessage(string text) 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 @@ protected override async IAsyncEnumerable RunCoreStreamingA 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 @@ 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 @@ private static int[] SampleIndicies(Random rng, int n, int c) 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 @@ private async IAsyncEnumerable RunStreamingAsync RunStreamingAsync 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 @@ public FunctionResultContent CreatePairedResponse(FunctionCallContent request) } } - 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 @@ public UserInputResponseContent CreatePairedResponse(UserInputRequestContent req } } - 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 @@ public void ProcessResponse(UserInputResponseContent response, TestRequestAgentT 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 @@ private interface IRequestResponseStrategy 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 @@ private IEnumerable ValidateUnpairedRequests(IEn 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 @@ static TRequest AssertAndExtractRequestContent(ExternalRequest request } } - private sealed class TestRequestAgentThread : InMemoryAgentThread + private sealed class TestRequestAgentSession : InMemoryAgentSession where TRequest : AIContent where TResponse : AIContent { - public TestRequestAgentThread() + public TestRequestAgentSession() { } @@ -341,17 +341,17 @@ public TestRequestAgentThread() 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 @@ public TestRequestAgentThread(JsonElement element, JsonSerializerOptions? jsonSe 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 async Task CreateChatClientAgentAsync( 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 OpenAIChatCompletionFixture(bool useReasoningChatModel) 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 Task DeleteAgentAsync(ChatClientAgent agent) => // 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 async Task> GetChatHistoryAsync(AgentThread thread) 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 Task DeleteAgentAsync(ChatClientAgent agent) => // 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;