diff --git a/dotnet/.editorconfig b/dotnet/.editorconfig index 59d57c4818..535c42a7f8 100644 --- a/dotnet/.editorconfig +++ b/dotnet/.editorconfig @@ -124,10 +124,12 @@ dotnet_diagnostic.CA1063.severity = warning # Implement IDisposable correctly dotnet_diagnostic.CA1064.severity = warning # Exceptions should be public dotnet_diagnostic.CA1416.severity = warning # Validate platform compatibility dotnet_diagnostic.CA1508.severity = warning # Avoid dead conditional code +dotnet_diagnostic.CA1805.severity = warning # Member is explicitly initialized to its default value +dotnet_diagnostic.CA1822.severity = suggestion # Member does not access instance data and can be marked as static dotnet_diagnostic.CA1852.severity = warning # Sealed classes dotnet_diagnostic.CA1859.severity = warning # Use concrete types when possible for improved performance dotnet_diagnostic.CA1860.severity = warning # Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance -dotnet_diagnostic.CA2000.severity = warning # Call System.IDisposable.Dispose on object before all references to it are out of scope +dotnet_diagnostic.CA2007.severity = warning # Do not directly await a Task dotnet_diagnostic.CA2201.severity = warning # Exception type System.Exception is not sufficiently specific dotnet_diagnostic.IDE0001.severity = warning # Simplify name @@ -152,9 +154,39 @@ dotnet_diagnostic.IDE0082.severity = warning # Convert typeof to nameof dotnet_diagnostic.IDE0090.severity = warning # Simplify new expression dotnet_diagnostic.IDE0161.severity = warning # Use file-scoped namespace +dotnet_diagnostic.VSTHRD111.severity = error # Use .ConfigureAwait(bool) +dotnet_diagnostic.VSTHRD200.severity = warning # Use Async suffix for async methods + +dotnet_diagnostic.RCS1021.severity = warning # Use expression-bodied lambda. +dotnet_diagnostic.RCS1061.severity = warning # Merge 'if' with nested 'if'. +dotnet_diagnostic.RCS1069.severity = warning # Remove unnecessary case label. +dotnet_diagnostic.RCS1077.severity = warning # Optimize LINQ method call. +dotnet_diagnostic.RCS1118.severity = warning # Mark local variable as const. +dotnet_diagnostic.RCS1124.severity = warning # Inline local variable. +dotnet_diagnostic.RCS1129.severity = warning # Remove redundant field initialization. +dotnet_diagnostic.RCS1146.severity = warning # Use conditional access. +dotnet_diagnostic.RCS1170.severity = warning # Use read-only auto-implemented property. +dotnet_diagnostic.RCS1173.severity = warning # Use coalesce expression instead of 'if'. +dotnet_diagnostic.RCS1186.severity = warning # Use Regex instance instead of static method. +dotnet_diagnostic.RCS1188.severity = warning # Remove redundant auto-property initialization. +dotnet_diagnostic.RCS1197.severity = suggestion # Optimize StringBuilder.AppendLine call. +dotnet_diagnostic.RCS1201.severity = warning # Use method chaining. + +dotnet_diagnostic.IDE0001.severity = warning # Simplify name +dotnet_diagnostic.IDE0002.severity = warning # Simplify member access +dotnet_diagnostic.IDE0004.severity = warning # Remove unnecessary cast +dotnet_diagnostic.IDE0035.severity = warning # Remove unreachable code +dotnet_diagnostic.IDE0051.severity = warning # Remove unused private member +dotnet_diagnostic.IDE0052.severity = warning # Remove unread private member +dotnet_diagnostic.IDE0059.severity = warning # Unnecessary assignment of a value +dotnet_diagnostic.IDE0110.severity = warning # Remove unnecessary discards +dotnet_diagnostic.IDE0032.severity = warning # Use auto property +dotnet_diagnostic.IDE0047.severity = warning # Parentheses can be removed +dotnet_diagnostic.IDE1006.severity = error # Naming rule violations + # Suppressed diagnostics dotnet_diagnostic.CA1002.severity = none # Change 'List' in '...' to use 'Collection' ... -dotnet_diagnostic.CA1031.severity = suggestion # Do not catch general exception types +dotnet_diagnostic.CA1031.severity = none # Do not catch general exception types dotnet_diagnostic.CA1032.severity = none # We're using RCS1194 which seems to cover more ctors dotnet_diagnostic.CA1034.severity = none # Do not nest type. Alternatively, change its accessibility so that it is not externally visible dotnet_diagnostic.CA1062.severity = none # Disable null check, C# already does it for us @@ -165,51 +197,35 @@ dotnet_diagnostic.CA1508.severity = none # Avoid dead conditional code. Too many dotnet_diagnostic.CA1510.severity = none # ArgumentNullException.Throw dotnet_diagnostic.CA1512.severity = none # ArgumentOutOfRangeException.Throw dotnet_diagnostic.CA1515.severity = none # Making public types from exes internal -dotnet_diagnostic.CA1805.severity = none # Member is explicitly initialized to its default value -dotnet_diagnostic.CA1822.severity = none # Member does not access instance data and can be marked as static dotnet_diagnostic.CA1848.severity = none # For improved performance, use the LoggerMessage delegates dotnet_diagnostic.CA1849.severity = none # Use async equivalent; analyzer is currently noisy dotnet_diagnostic.CA1865.severity = none # StartsWith(char) dotnet_diagnostic.CA1867.severity = none # EndsWith(char) -dotnet_diagnostic.CA2007.severity = none # Do not directly await a Task +dotnet_diagnostic.CS1998.severity = none # async method lacks 'await' operators and will run synchronously +dotnet_diagnostic.CA2000.severity = suggestion # Call System.IDisposable.Dispose on object before all references to it are out of scope dotnet_diagnostic.CA2225.severity = none # Operator overloads have named alternates dotnet_diagnostic.CA2227.severity = none # Change to be read-only by removing the property setter dotnet_diagnostic.CA2253.severity = none # Named placeholders in the logging message template should not be comprised of only numeric characters dotnet_diagnostic.CA2253.severity = none # Named placeholders in the logging message template should not be comprised of only numeric characters dotnet_diagnostic.CA2263.severity = suggestion # Use generic overload -dotnet_diagnostic.CS1998.severity = suggestion # async method lacks 'await' operators and will run synchronously +dotnet_diagnostic.CA5394.severity = none # Do not use insecure sources of randomness dotnet_diagnostic.VSTHRD003.severity = none # Waiting on thread from another context dotnet_diagnostic.VSTHRD103.severity = none # Use async equivalent; analyzer is currently noisy dotnet_diagnostic.VSTHRD111.severity = none # Use .ConfigureAwait(bool) is hidden by default, set to none to prevent IDE from changing on autosave -dotnet_diagnostic.VSTHRD200.severity = none # Use Async suffix for async methods dotnet_diagnostic.xUnit1004.severity = none # Test methods should not be skipped. Remove the Skip property to start running the test again. -dotnet_diagnostic.RCS1021.severity = none # Use expression-bodied lambda. dotnet_diagnostic.RCS1032.severity = none # Remove redundant parentheses. -dotnet_diagnostic.RCS1061.severity = none # Merge 'if' with nested 'if'. -dotnet_diagnostic.RCS1069.severity = none # Remove unnecessary case label. dotnet_diagnostic.RCS1074.severity = none # Remove redundant constructor. -dotnet_diagnostic.RCS1077.severity = none # Optimize LINQ method call. -dotnet_diagnostic.RCS1118.severity = none # Mark local variable as const. -dotnet_diagnostic.RCS1124.severity = none # Inline local variable. -dotnet_diagnostic.RCS1129.severity = none # Remove redundant field initialization. dotnet_diagnostic.RCS1140.severity = none # Add exception to documentation comment. dotnet_diagnostic.RCS1141.severity = none # Add 'param' element to documentation comment. dotnet_diagnostic.RCS1142.severity = none # Add 'typeparam' element to documentation comment. -dotnet_diagnostic.RCS1146.severity = none # Use conditional access. dotnet_diagnostic.RCS1151.severity = none # Remove redundant cast. dotnet_diagnostic.RCS1158.severity = none # Static member in generic type should use a type parameter. dotnet_diagnostic.RCS1161.severity = none # Enum should declare explicit value dotnet_diagnostic.RCS1163.severity = none # Unused parameter 'foo'. -dotnet_diagnostic.RCS1170.severity = none # Use read-only auto-implemented property. -dotnet_diagnostic.RCS1173.severity = none # Use coalesce expression instead of 'if'. dotnet_diagnostic.RCS1181.severity = none # Convert comment to documentation comment. -dotnet_diagnostic.RCS1186.severity = none # Use Regex instance instead of static method. -dotnet_diagnostic.RCS1188.severity = none # Remove redundant auto-property initialization. dotnet_diagnostic.RCS1189.severity = none # Add region name to #endregion. -dotnet_diagnostic.RCS1197.severity = none # Optimize StringBuilder.AppendLine call. -dotnet_diagnostic.RCS1201.severity = none # Use method chaining. dotnet_diagnostic.RCS1205.severity = none # Order named arguments according to the order of parameters. dotnet_diagnostic.RCS1212.severity = none # Remove redundant assignment. dotnet_diagnostic.RCS1217.severity = none # Convert interpolated string to concatenation. @@ -220,42 +236,27 @@ dotnet_diagnostic.RCS1234.severity = none # Enum duplicate value dotnet_diagnostic.RCS1238.severity = none # Avoid nested ?: operators. dotnet_diagnostic.RCS1241.severity = none # Implement IComparable when implementing IComparable. -dotnet_diagnostic.IDE0001.severity = none # Simplify name -dotnet_diagnostic.IDE0002.severity = none # Simplify member access -dotnet_diagnostic.IDE0004.severity = none # Remove unnecessary cast dotnet_diagnostic.IDE0010.severity = none # Populate switch dotnet_diagnostic.IDE0021.severity = none # Use block body for constructors dotnet_diagnostic.IDE0022.severity = none # Use block body for methods dotnet_diagnostic.IDE0024.severity = none # Use block body for operator -dotnet_diagnostic.IDE0035.severity = none # Remove unreachable code -dotnet_diagnostic.IDE0051.severity = none # Remove unused private member -dotnet_diagnostic.IDE0052.severity = none # Remove unread private member dotnet_diagnostic.IDE0058.severity = none # Remove unused expression value -dotnet_diagnostic.IDE0059.severity = none # Unnecessary assignment of a value dotnet_diagnostic.IDE0060.severity = none # Remove unused parameter dotnet_diagnostic.IDE0061.severity = none # Use block body for local function dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression. dotnet_diagnostic.IDE0080.severity = none # Remove unnecessary suppression operator. dotnet_diagnostic.IDE0100.severity = none # Remove unnecessary equality operator -dotnet_diagnostic.IDE0110.severity = none # Remove unnecessary discards dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure dotnet_diagnostic.IDE0290.severity = none # Use primary constructor -dotnet_diagnostic.IDE0032.severity = none # Use auto property dotnet_diagnostic.IDE0160.severity = none # Use block-scoped namespace dotnet_diagnostic.IDE1006.severity = warning # Naming rule violations +dotnet_diagnostic.IDE0042.severity = none # Variable declaration can be deconstructed dotnet_diagnostic.IDE0046.severity = suggestion # If statement can be simplified dotnet_diagnostic.IDE0056.severity = suggestion # Indexing can be simplified dotnet_diagnostic.IDE0057.severity = suggestion # Substring can be simplified dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression dotnet_diagnostic.IDE0290.severity = none # Use primary constructor dotnet_diagnostic.IDE0046.severity = none # if statement can be simplified -dotnet_diagnostic.IDE0057.severity = none # Substring can be simplified -dotnet_diagnostic.IDE0042.severity = none # Variable declaration can be deconstructed -dotnet_diagnostic.IDE0047.severity = none # Parentheses can be removed - -dotnet_diagnostic.CA2007.severity = error # Do not directly await a Task -dotnet_diagnostic.VSTHRD111.severity = error # Use .ConfigureAwait(bool) -dotnet_diagnostic.IDE1006.severity = error # Naming rule violations # Testing dotnet_diagnostic.Moq1400.severity = none # Explicitly choose a mocking behavior instead of relying on the default (Loose) behavior diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index a03b9489e9..f63c898ae0 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -37,11 +37,11 @@ - + diff --git a/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs b/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs index 75513b0834..12f151b471 100644 --- a/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs +++ b/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs @@ -12,7 +12,6 @@ internal sealed class HostClientAgent { internal HostClientAgent(ILoggerFactory loggerFactory) { - this._loggerFactory = loggerFactory; this._logger = loggerFactory.CreateLogger("HostClientAgent"); } @@ -23,7 +22,7 @@ internal sealed class HostClientAgent this._logger.LogInformation("Initializing Agent Framework agent with model: {ModelId}", modelId); // Connect to the remote agents via A2A - var createAgentTasks = agentUrls.Select(agentUrl => this.CreateAgentAsync(agentUrl)); + var createAgentTasks = agentUrls.Select(CreateAgentAsync); var agents = await Task.WhenAll(createAgentTasks); var tools = agents.Select(agent => (AITool)agent.AsAIFunction()).ToList(); @@ -45,10 +44,9 @@ internal sealed class HostClientAgent public AIAgent? Agent { get; private set; } #region private - private readonly ILoggerFactory _loggerFactory; private readonly ILogger _logger; - private async Task CreateAgentAsync(string agentUri) + private static async Task CreateAgentAsync(string agentUri) { var url = new Uri(agentUri); var httpClient = new HttpClient diff --git a/dotnet/samples/A2AClientServer/A2AClient/Program.cs b/dotnet/samples/A2AClientServer/A2AClient/Program.cs index 2715fdaf70..e22a4d6a35 100644 --- a/dotnet/samples/A2AClientServer/A2AClient/Program.cs +++ b/dotnet/samples/A2AClientServer/A2AClient/Program.cs @@ -57,7 +57,7 @@ public static class Program continue; } - if (message == ":q" || message == "quit") + if (message is ":q" or "quit") { break; } diff --git a/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs b/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs index 499e54aa6e..56ba14b49f 100644 --- a/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs +++ b/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs @@ -20,10 +20,7 @@ public class Product this.Price = price; } - public decimal TotalPrice() - { - return this.Quantity * this.Price; // Total price for this product - } + public decimal TotalPrice() => this.Quantity * this.Price; // Total price for this product } public class Invoice @@ -43,82 +40,78 @@ public class Invoice this.Products = products; } - public decimal TotalInvoicePrice() - { - return this.Products.Sum(product => product.TotalPrice()); // Total price of all products in the invoice - } + public decimal TotalInvoicePrice() => this.Products.Sum(product => product.TotalPrice()); // Total price of all products in the invoice } public class InvoiceQuery { private readonly List _invoices; - private static readonly Random s_random = new(); public InvoiceQuery() { // Extended mock data with quantities and prices this._invoices = [ - new("TICKET-XYZ987", "INV789", "Contoso", GetRandomDateWithinLastTwoMonths(), new List - { + new("TICKET-XYZ987", "INV789", "Contoso", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 150, 10.00m), new("Hats", 200, 15.00m), new("Glasses", 300, 5.00m) - }), - new("TICKET-XYZ111", "INV111", "XStore", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ111", "INV111", "XStore", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 2500, 12.00m), new("Hats", 1500, 8.00m), new("Glasses", 200, 20.00m) - }), - new("TICKET-XYZ222", "INV222", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ222", "INV222", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 1200, 14.00m), new("Hats", 800, 7.00m), new("Glasses", 500, 25.00m) - }), - new("TICKET-XYZ333", "INV333", "Contoso", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ333", "INV333", "Contoso", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 400, 11.00m), new("Hats", 600, 15.00m), new("Glasses", 700, 5.00m) - }), - new("TICKET-XYZ444", "INV444", "XStore", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ444", "INV444", "XStore", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 800, 10.00m), new("Hats", 500, 18.00m), new("Glasses", 300, 22.00m) - }), - new("TICKET-XYZ555", "INV555", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ555", "INV555", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 1100, 9.00m), new("Hats", 900, 12.00m), new("Glasses", 1200, 15.00m) - }), - new("TICKET-XYZ666", "INV666", "Contoso", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ666", "INV666", "Contoso", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 2500, 8.00m), new("Hats", 1200, 10.00m), new("Glasses", 1000, 6.00m) - }), - new("TICKET-XYZ777", "INV777", "XStore", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ777", "INV777", "XStore", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 1900, 13.00m), new("Hats", 1300, 16.00m), new("Glasses", 800, 19.00m) - }), - new("TICKET-XYZ888", "INV888", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ888", "INV888", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 2200, 11.00m), new("Hats", 1700, 8.50m), new("Glasses", 600, 21.00m) - }), - new("TICKET-XYZ999", "INV999", "Contoso", GetRandomDateWithinLastTwoMonths(), new List - { + ]), + new("TICKET-XYZ999", "INV999", "Contoso", GetRandomDateWithinLastTwoMonths(), + [ new("T-Shirts", 1400, 10.50m), new("Hats", 1100, 9.00m), new("Glasses", 950, 12.00m) - }) + ]) ]; } @@ -132,9 +125,7 @@ public class InvoiceQuery // Generate a random number of days between 0 and the total number of days in the range int totalDays = (endDate - startDate).Days; -#pragma warning disable CA5394 // Do not use insecure randomness - int randomDays = s_random.Next(0, totalDays + 1); // +1 to include the end date -#pragma warning restore CA5394 // Do not use insecure randomness + int randomDays = Random.Shared.Next(0, totalDays + 1); // +1 to include the end date // Return the random date return startDate.AddDays(randomDays); diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/HttpActorProcessor.cs b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/HttpActorProcessor.cs index 6b4ec34483..4f2f360ac5 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/HttpActorProcessor.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/HttpActorProcessor.cs @@ -28,12 +28,12 @@ internal static class HttpActorProcessor return GetResult(response); } - if (streaming == true) + if (streaming is true) { return new ActorUpdateStreamingResult(responseHandle); } - if (blocking == true) + if (blocking is true) { response = await responseHandle.GetResponseAsync(cancellationToken); return GetResult(response); @@ -44,7 +44,7 @@ internal static class HttpActorProcessor ActorId = actorId, MessageId = messageId, Status = RequestStatus.Pending, - Data = JsonDocument.Parse("{}").RootElement + Data = JsonSerializer.Deserialize("{}"), }); } @@ -64,12 +64,12 @@ internal static class HttpActorProcessor return GetResult(response); } - if (streaming == true) + if (streaming is true) { return new ActorUpdateStreamingResult(responseHandle); } - if (blocking == true) + if (blocking is true) { response = await responseHandle.GetResponseAsync(cancellationToken); return GetResult(response); diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs index 72311b4ee4..240b5012ef 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs @@ -83,10 +83,7 @@ builder.Services.AddCosmosActorStateStorage("actor-state-db", "ActorState"); var app = builder.Build(); app.MapOpenApi(); -app.UseSwaggerUI(options => -{ - options.SwaggerEndpoint("/openapi/v1.json", "Agents API"); -}); +app.UseSwaggerUI(options => options.SwaggerEndpoint("/openapi/v1.json", "Agents API")); // Configure the HTTP request pipeline. app.UseExceptionHandler(); diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs index 9c459c1a58..ad1f30021f 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs @@ -52,7 +52,7 @@ public class ChatClientConnectionInfo Enum.TryParse(providerValue, ignoreCase: true, out provider); } - if (endpoint is null && provider != ClientChatProvider.OpenAI || model is null || provider == ClientChatProvider.Unknown) + if ((endpoint is null && provider != ClientChatProvider.OpenAI) || model is null || provider is ClientChatProvider.Unknown) { settings = null; return false; diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs index d6333d102c..6cd3d888c8 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs @@ -36,19 +36,16 @@ public static class ChatClientExtensions return chatClientBuilder; } - private static ChatClientBuilder AddOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) - { - return builder.AddOpenAIClient(connectionName, settings => + private static ChatClientBuilder AddOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) => + builder.AddOpenAIClient(connectionName, settings => { settings.Endpoint = connectionInfo.Endpoint; settings.Key = connectionInfo.AccessKey; }) .AddChatClient(connectionInfo.SelectedModel); - } - private static ChatClientBuilder AddAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) - { - return builder.Services.AddChatClient(sp => + private static ChatClientBuilder AddAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) => + builder.Services.AddChatClient(sp => { var credential = new AzureKeyCredential(connectionInfo.AccessKey!); @@ -56,16 +53,12 @@ public static class ChatClientExtensions return client.AsIChatClient(connectionInfo.SelectedModel); }); - } private static ChatClientBuilder AddOllamaClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) { var httpKey = $"{connectionName}_http"; - builder.Services.AddHttpClient(httpKey, c => - { - c.BaseAddress = connectionInfo.Endpoint; - }); + builder.Services.AddHttpClient(httpKey, c => c.BaseAddress = connectionInfo.Endpoint); return builder.Services.AddChatClient(sp => { @@ -102,19 +95,16 @@ public static class ChatClientExtensions return chatClientBuilder; } - private static ChatClientBuilder AddKeyedOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) - { - return builder.AddKeyedOpenAIClient(connectionName, settings => + private static ChatClientBuilder AddKeyedOpenAIClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) => + builder.AddKeyedOpenAIClient(connectionName, settings => { settings.Endpoint = connectionInfo.Endpoint; settings.Key = connectionInfo.AccessKey; }) .AddKeyedChatClient(connectionName, connectionInfo.SelectedModel); - } - private static ChatClientBuilder AddKeyedAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) - { - return builder.Services.AddKeyedChatClient(connectionName, sp => + private static ChatClientBuilder AddKeyedAzureInferenceClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) => + builder.Services.AddKeyedChatClient(connectionName, sp => { var credential = new AzureKeyCredential(connectionInfo.AccessKey!); @@ -122,16 +112,12 @@ public static class ChatClientExtensions return client.AsIChatClient(connectionInfo.SelectedModel); }); - } private static ChatClientBuilder AddKeyedOllamaClient(this IHostApplicationBuilder builder, string connectionName, ChatClientConnectionInfo connectionInfo) { var httpKey = $"{connectionName}_http"; - builder.Services.AddHttpClient(httpKey, c => - { - c.BaseAddress = connectionInfo.Endpoint; - }); + builder.Services.AddHttpClient(httpKey, c => c.BaseAddress = connectionInfo.Endpoint); return builder.Services.AddKeyedChatClient(connectionName, sp => { diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AActorClient.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AActorClient.cs index b5be434617..956036d10e 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AActorClient.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AActorClient.cs @@ -19,7 +19,7 @@ internal sealed class A2AActorClient : IActorClient // because A2A sdk does not provide a client which can handle multiple agents, we need a client per agent // for this app the convention is "baseUri/" - private readonly ConcurrentDictionary _clients = new(); + private readonly ConcurrentDictionary _clients = []; public A2AActorClient(ILogger logger, Uri baseUri) { @@ -35,10 +35,7 @@ internal sealed class A2AActorClient : IActorClient return a2aCardResolver.GetAgentCardAsync(cancellationToken); } - public ValueTask GetResponseAsync(ActorId actorId, string messageId, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } + public ValueTask GetResponseAsync(ActorId actorId, string messageId, CancellationToken cancellationToken) => throw new NotImplementedException(); public ValueTask SendRequestAsync(ActorRequest request, CancellationToken cancellationToken) { @@ -51,9 +48,8 @@ internal sealed class A2AActorClient : IActorClient private (A2AClient, A2ACardResolver) ResolveClient(ActorType agentName) => this.ResolveClient(agentName.Name); - private (A2AClient, A2ACardResolver) ResolveClient(string agentName) - { - return this._clients.GetOrAdd(agentName, name => + private (A2AClient, A2ACardResolver) ResolveClient(string agentName) => + this._clients.GetOrAdd(agentName, name => { var uri = new Uri($"{this._uri}/{name}/"); var a2aClient = new A2AClient(uri); @@ -64,7 +60,6 @@ internal sealed class A2AActorClient : IActorClient this._logger.LogInformation("Built clients for agent {Agent} with baseUri {Uri}", name, uri); return (a2aClient, a2aCardResolver); }); - } private sealed class A2AActorResponseHandle : ActorResponseHandle { @@ -77,20 +72,11 @@ internal sealed class A2AActorClient : IActorClient this._request = request; } - public override ValueTask CancelAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } + public override ValueTask CancelAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); - public override ValueTask GetResponseAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } + public override ValueTask GetResponseAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); - public override bool TryGetResponse([NotNullWhen(true)] out ActorResponse? response) - { - throw new NotImplementedException(); - } + public override bool TryGetResponse([NotNullWhen(true)] out ActorResponse? response) => throw new NotImplementedException(); public override async IAsyncEnumerable WatchUpdatesAsync([EnumeratorCancellation] CancellationToken cancellationToken) { diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor b/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor index fcaa7c6ef6..500419e8b1 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor @@ -31,8 +31,5 @@ private string? requestId; private bool ShowRequestId => !string.IsNullOrEmpty(requestId); - protected override void OnInitialized() - { - requestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; - } + protected override void OnInitialized() => requestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; } diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor b/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor index 9e15c145e3..ae7932d566 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor @@ -907,7 +907,7 @@ A2A // Agent-to-Agent protocol } - private class Conversation + private sealed class Conversation { public string SessionId { get; set; } = Guid.NewGuid().ToString(); public string AgentName { get; set; } = ""; @@ -944,32 +944,23 @@ // Conversations start fresh on page load } - private string GetAgentIcon(string agentName) - { - return agentName?.ToLower() switch - { - "pirate" => "🏴‍☠️", - "knights-and-knaves" => "⚔️", - _ => "🤖" - }; - } + private string GetAgentIcon(string agentName) => agentName?.ToLower() switch + { + "pirate" => "🏴‍☠️", + "knights-and-knaves" => "⚔️", + _ => "🤖" + }; - private string GetAgentDisplayName(string agentName) - { - return agentName?.ToLower() switch - { - "pirate" => "Pirate", - "knights-and-knaves" => "Knights & Knaves", - _ => agentName ?? "Agent" - }; - } + private string GetAgentDisplayName(string agentName) => agentName?.ToLower() switch + { + "pirate" => "Pirate", + "knights-and-knaves" => "Knights & Knaves", + _ => agentName ?? "Agent" + }; - private void ToggleA2AExpanded() - { - isA2AExpanded = !isA2AExpanded; - } + private void ToggleA2AExpanded() => isA2AExpanded = !isA2AExpanded; - private async Task DiscoverAgentCard() + private async Task DiscoverAgentCard() { if (string.IsNullOrEmpty(selectedAgentName) || isDiscoveringCard) return; diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/HttpActorClient.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/HttpActorClient.cs index ed841f8799..bd75eab36a 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/HttpActorClient.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/HttpActorClient.cs @@ -99,7 +99,7 @@ internal sealed class HttpActorClient(HttpClient httpClient) : IActorClient public override bool TryGetResponse([NotNullWhen(true)] out ActorResponse? response) { response = this._lastResponse; - return response != null; + return response is not null; } public override async IAsyncEnumerable WatchUpdatesAsync([EnumeratorCancellation] CancellationToken cancellationToken) @@ -138,7 +138,7 @@ internal sealed class HttpActorClient(HttpClient httpClient) : IActorClient private static async IAsyncEnumerable EnumerateAsync(HttpResponseMessage responseMessage, [EnumeratorCancellation] CancellationToken cancellationToken) { var responseStream = await responseMessage.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - var sseParser = SseParser.Create(responseStream, (eventType, data) => + var sseParser = SseParser.Create(responseStream, (eventType, data) => { if (eventType != "message") { @@ -159,26 +159,13 @@ internal sealed class HttpActorClient(HttpClient httpClient) : IActorClient } } - private async Task ReadResponseAsync(HttpResponseMessage responseMessage, CancellationToken cancellationToken) - { - var response = await responseMessage.Content.ReadFromJsonAsync(AgentRuntimeJsonUtilities.DefaultOptions, cancellationToken).ConfigureAwait(false); - if (response == null) - { - throw new InvalidOperationException($"No response found for actor '{actorId}' with message ID '{messageId}'."); - } + private async Task ReadResponseAsync(HttpResponseMessage responseMessage, CancellationToken cancellationToken) => + await responseMessage.Content.ReadFromJsonAsync(AgentRuntimeJsonUtilities.DefaultOptions, cancellationToken).ConfigureAwait(false) ?? + throw new InvalidOperationException($"No response found for actor '{actorId}' with message ID '{messageId}'."); - return response; - } + private static bool IsJsonResponse([NotNullWhen(true)] HttpResponseMessage? response) => response?.Content.Headers.ContentType?.MediaType == "application/json"; - private static bool IsJsonResponse([NotNullWhen(true)] HttpResponseMessage? response) - { - return response?.Content.Headers.ContentType?.MediaType == "application/json"; - } - - private static bool IsStreamingResponse([NotNullWhen(true)] HttpResponseMessage? response) - { - return response?.Content.Headers.ContentType?.MediaType == "text/event-stream"; - } + private static bool IsStreamingResponse([NotNullWhen(true)] HttpResponseMessage? response) => response?.Content.Headers.ContentType?.MediaType == "text/event-stream"; protected override void Dispose(bool disposing) { diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Program.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/Program.cs index 7d6eae8199..ec34db5a64 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Program.cs +++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/Program.cs @@ -24,10 +24,7 @@ Uri a2aAddress = new("http://localhost:5390/a2a"); builder.Services.AddHttpClient(client => client.BaseAddress = baseAddress); builder.Services.AddHttpClient(client => client.BaseAddress = baseAddress); -builder.Services.AddSingleton(sp => -{ - return new A2AActorClient(sp.GetRequiredService>(), a2aAddress); -}); +builder.Services.AddSingleton(sp => new A2AActorClient(sp.GetRequiredService>(), a2aAddress)); var app = builder.Build(); diff --git a/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs b/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs index e42506b948..e28eb3a81a 100644 --- a/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs +++ b/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs @@ -42,7 +42,7 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder() .AddSource(SourceName) // Our custom activity source .AddSource("Microsoft.Extensions.AI.Agents") // Agent Framework telemetry .AddHttpClientInstrumentation() // Capture HTTP calls to OpenAI - .AddOtlpExporter(options => { options.Endpoint = new Uri(otlpEndpoint); }) + .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint)) .Build(); // Setup metrics with resource and instrument name filtering @@ -52,7 +52,7 @@ using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("Microsoft.Extensions.AI.Agents") // Agent Framework metrics .AddHttpClientInstrumentation() // HTTP client metrics .AddRuntimeInstrumentation() // .NET runtime metrics - .AddOtlpExporter(options => { options.Endpoint = new Uri(otlpEndpoint); }) + .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint)) .Build(); // Setup structured logging with OpenTelemetry @@ -62,10 +62,7 @@ serviceCollection.AddLogging(loggingBuilder => loggingBuilder .AddOpenTelemetry(options => { options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(ServiceName, serviceVersion: "1.0.0")); - options.AddOtlpExporter(otlpOptions => - { - otlpOptions.Endpoint = new Uri(otlpEndpoint); - }); + options.AddOtlpExporter(otlpOptions => otlpOptions.Endpoint = new Uri(otlpEndpoint)); options.IncludeScopes = true; options.IncludeFormattedMessage = true; })); @@ -97,7 +94,7 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT appLogger.LogInformation("OpenTelemetry Aspire Demo application started"); [Description("Get the weather for a given location.")] -static async Task GetWeather([Description("The location to get the weather for.")] string location) +static async Task GetWeatherAsync([Description("The location to get the weather for.")] string location) { await Task.Delay(2000); return $"The weather in {location} is cloudy with a high of 15°C."; @@ -109,7 +106,7 @@ using var instrumentedChatClient = new AzureOpenAIClient(new Uri(endpoint), new .AsIChatClient() // Converts a native OpenAI SDK ChatClient into a Microsoft.Extensions.AI.IChatClient .AsBuilder() .UseFunctionInvocation() - .UseOpenTelemetry(loggerFactory: loggerFactory, sourceName: SourceName, (cfg) => { cfg.EnableSensitiveData = true; }) + .UseOpenTelemetry(loggerFactory: loggerFactory, sourceName: SourceName, (cfg) => cfg.EnableSensitiveData = true) .Build(); appLogger.LogInformation("Creating Agent with OpenTelemetry instrumentation"); @@ -117,7 +114,7 @@ appLogger.LogInformation("Creating Agent with OpenTelemetry instrumentation"); using var agent = new ChatClientAgent(instrumentedChatClient, name: "OpenTelemetryDemoAgent", instructions: "You are a helpful assistant that provides concise and informative responses.", - tools: [AIFunctionFactory.Create(GetWeather)]) + tools: [AIFunctionFactory.Create(GetWeatherAsync)]) .WithOpenTelemetry(loggerFactory, SourceName); // Enable telemetry on the agent var thread = agent.GetNewThread(); @@ -127,9 +124,10 @@ appLogger.LogInformation("Agent created successfully with ID: {AgentId}", agent. // Create a parent span for the entire agent session using var sessionActivity = activitySource.StartActivity("Agent Session"); var sessionId = thread.ConversationId ?? Guid.NewGuid().ToString(); -sessionActivity?.SetTag("agent.name", "OpenTelemetryDemoAgent"); -sessionActivity?.SetTag("session.id", sessionId); -sessionActivity?.SetTag("session.start_time", DateTimeOffset.UtcNow.ToString("O")); +sessionActivity? + .SetTag("agent.name", "OpenTelemetryDemoAgent") + .SetTag("session.id", sessionId) + .SetTag("session.start_time", DateTimeOffset.UtcNow.ToString("O")); appLogger.LogInformation("Starting agent session with ID: {SessionId}", sessionId); using (appLogger.BeginScope(new Dictionary { ["SessionId"] = sessionId, ["AgentName"] = "OpenTelemetryDemoAgent" })) @@ -152,11 +150,12 @@ using (appLogger.BeginScope(new Dictionary { ["SessionId"] = ses // Create a child span for each individual interaction using var activity = activitySource.StartActivity("Agent Interaction"); - activity?.SetTag("user.input", userInput); - activity?.SetTag("agent.name", "OpenTelemetryDemoAgent"); - activity?.SetTag("interaction.number", interactionCount); + activity? + .SetTag("user.input", userInput) + .SetTag("agent.name", "OpenTelemetryDemoAgent") + .SetTag("interaction.number", interactionCount); - var stopwatch = System.Diagnostics.Stopwatch.StartNew(); + var stopwatch = Stopwatch.StartNew(); try { @@ -197,9 +196,10 @@ using (appLogger.BeginScope(new Dictionary { ["SessionId"] = ses responseTimeHistogram.Record(responseTime, new KeyValuePair("status", "error")); - activity?.SetTag("response.success", false); - activity?.SetTag("error.message", ex.Message); - activity?.SetStatus(ActivityStatusCode.Error, ex.Message); + activity? + .SetTag("response.success", false) + .SetTag("error.message", ex.Message) + .SetStatus(ActivityStatusCode.Error, ex.Message); appLogger.LogError(ex, "Agent interaction #{InteractionNumber} failed after {ResponseTime:F2} seconds: {ErrorMessage}", interactionCount, responseTime, ex.Message); @@ -207,8 +207,9 @@ using (appLogger.BeginScope(new Dictionary { ["SessionId"] = ses } // Add session summary to the parent span - sessionActivity?.SetTag("session.total_interactions", interactionCount); - sessionActivity?.SetTag("session.end_time", DateTimeOffset.UtcNow.ToString("O")); + sessionActivity? + .SetTag("session.total_interactions", interactionCount) + .SetTag("session.end_time", DateTimeOffset.UtcNow.ToString("O")); appLogger.LogInformation("Agent session completed. Total interactions: {TotalInteractions}", interactionCount); } // End of logging scope diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs b/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs index 0f86869579..d39cb0c793 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs @@ -32,7 +32,7 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) AzureAIAgentsPersistent } - protected IChatClient GetChatClient(ChatClientProviders provider, ChatClientAgentOptions? options = null) + protected static IChatClient GetChatClient(ChatClientProviders provider, ChatClientAgentOptions? options = null) => provider switch { ChatClientProviders.OpenAIChatCompletion => GetOpenAIChatClient(), @@ -46,14 +46,6 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) _ => throw new NotSupportedException($"Provider {provider} is not supported.") }; - protected ChatOptions? GetChatOptions(ChatClientProviders? provider) - => provider switch - { - ChatClientProviders.OpenAIResponses_InMemoryMessageThread => new() { RawRepresentationFactory = static (_) => new ResponseCreationOptions() { StoredOutputEnabled = false } }, - ChatClientProviders.OpenAIResponses_ConversationIdThread => new() { RawRepresentationFactory = static (_) => new ResponseCreationOptions() { StoredOutputEnabled = true } }, - _ => null - }; - /// /// For providers that store the agent and the thread on the server side, this will clean and delete /// any sample agent and thread that was created during this execution. @@ -65,16 +57,14 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) /// /// Ideally for faster execution and potential cost savings, server-side agents should be reused. /// - protected Task AgentCleanUpAsync(ChatClientProviders provider, AIAgent agent, AgentThread? thread = null, CancellationToken cancellationToken = default) - { - return provider switch + protected static Task AgentCleanUpAsync(ChatClientProviders provider, AIAgent agent, AgentThread? thread = null, CancellationToken cancellationToken = default) + => provider switch { ChatClientProviders.AzureAIAgentsPersistent => AzureAIAgentsPersistentAgentCleanUpAsync(agent, thread, cancellationToken), ChatClientProviders.OpenAIAssistant => OpenAIAssistantCleanUpAgentAsync(agent, thread, cancellationToken), // For other remaining provider sample types, no cleanup is needed as they don't offer a server-side agent/thread clean-up API. _ => Task.CompletedTask }; - } /// /// Creates a server-side agent identifier based on the specified provider and options. @@ -84,23 +74,21 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) /// The to monitor for cancellation requests. The default is . /// The identifier of the created agent, or if the provider does not use server-side agents. /// Some server-side agent providers require an agent id reference to be created before it can be invoked. - protected Task AgentCreateAsync(ChatClientProviders provider, ChatClientAgentOptions options, CancellationToken cancellationToken = default) - { - return provider switch + protected static Task AgentCreateAsync(ChatClientProviders provider, ChatClientAgentOptions options, CancellationToken cancellationToken = default) + => provider switch { ChatClientProviders.OpenAIAssistant => OpenAIAssistantCreateAgentAsync(options, cancellationToken), ChatClientProviders.AzureAIAgentsPersistent => AzureAIAgentsPersistentCreateAgentAsync(options, cancellationToken), _ => Task.FromResult(null) }; - } #region Private GetChatClient - private IChatClient GetOpenAIChatClient() + private static IChatClient GetOpenAIChatClient() => new ChatClient(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .AsIChatClient(); - private IChatClient GetAzureOpenAIChatClient() + private static IChatClient GetAzureOpenAIChatClient() => ((TestConfiguration.AzureOpenAI.ApiKey is null) // Use Azure CLI credentials if API key is not provided. ? new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new AzureCliCredential()) @@ -108,23 +96,23 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) .GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName) .AsIChatClient(); - private IChatClient GetOpenAIResponsesClient() + private static IChatClient GetOpenAIResponsesClient() => new OpenAIResponseClient(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .AsIChatClient(); - private IChatClient GetAzureAIAgentPersistentClient(ChatClientAgentOptions options) + private static IChatClient GetAzureAIAgentPersistentClient(ChatClientAgentOptions options) => new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential()).AsNewIChatClient(options.Id!); - private IChatClient GetOpenAIAssistantChatClient(ChatClientAgentOptions options) + private static IChatClient GetOpenAIAssistantChatClient(ChatClientAgentOptions options) => new AssistantClient(TestConfiguration.OpenAI.ApiKey).AsIChatClient(options.Id!); #endregion #region Private AgentCreate - private async Task AzureAIAgentsPersistentCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken) + private static async Task AzureAIAgentsPersistentCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken) { - var persistentAgentsClient = new Azure.AI.Agents.Persistent.PersistentAgentsAdministrationClient( + var persistentAgentsClient = new PersistentAgentsAdministrationClient( TestConfiguration.AzureAI.Endpoint, new AzureCliCredential()); @@ -138,9 +126,9 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) return result?.Value.Id; } - private async Task OpenAIAssistantCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken) + private static async Task OpenAIAssistantCreateAgentAsync(ChatClientAgentOptions options, CancellationToken cancellationToken) { - var assistantClient = new OpenAI.Assistants.AssistantClient(TestConfiguration.OpenAI.ApiKey); + var assistantClient = new AssistantClient(TestConfiguration.OpenAI.ApiKey); Assistant assistant = await assistantClient.CreateAssistantAsync( TestConfiguration.OpenAI.ChatModelId, new() @@ -157,7 +145,7 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) #region Private AgentCleanUp - private async Task AzureAIAgentsPersistentAgentCleanUpAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken) + private static async Task AzureAIAgentsPersistentAgentCleanUpAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken) { var persistentAgentsClient = (agent as ChatClientAgent)?.ChatClient.GetService() ?? throw new InvalidOperationException("The provided chat client is not a Persistent Agents Chat Client"); @@ -171,7 +159,7 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) } } - private async Task OpenAIAssistantCleanUpAgentAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken) + private static async Task OpenAIAssistantCleanUpAgentAsync(AIAgent agent, AgentThread? thread, CancellationToken cancellationToken) { var assistantClient = (agent as ChatClientAgent)?.ChatClient .GetService() diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_Intro.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_Intro.cs index 6f152caaa6..26d7053d0f 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_Intro.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_Intro.cs @@ -18,11 +18,11 @@ public class ConcurrentOrchestration_Intro(ITestOutputHelper output) : Orchestra { // Define the agents ChatClientAgent physicist = - this.CreateAgent( + CreateAgent( instructions: "You are an expert in physics. You answer questions from a physics perspective.", description: "An expert in physics"); ChatClientAgent chemist = - this.CreateAgent( + CreateAgent( instructions: "You are an expert in chemistry. You answer questions from a chemistry perspective.", description: "An expert in chemistry"); @@ -36,14 +36,14 @@ public class ConcurrentOrchestration_Intro(ITestOutputHelper output) : Orchestra new(physicist, chemist) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, - StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null, + ResponseCallback = monitor.ResponseCallbackAsync, + StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null, }; // Run the orchestration - string input = "What is temperature?"; - Console.WriteLine($"\n# INPUT: {input}\n"); - AgentRunResponse result = await orchestration.RunAsync(input); + const string Input = "What is temperature?"; + Console.WriteLine($"\n# INPUT: {Input}\n"); + AgentRunResponse result = await orchestration.RunAsync(Input); Console.WriteLine($"\n# RESULT:\n{string.Join("\n\n", result.Messages.Select(r => $"{r.Text}"))}"); diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs index 1f4ad9ac04..3005db5236 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs @@ -19,15 +19,15 @@ public class ConcurrentOrchestration_With_StructuredOutput(ITestOutputHelper out { // Define the agents ChatClientAgent agent1 = - this.CreateAgent( + CreateAgent( instructions: "You are an expert in identifying themes in articles. Given an article, identify the main themes.", description: "An expert in identifying themes in articles"); ChatClientAgent agent2 = - this.CreateAgent( + CreateAgent( instructions: "You are an expert in sentiment analysis. Given an article, identify the sentiment.", description: "An expert in sentiment analysis"); ChatClientAgent agent3 = - this.CreateAgent( + CreateAgent( instructions: "You are an expert in entity recognition. Given an article, extract the entities.", description: "An expert in entity recognition"); @@ -35,11 +35,11 @@ public class ConcurrentOrchestration_With_StructuredOutput(ITestOutputHelper out ConcurrentOrchestration orchestration = new(agent1, agent2, agent3) { LoggerFactory = this.LoggerFactory }; // Run the orchestration - const string resourceId = "Hamlet_full_play_summary.txt"; - string input = Resources.Read(resourceId); - Console.WriteLine($"\n# INPUT: @{resourceId}\n"); + const string ResourceId = "Hamlet_full_play_summary.txt"; + string input = Resources.Read(ResourceId); + Console.WriteLine($"\n# INPUT: @{ResourceId}\n"); - var output = await orchestration.RunAsync(this.CreateChatClient(), input); + var output = await orchestration.RunAsync(CreateChatClient(), input); Console.WriteLine($"\n# RESULT:\n{JsonSerializer.Serialize(output, s_options)}"); } @@ -50,5 +50,5 @@ public class ConcurrentOrchestration_With_StructuredOutput(ITestOutputHelper out public IList Sentiments { get; set; } = []; public IList Entities { get; set; } = []; } -#pragma warning restore CA1812 // Avoid uninstantiated internal classes +#pragma warning restore CA1812 } diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_Intro.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_Intro.cs index 9ef505adeb..8f7e9e5f96 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_Intro.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_Intro.cs @@ -24,7 +24,7 @@ public class GroupChatOrchestration_Intro(ITestOutputHelper output) : Orchestrat { // Define the agents ChatClientAgent writer = - this.CreateAgent( + CreateAgent( name: "CopyWriter", description: "A copy writer", instructions: @@ -37,7 +37,7 @@ public class GroupChatOrchestration_Intro(ITestOutputHelper output) : Orchestrat Consider suggestions when refining an idea. """); ChatClientAgent editor = - this.CreateAgent( + CreateAgent( name: "Reviewer", description: "An editor.", instructions: @@ -62,13 +62,13 @@ public class GroupChatOrchestration_Intro(ITestOutputHelper output) : Orchestrat editor) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, - StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null, + ResponseCallback = monitor.ResponseCallbackAsync, + StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null, }; - string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive."; - Console.WriteLine($"\n# INPUT: {input}\n"); - AgentRunResponse result = await orchestration.RunAsync(input); + const string Input = "Create a slogon for a new eletric SUV that is affordable and fun to drive."; + Console.WriteLine($"\n# INPUT: {Input}\n"); + AgentRunResponse result = await orchestration.RunAsync(Input); Console.WriteLine($"\n# RESULT: {result}"); this.DisplayHistory(monitor.History); diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_AIManager.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_AIManager.cs index fbbc6ba317..ca5f27c1f2 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_AIManager.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_AIManager.cs @@ -18,7 +18,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O { // Define the agents ChatClientAgent farmer = - this.CreateAgent( + CreateAgent( name: "Farmer", description: "A rural farmer from Southeast Asia.", instructions: @@ -29,7 +29,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O You are in a debate. Feel free to challenge the other participants with respect. """); ChatClientAgent developer = - this.CreateAgent( + CreateAgent( name: "Developer", description: "An urban software developer from the United States.", instructions: @@ -40,7 +40,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O You are in a debate. Feel free to challenge the other participants with respect. """); ChatClientAgent teacher = - this.CreateAgent( + CreateAgent( name: "Teacher", description: "A retired history teacher from Eastern Europe", instructions: @@ -51,7 +51,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O You are in a debate. Feel free to challenge the other participants with respect. """); ChatClientAgent activist = - this.CreateAgent( + CreateAgent( name: "Activist", description: "A young activist from South America.", instructions: @@ -61,7 +61,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O You are in a debate. Feel free to challenge the other participants with respect. """); ChatClientAgent spiritual = - this.CreateAgent( + CreateAgent( name: "SpiritualLeader", description: "A spiritual leader from the Middle East.", instructions: @@ -71,7 +71,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O You are in a debate. Feel free to challenge the other participants with respect. """); ChatClientAgent artist = - this.CreateAgent( + CreateAgent( name: "Artist", description: "An artist from Africa.", instructions: @@ -81,7 +81,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O You are in a debate. Feel free to challenge the other participants with respect. """); ChatClientAgent immigrant = - this.CreateAgent( + CreateAgent( name: "Immigrant", description: "An immigrant entrepreneur from Asia living in Canada.", instructions: @@ -92,7 +92,7 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O You are in a debate. Feel free to challenge the other participants with respect. """); ChatClientAgent doctor = - this.CreateAgent( + CreateAgent( name: "Doctor", description: "A doctor from Scandinavia.", instructions: @@ -108,12 +108,12 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O OrchestrationMonitor monitor = new(); // Define the orchestration - const string topic = "What does a good life mean to you personally?"; + const string Topic = "What does a good life mean to you personally?"; GroupChatOrchestration orchestration = new( new AIGroupChatManager( - topic, - this.CreateChatClient()) + Topic, + CreateChatClient()) { MaximumInvocationCount = 5 }, @@ -127,12 +127,12 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O doctor) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, + ResponseCallback = monitor.ResponseCallbackAsync, }; // Run the orchestration - Console.WriteLine($"\n# INPUT: {topic}\n"); - AgentRunResponse result = await orchestration.RunAsync(topic); + Console.WriteLine($"\n# INPUT: {Topic}\n"); + AgentRunResponse result = await orchestration.RunAsync(Topic); Console.WriteLine($"\n# RESULT: {result}"); this.DisplayHistory(monitor.History); @@ -167,21 +167,21 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O } /// - protected override ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default) => + protected override ValueTask> FilterResultsAsync(IReadOnlyCollection history, CancellationToken cancellationToken = default) => this.GetResponseAsync(history, Prompts.Filter(topic), cancellationToken); /// - protected override ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default) => + protected override ValueTask> SelectNextAgentAsync(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default) => this.GetResponseAsync(history, Prompts.Selection(topic, team.FormatList()), cancellationToken); /// - protected override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default) => + protected override ValueTask> ShouldRequestUserInputAsync(IReadOnlyCollection history, CancellationToken cancellationToken = default) => new(new GroupChatManagerResult(false) { Reason = "The AI group chat manager does not request user input." }); /// - protected override async ValueTask> ShouldTerminate(IReadOnlyCollection history, CancellationToken cancellationToken = default) + protected override async ValueTask> ShouldTerminateAsync(IReadOnlyCollection history, CancellationToken cancellationToken = default) { - GroupChatManagerResult result = await base.ShouldTerminate(history, cancellationToken); + GroupChatManagerResult result = await base.ShouldTerminateAsync(history, cancellationToken); if (!result.Value) { result = await this.GetResponseAsync(history, Prompts.Termination(topic), cancellationToken); diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs index d35892ecb9..5e77aff593 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs @@ -16,7 +16,7 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output { // Define the agents ChatClientAgent writer = - this.CreateAgent( + CreateAgent( name: "CopyWriter", description: "A copy writer", instructions: @@ -29,7 +29,7 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output Consider suggestions when refining an idea. """); ChatClientAgent editor = - this.CreateAgent( + CreateAgent( name: "Reviewer", description: "An editor.", instructions: @@ -63,13 +63,13 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output editor) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, + ResponseCallback = monitor.ResponseCallbackAsync, }; // Run the orchestration - string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive."; - Console.WriteLine($"\n# INPUT: {input}\n"); - AgentRunResponse result = await orchestration.RunAsync(input); + const string Input = "Create a slogon for a new eletric SUV that is affordable and fun to drive."; + Console.WriteLine($"\n# INPUT: {Input}\n"); + AgentRunResponse result = await orchestration.RunAsync(Input); Console.WriteLine($"\n# RESULT: {result}"); this.DisplayHistory(monitor.History); @@ -84,7 +84,7 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output /// private sealed class CustomRoundRobinGroupChatManager : RoundRobinGroupChatManager { - protected override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default) + protected override ValueTask> ShouldRequestUserInputAsync(IReadOnlyCollection history, CancellationToken cancellationToken = default) { string? lastAgent = history.LastOrDefault()?.AuthorName; diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_Intro.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_Intro.cs index aaf0d70946..e8a68d0dab 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_Intro.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_Intro.cs @@ -20,24 +20,24 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio { // Define the agents & tools ChatClientAgent triageAgent = - this.CreateAgent( + CreateAgent( instructions: "A customer support agent that triages issues.", name: "TriageAgent", description: "Handle customer requests."); ChatClientAgent statusAgent = - this.CreateAgent( + CreateAgent( name: "OrderStatusAgent", instructions: "Handle order status requests.", description: "A customer support agent that checks order status.", functions: AIFunctionFactory.Create(OrderFunctions.CheckOrderStatus)); ChatClientAgent returnAgent = - this.CreateAgent( + CreateAgent( name: "OrderReturnAgent", instructions: "Handle order return requests.", description: "A customer support agent that handles order returns.", functions: AIFunctionFactory.Create(OrderFunctions.ProcessReturn)); ChatClientAgent refundAgent = - this.CreateAgent( + CreateAgent( name: "OrderRefundAgent", instructions: "Handle order refund requests.", description: "A customer support agent that handles order refund.", @@ -49,7 +49,7 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio OrchestrationMonitor monitor = new(); // Define user responses for InteractiveCallback (since sample is not interactive) Queue responses = new(); - string task = "I am a customer that needs help with my orders"; + const string Task = "I am a customer that needs help with my orders"; responses.Enqueue("I'd like to track the status of my order"); responses.Enqueue("My order ID is 123"); responses.Enqueue("I want to return another order of mine"); @@ -75,13 +75,13 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio return new(input); }, LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, - StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null, + ResponseCallback = monitor.ResponseCallbackAsync, + StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null, }; // Run the orchestration - Console.WriteLine($"\n# INPUT:\n{task}\n"); - AgentRunResponse result = await orchestration.RunAsync(task); + Console.WriteLine($"\n# INPUT:\n{Task}\n"); + AgentRunResponse result = await orchestration.RunAsync(Task); Console.WriteLine($"\n# RESULT: {result}"); diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_With_StructuredInput.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_With_StructuredInput.cs index dd17d56891..509df9df7f 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_With_StructuredInput.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/HandoffOrchestration_With_StructuredInput.cs @@ -22,18 +22,18 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output) // Define the agents ChatClientAgent triageAgent = - this.CreateAgent( + CreateAgent( instructions: "Given a GitHub issue, triage it.", name: "TriageAgent", description: "An agent that triages GitHub issues"); ChatClientAgent pythonAgent = - this.CreateAgent( + CreateAgent( instructions: "You are an agent that handles Python related GitHub issues.", name: "PythonAgent", description: "An agent that handles Python related issues", functions: githubAddLabelFunction); ChatClientAgent dotnetAgent = - this.CreateAgent( + CreateAgent( instructions: "You are an agent that handles .NET related GitHub issues.", name: "DotNetAgent", description: "An agent that handles .NET related issues", @@ -51,7 +51,7 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output) .Add(triageAgent, [dotnetAgent, pythonAgent])) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, + ResponseCallback = monitor.ResponseCallbackAsync, }; GithubIssue input = @@ -105,9 +105,6 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output) { public Dictionary Labels { get; } = []; - public void AddLabels(string issueId, params string[] labels) - { - this.Labels[issueId] = labels; - } + public void AddLabels(string issueId, params string[] labels) => this.Labels[issueId] = labels; } } diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Foundry_Agents.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Foundry_Agents.cs index a9ff1ee208..aa94f09ccc 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Foundry_Agents.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Foundry_Agents.cs @@ -67,14 +67,14 @@ public class SequentialOrchestration_Foundry_Agents(ITestOutputHelper output) : new(analystAgent, writerAgent, editorAgent) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, - StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null, + ResponseCallback = monitor.ResponseCallbackAsync, + StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null, }; // Run the orchestration - string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours"; - Console.WriteLine($"\n# INPUT: {input}\n"); - AgentRunResponse result = await orchestration.RunAsync(input); + const string Input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours"; + Console.WriteLine($"\n# INPUT: {Input}\n"); + AgentRunResponse result = await orchestration.RunAsync(Input); Console.WriteLine($"\n# RESULT: {result}"); this.DisplayHistory(monitor.History); diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Intro.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Intro.cs index f0bfe28d3e..a2c05b7ad8 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Intro.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Intro.cs @@ -19,7 +19,7 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra { // Define the agents ChatClientAgent analystAgent = - this.CreateAgent( + CreateAgent( name: "Analyst", instructions: """ @@ -30,7 +30,7 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra """, description: "A agent that extracts key concepts from a product description."); ChatClientAgent writerAgent = - this.CreateAgent( + CreateAgent( name: "copywriter", instructions: """ @@ -40,7 +40,7 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra """, description: "An agent that writes a marketing copy based on the extracted concepts."); ChatClientAgent editorAgent = - this.CreateAgent( + CreateAgent( name: "editor", instructions: """ @@ -58,14 +58,14 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra new(analystAgent, writerAgent, editorAgent) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, - StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null, + ResponseCallback = monitor.ResponseCallbackAsync, + StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null, }; // Run the orchestration - string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours"; - Console.WriteLine($"\n# INPUT: {input}\n"); - AgentRunResponse result = await orchestration.RunAsync(input); + const string Input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours"; + Console.WriteLine($"\n# INPUT: {Input}\n"); + AgentRunResponse result = await orchestration.RunAsync(Input); Console.WriteLine($"\n# RESULT: {result}"); this.DisplayHistory(monitor.History); diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Multi_Agent.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Multi_Agent.cs index f79b6ad616..7edabed6f6 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Multi_Agent.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_Multi_Agent.cs @@ -63,14 +63,14 @@ public class SequentialOrchestration_Multi_Agent(ITestOutputHelper output) : Orc new(analystAgent, writerAgent, editorAgent) { LoggerFactory = this.LoggerFactory, - ResponseCallback = monitor.ResponseCallback, - StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null, + ResponseCallback = monitor.ResponseCallbackAsync, + StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallbackAsync : null, }; // Run the orchestration - string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours"; - Console.WriteLine($"\n# INPUT: {input}\n"); - AgentRunResponse result = await orchestration.RunAsync(input); + const string Input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours"; + Console.WriteLine($"\n# INPUT: {Input}\n"); + AgentRunResponse result = await orchestration.RunAsync(Input); Console.WriteLine($"\n# RESULT: {result}"); this.DisplayHistory(monitor.History); diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_With_Cancellation.cs b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_With_Cancellation.cs index 537efd5a62..6dcd685e0b 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_With_Cancellation.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/Orchestration/SequentialOrchestration_With_Cancellation.cs @@ -16,7 +16,7 @@ public class SequentialOrchestration_With_Cancellation(ITestOutputHelper output) { // Define the agents ChatClientAgent agent = - this.CreateAgent( + CreateAgent( """ If the input message is a number, return the number incremented by one. """, @@ -26,10 +26,10 @@ public class SequentialOrchestration_With_Cancellation(ITestOutputHelper output) SequentialOrchestration orchestration = new(agent) { LoggerFactory = this.LoggerFactory }; // Run the orchestration - string input = "42"; - Console.WriteLine($"\n# INPUT: {input}\n"); + const string Input = "42"; + Console.WriteLine($"\n# INPUT: {Input}\n"); - OrchestratingAgentResponse result = await orchestration.RunAsync([new ChatMessage(ChatRole.User, input)]); + OrchestratingAgentResponse result = await orchestration.RunAsync([new ChatMessage(ChatRole.User, Input)]); result.Cancel(); await Task.Delay(TimeSpan.FromSeconds(3)); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs index d8b20ba974..7c26c40012 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs @@ -20,7 +20,7 @@ ChatClientAgentOptions agentOptions = new(name: "HelpfulAssistant", instructions { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( + ResponseFormat = ChatResponseFormat.ForJsonSchema( schema: AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)), schemaName: "PersonInfo", schemaDescription: "Information about a person including their name, age, and occupation") diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs index 62f32cba67..cc449ce177 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs @@ -84,15 +84,13 @@ namespace SampleApp { this._vectorStore = vectorStore ?? throw new ArgumentNullException(nameof(vectorStore)); - if (serializedStoreState.ValueKind == JsonValueKind.String) + if (serializedStoreState.ValueKind is JsonValueKind.String) { // Here we can deserialize the thread id so that we can access the same messages as before the suspension. - this._threadId = JsonSerializer.Deserialize(serializedStoreState); + this._threadId = serializedStoreState.Deserialize(); } } - public string? ThreadId => this._threadId; - public async Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken) { this._threadId ??= Guid.NewGuid().ToString(); @@ -122,18 +120,15 @@ namespace SampleApp cancellationToken) .ToListAsync(cancellationToken); - var messages = records - .Select(x => JsonSerializer.Deserialize(x.SerializedMessage!)!) - .ToList(); + var messages = records.ConvertAll(x => JsonSerializer.Deserialize(x.SerializedMessage!)!) +; messages.Reverse(); return messages; } - public ValueTask SerializeStateAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - { + public ValueTask SerializeStateAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => // We have to serialize the thread id, so that on deserialization we can retrieve the messages using the same thread id. - return new ValueTask(JsonSerializer.SerializeToElement(this._threadId)); - } + new(JsonSerializer.SerializeToElement(this._threadId)); /// /// The data structure used to store chat history items in the vector store. diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs index 658ba65515..ce98572bab 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs @@ -69,7 +69,7 @@ internal sealed class SampleService(AIAgent agent, [FromKeyedServices("AppShutdo // Delay a little to allow the service to finish starting. await Task.Delay(100, cancellationToken); - while (cancellationToken.IsCancellationRequested is false) + while (!cancellationToken.IsCancellationRequested) { Console.WriteLine("\nAgent: Ask me to tell you a joke about a specific topic. To exit just press Ctrl+C or enter without any input.\n"); Console.Write("> "); @@ -83,7 +83,7 @@ internal sealed class SampleService(AIAgent agent, [FromKeyedServices("AppShutdo } // 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._thread, cancellationToken: cancellationToken)) { Console.Write(update); } diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step13_Memory/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step13_Memory/Program.cs index df6e9ecb7a..b9354a7511 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step13_Memory/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step13_Memory/Program.cs @@ -102,7 +102,7 @@ namespace SampleApp public UserInfoMemory(IChatClient chatClient, JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null) { this._chatClient = chatClient; - this.UserInfo = JsonSerializer.Deserialize(serializedState, jsonSerializerOptions) ?? new UserInfo(); + this.UserInfo = serializedState.Deserialize(jsonSerializerOptions) ?? new UserInfo(); } public UserInfo UserInfo { get; set; } @@ -110,7 +110,7 @@ namespace SampleApp public override async ValueTask InvokedAsync(InvokedContext context, CancellationToken cancellationToken = default) { // Try and extract the user name and age from the message if we don't have it already and it's a user message. - if ((this.UserInfo.UserName == null || this.UserInfo.UserAge == null) && context.RequestMessages.Any(x => x.Role == ChatRole.User)) + if ((this.UserInfo.UserName is null || this.UserInfo.UserAge is null) && context.RequestMessages.Any(x => x.Role == ChatRole.User)) { var result = await this._chatClient.GetResponseAsync( context.RequestMessages, @@ -130,15 +130,15 @@ namespace SampleApp StringBuilder instructions = new(); // If we don't already know the user's name and age, add instructions to ask for them, otherwise just provide what we have to the context. - instructions.AppendLine( - this.UserInfo.UserName == null ? - "Ask the user for their name and politely decline to answer any questions until they provide it." : - $"The user's name is {this.UserInfo.UserName}."); - - instructions.AppendLine( - this.UserInfo.UserAge == null ? - "Ask the user for their age and politely decline to answer any questions until they provide it." : - $"The user's age is {this.UserInfo.UserAge}."); + instructions + .AppendLine( + this.UserInfo.UserName is null ? + "Ask the user for their name and politely decline to answer any questions until they provide it." : + $"The user's name is {this.UserInfo.UserName}.") + .AppendLine( + this.UserInfo.UserAge is null ? + "Ask the user for their age and politely decline to answer any questions until they provide it." : + $"The user's age is {this.UserInfo.UserAge}."); return new ValueTask(new AIContext { @@ -153,7 +153,7 @@ namespace SampleApp public override ValueTask DeserializeAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { - this.UserInfo = JsonSerializer.Deserialize(serializedState, jsonSerializerOptions) ?? new UserInfo(); + this.UserInfo = serializedState.Deserialize(jsonSerializerOptions) ?? new UserInfo(); return default; } } diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs index 26e2ae22f1..2a2f3e3c63 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using System.Linq; using System.Net; using System.Net.Http; using System.Text; @@ -13,7 +12,6 @@ using System.Threading.Tasks; using System.Web; using Azure.AI.OpenAI; using Azure.Identity; -using Microsoft.Extensions.AI; using Microsoft.Extensions.AI.Agents; using Microsoft.Extensions.Logging; using ModelContextProtocol.Client; @@ -30,10 +28,7 @@ using var sharedHandler = new SocketsHttpHandler }; using var httpClient = new HttpClient(sharedHandler); -var consoleLoggerFactory = LoggerFactory.Create(builder => -{ - builder.AddConsole(); -}); +var consoleLoggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); // Create SSE client transport for the MCP server var serverUrl = "http://localhost:7071/"; @@ -59,7 +54,7 @@ AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new AzureCliCredential()) .GetChatClient(deploymentName) - .CreateAIAgent(instructions: "You answer questions related to the weather.", tools: [.. mcpTools.Select(mcpTool => (AITool)mcpTool)]); + .CreateAIAgent(instructions: "You answer questions related to the weather.", tools: [.. mcpTools]); // Invoke the agent and output the text result. Console.WriteLine(await agent.RunAsync("Get current weather alerts for New York?")); @@ -92,8 +87,8 @@ static async Task HandleAuthorizationUrlAsync(Uri authorizationUrl, Uri var code = query["code"]; var error = query["error"]; - string responseHtml = "

Authentication complete

You can close this window now.

"; - byte[] buffer = Encoding.UTF8.GetBytes(responseHtml); + const string ResponseHtml = "

Authentication complete

You can close this window now.

"; + byte[] buffer = Encoding.UTF8.GetBytes(ResponseHtml); context.Response.ContentLength64 = buffer.Length; context.Response.ContentType = "text/html"; context.Response.OutputStream.Write(buffer, 0, buffer.Length); diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs index fe1b612603..46f438fc83 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs @@ -44,17 +44,16 @@ public static class Program var feedbackProvider = new FeedbackExecutor(chatClient); // Build the workflow by adding executors and connecting them - WorkflowBuilder builder = new(sloganWriter); - builder.AddEdge(sloganWriter, feedbackProvider); - builder.AddEdge(feedbackProvider, sloganWriter); - var workflow = builder.Build(); + var workflow = new WorkflowBuilder(sloganWriter) + .AddEdge(sloganWriter, feedbackProvider) + .AddEdge(feedbackProvider, sloganWriter) + .Build(); // Execute the workflow - var ask = "Create a slogan for a new electric SUV that is affordable and fun to drive."; - StreamingRun run = await InProcessExecution.StreamAsync(workflow, ask); + StreamingRun run = await InProcessExecution.StreamAsync(workflow, "Create a slogan for a new electric SUV that is affordable and fun to drive."); await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) { - if (evt is SloganGeneratedEvent || evt is FeedbackEvent) + if (evt is SloganGeneratedEvent or FeedbackEvent) { // Custom events to allow us to monitor the progress of the workflow. Console.WriteLine($"{evt}"); @@ -114,10 +113,6 @@ internal sealed class SloganWriterExecutor IMessageHandler, IMessageHandler { - private const string Instruction = """ - You are a professional slogan writer. You will be given a task to create a slogan. - """; - private readonly AIAgent _agent; private readonly AgentThread _thread; @@ -127,13 +122,11 @@ internal sealed class SloganWriterExecutor /// The chat client to use for the AI agent. public SloganWriterExecutor(IChatClient chatClient) { - var agentOptions = new ChatClientAgentOptions(instructions: Instruction) + ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(SloganResult)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(SloganResult))) } }; @@ -184,10 +177,6 @@ internal sealed class FeedbackEvent(FeedbackResult feedbackResult) : WorkflowEve ///
internal sealed class FeedbackExecutor : ReflectingExecutor, IMessageHandler { - private const string Instruction = """ - You are a professional editor. You will be given a slogan and the task it is meant to accomplish. - """; - private readonly AIAgent _agent; private readonly AgentThread _thread; @@ -195,7 +184,7 @@ internal sealed class FeedbackExecutor : ReflectingExecutor, I public int MaxAttempts { get; init; } = 3; - private int _attempts = 0; + private int _attempts; /// /// Initializes a new instance of the class. @@ -203,13 +192,11 @@ internal sealed class FeedbackExecutor : ReflectingExecutor, I /// The chat client to use for the AI agent. public FeedbackExecutor(IChatClient chatClient) { - var agentOptions = new ChatClientAgentOptions(instructions: Instruction) + ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(FeedbackResult)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(FeedbackResult))) } }; @@ -229,11 +216,13 @@ internal sealed class FeedbackExecutor : ReflectingExecutor, I var feedback = JsonSerializer.Deserialize(response.Text) ?? throw new InvalidOperationException("Failed to deserialize feedback."); await context.AddEventAsync(new FeedbackEvent(feedback)); + if (feedback.Rating >= this.MinimumRating) { await context.AddEventAsync(new WorkflowCompletedEvent($"The following slogan was accepted:\n\n{message.Slogan}")); return; } + if (this._attempts >= this.MaxAttempts) { await context.AddEventAsync(new WorkflowCompletedEvent($"The slogan was rejected after {this.MaxAttempts} attempts. Final slogan:\n\n{message.Slogan}")); diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs index 586780f1f3..e4de06a677 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs @@ -34,10 +34,10 @@ public static class Program AIAgent englishAgent = await GetTranslationAgentAsync("English", persistentAgentsClient, model); // Build the workflow by adding executors and connecting them - WorkflowBuilder builder = new(frenchAgent); - builder.AddEdge(frenchAgent, spanishAgent); - builder.AddEdge(spanishAgent, englishAgent); - var workflow = builder.Build(); + var workflow = new WorkflowBuilder(frenchAgent) + .AddEdge(frenchAgent, spanishAgent) + .AddEdge(spanishAgent, englishAgent) + .Build(); // Execute the workflow StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!")); @@ -71,11 +71,10 @@ public static class Program PersistentAgentsClient persistentAgentsClient, string model) { - string instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}."; var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync( model: model, name: $"{targetLanguage} Translator", - instructions: instructions); + instructions: $"You are a translation assistant that translates the provided text to {targetLanguage}."); return await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id); } diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs index 7611b14984..d61415c38a 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs @@ -64,7 +64,7 @@ public static class Program Dictionary> buffer = []; await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(input, thread).ConfigureAwait(false)) { - if (update.MessageId == null) + if (update.MessageId is null) { // skip updates that don't have a message ID continue; diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs index 9470e22c10..efedca6ca5 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs +++ b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs @@ -26,11 +26,10 @@ internal static class WorkflowHelper AIAgent englishAgent = GetLanguageAgent("English", chatClient); // Build the workflow by adding executors and connecting them - WorkflowBuilder builder = new(startExecutor); - builder.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent]); - builder.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent]); - - return builder.Build>(); + return new WorkflowBuilder(startExecutor) + .AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent]) + .AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent]) + .Build>(); } /// @@ -39,11 +38,8 @@ internal static class WorkflowHelper /// The target language for translation /// The chat client to use for the agent /// A ChatClientAgent configured for the specified language - private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient) - { - string instructions = $"You're a helpful assistant who always responds in {targetLanguage}."; - return new ChatClientAgent(chatClient, instructions, name: $"{targetLanguage}Agent"); - } + private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient) => + new(chatClient, instructions: $"You're a helpful assistant who always responds in {targetLanguage}.", name: $"{targetLanguage}Agent"); /// /// Executor that starts the concurrent processing by sending messages to the agents. diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs index 7c20b773df..4e34d4eb79 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs @@ -51,7 +51,7 @@ public static class Program // Checkpoints are automatically created at the end of each super step when a // checkpoint manager is provided. You can store the checkpoint info for later use. CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint; - if (checkpoint != null) + if (checkpoint is not null) { checkpoints.Add(checkpoint); Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}."); @@ -72,9 +72,9 @@ public static class Program // Rehydrate a new workflow instance from a saved checkpoint and continue execution var newWorkflow = WorkflowHelper.GetWorkflow(); - var checkpointIndex = 5; - Console.WriteLine($"\n\nHydrating a new workflow instance from the {checkpointIndex + 1}th checkpoint."); - CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex]; + const int CheckpointIndex = 5; + Console.WriteLine($"\n\nHydrating a new workflow instance from the {CheckpointIndex + 1}th checkpoint."); + CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex]; Checkpointed newCheckpointedRun = await InProcessExecution .StreamAsync(newWorkflow, NumberSignal.Init, checkpointManager) diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs index 09ffc8db74..e1aa53dd16 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs @@ -23,12 +23,10 @@ internal static class WorkflowHelper JudgeExecutor judgeExecutor = new(42); // Build the workflow by connecting executors in a loop - var workflow = new WorkflowBuilder(guessNumberExecutor) + return new WorkflowBuilder(guessNumberExecutor) .AddEdge(guessNumberExecutor, judgeExecutor) .AddEdge(judgeExecutor, guessNumberExecutor) .Build(); - - return workflow; } } @@ -94,19 +92,15 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor - protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) - { - return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound)); - } + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) => + context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound)); /// /// Restore the state of the executor from a checkpoint. /// This must be overridden to restore any state that was saved during checkpointing. /// - protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) - { + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) => (this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false); - } } /// @@ -115,7 +109,7 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor("Judge"), IMessageHandler { private readonly int _targetNumber; - private int _tries = 0; + private int _tries; private const string StateKey = "JudgeExecutorState"; /// @@ -149,17 +143,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor("Judge /// Checkpoint the current state of the executor. /// This must be overridden to save any state that is needed to resume the executor. /// - protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) - { - return context.QueueStateUpdateAsync(StateKey, this._tries); - } + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) => + context.QueueStateUpdateAsync(StateKey, this._tries); /// /// Restore the state of the executor from a checkpoint. /// This must be overridden to restore any state that was saved during checkpointing. /// - protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) - { + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) => this._tries = await context.ReadStateAsync(StateKey).ConfigureAwait(false); - } } diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs index 4b5b5fe425..5aa3612101 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs @@ -50,7 +50,7 @@ public static class Program // Checkpoints are automatically created at the end of each super step when a // checkpoint manager is provided. You can store the checkpoint info for later use. CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint; - if (checkpoint != null) + if (checkpoint is not null) { checkpoints.Add(checkpoint); Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}."); @@ -70,9 +70,9 @@ public static class Program Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}"); // Restoring from a checkpoint and resuming execution - var checkpointIndex = 5; - Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint."); - CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex]; + const int CheckpointIndex = 5; + Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint."); + CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex]; // Note that we are restoring the state directly to the same run instance. await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false); await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs index aa4f5506ac..63be96a306 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs @@ -23,12 +23,10 @@ internal static class WorkflowHelper JudgeExecutor judgeExecutor = new(42); // Build the workflow by connecting executors in a loop - var workflow = new WorkflowBuilder(guessNumberExecutor) + return new WorkflowBuilder(guessNumberExecutor) .AddEdge(guessNumberExecutor, judgeExecutor) .AddEdge(judgeExecutor, guessNumberExecutor) .Build(); - - return workflow; } } @@ -94,19 +92,15 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor - protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) - { - return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound)); - } + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) => + context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound)); /// /// Restore the state of the executor from a checkpoint. /// This must be overridden to restore any state that was saved during checkpointing. /// - protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) - { + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) => (this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false); - } } /// @@ -115,7 +109,7 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor("Judge"), IMessageHandler { private readonly int _targetNumber; - private int _tries = 0; + private int _tries; private const string StateKey = "JudgeExecutorState"; /// @@ -149,17 +143,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor("Judge /// Checkpoint the current state of the executor. /// This must be overridden to save any state that is needed to resume the executor. /// - protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) - { - return context.QueueStateUpdateAsync(StateKey, this._tries); - } + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) => + context.QueueStateUpdateAsync(StateKey, this._tries); /// /// Restore the state of the executor from a checkpoint. /// This must be overridden to restore any state that was saved during checkpointing. /// - protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) - { + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) => this._tries = await context.ReadStateAsync(StateKey).ConfigureAwait(false); - } } diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs index f0f193a0e0..aaa75f0e2d 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs @@ -57,7 +57,7 @@ public static class Program // Checkpoints are automatically created at the end of each super step when a // checkpoint manager is provided. You can store the checkpoint info for later use. CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint; - if (checkpoint != null) + if (checkpoint is not null) { checkpoints.Add(checkpoint); Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}."); @@ -76,9 +76,9 @@ public static class Program Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}"); // Restoring from a checkpoint and resuming execution - var checkpointIndex = 1; - Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint."); - CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex]; + const int CheckpointIndex = 1; + Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint."); + CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex]; // Note that we are restoring the state directly to the same run instance. await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false); await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) @@ -109,13 +109,13 @@ public static class Program { case NumberSignal.Init: int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: "); - return request.CreateResponse(initialGuess); + return request.CreateResponse(initialGuess); case NumberSignal.Above: int lowerGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too large. Please provide a new guess: "); - return request.CreateResponse(lowerGuess); + return request.CreateResponse(lowerGuess); case NumberSignal.Below: int higherGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too small. Please provide a new guess: "); - return request.CreateResponse(higherGuess); + return request.CreateResponse(higherGuess); } } diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs index a3e81914b7..bfccebed54 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs @@ -20,12 +20,10 @@ internal static class WorkflowHelper JudgeExecutor judgeExecutor = new(42); // Build the workflow by connecting executors in a loop - var workflow = new WorkflowBuilder(numberInputPort) + return new WorkflowBuilder(numberInputPort) .AddEdge(numberInputPort, judgeExecutor) .AddEdge(judgeExecutor, numberInputPort) .Build(); - - return workflow; } } @@ -60,7 +58,7 @@ internal sealed class SignalWithNumber internal sealed class JudgeExecutor() : ReflectingExecutor("Judge"), IMessageHandler { private readonly int _targetNumber; - private int _tries = 0; + private int _tries; private const string StateKey = "JudgeExecutorState"; /// @@ -94,17 +92,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor("Judge /// Checkpoint the current state of the executor. /// This must be overridden to save any state that is needed to resume the executor. /// - protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) - { - return context.QueueStateUpdateAsync(StateKey, this._tries); - } + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) => + context.QueueStateUpdateAsync(StateKey, this._tries); /// /// Restore the state of the executor from a checkpoint. /// This must be overridden to restore any state that was saved during checkpointing. /// - protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) - { + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) => this._tries = await context.ReadStateAsync(StateKey).ConfigureAwait(false); - } } diff --git a/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs index b6e6cfcc00..9e4bd69b62 100644 --- a/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs @@ -56,10 +56,10 @@ public static class Program var aggregationExecutor = new ConcurrentAggregationExecutor(); // Build the workflow by adding executors and connecting them - WorkflowBuilder builder = new(startExecutor); - builder.AddFanOutEdge(startExecutor, targets: [physicist, chemist]); - builder.AddFanInEdge(aggregationExecutor, sources: [physicist, chemist]); - var workflow = builder.Build(); + var workflow = new WorkflowBuilder(startExecutor) + .AddFanOutEdge(startExecutor, targets: [physicist, chemist]) + .AddFanInEdge(aggregationExecutor, sources: [physicist, chemist]) + .Build(); // Execute the workflow in streaming mode StreamingRun run = await InProcessExecution.StreamAsync(workflow, "What is temperature?"); diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs index 045228f68e..7b09b9d4d9 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs @@ -53,11 +53,11 @@ public static class Program var handleSpamExecutor = new HandleSpamExecutor(); // Build the workflow by adding executors and connecting them - WorkflowBuilder builder = new(spamDetectionExecutor); - builder.AddEdge(spamDetectionExecutor, emailAssistantExecutor, condition: GetCondition(expectedResult: false)); - builder.AddEdge(emailAssistantExecutor, sendEmailExecutor); - builder.AddEdge(spamDetectionExecutor, handleSpamExecutor, condition: GetCondition(expectedResult: true)); - var workflow = builder.Build(); + var workflow = new WorkflowBuilder(spamDetectionExecutor) + .AddEdge(spamDetectionExecutor, emailAssistantExecutor, condition: GetCondition(expectedResult: false)) + .AddEdge(emailAssistantExecutor, sendEmailExecutor) + .AddEdge(spamDetectionExecutor, handleSpamExecutor, condition: GetCondition(expectedResult: true)) + .Build(); // Read a email from a text file string email = Resources.Read("spam.txt"); @@ -79,53 +79,34 @@ public static class Program /// /// The expected spam detection result /// A function that evaluates whether a message meets the expected result - private static Func GetCondition(bool expectedResult) - { - return detectionResult => - { - return detectionResult is DetectionResult result && result.IsSpam == expectedResult; - }; - } + private static Func GetCondition(bool expectedResult) => + detectionResult => detectionResult is DetectionResult result && result.IsSpam == expectedResult; /// /// Creates a spam detection agent. /// /// A ChatClientAgent configured for spam detection - private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) - { - string instructions = "You are a spam detection assistant that identifies spam emails."; - var agentOptions = new ChatClientAgentOptions(instructions: instructions) + private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) => + new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult))) } - }; - - return new ChatClientAgent(chatClient, agentOptions); - } + }); /// /// Creates an email assistant agent. /// /// A ChatClientAgent configured for email assistance - private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) - { - string instructions = "You are an email assistant that helps users draft responses to emails with professionalism."; - var agentOptions = new ChatClientAgentOptions(instructions: instructions) + private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) => + new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))) } - }; - - return new ChatClientAgent(chatClient, agentOptions); - } + }); } /// @@ -188,7 +169,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope); + await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope); // Invoke the agent var response = await this._spamDetectionAgent.RunAsync(message); @@ -252,10 +233,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor /// Simulate the sending of an email. /// - public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) - { + public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) => await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}")); - } } /// diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs index c8596d4c61..316ae3330d 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs @@ -94,53 +94,33 @@ public static class Program /// /// The expected spam detection decision /// A function that evaluates whether a message meets the expected result - private static Func GetCondition(SpamDecision expectedDecision) - { - return detectionResult => - { - return detectionResult is DetectionResult result && result.spamDecision == expectedDecision; - }; - } + private static Func GetCondition(SpamDecision expectedDecision) => detectionResult => detectionResult is DetectionResult result && result.spamDecision == expectedDecision; /// /// Creates a spam detection agent. /// /// A ChatClientAgent configured for spam detection - private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) - { - string instructions = "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments."; - var agentOptions = new ChatClientAgentOptions(instructions: instructions) + private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) => + new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult))) } - }; - - return new ChatClientAgent(chatClient, agentOptions); - } + }); /// /// Creates an email assistant agent. /// /// A ChatClientAgent configured for email assistance - private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) - { - string instructions = "You are an email assistant that helps users draft responses to emails with professionalism."; - var agentOptions = new ChatClientAgentOptions(instructions: instructions) + private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) => + new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))) } - }; - - return new ChatClientAgent(chatClient, agentOptions); - } + }); } /// @@ -213,7 +193,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope); + await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope); // Invoke the agent var response = await this._spamDetectionAgent.RunAsync(message); @@ -276,10 +256,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor /// Simulate the sending of an email. /// - public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) - { + public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) => await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}")); - } } /// diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs index 67d90a0705..374eb85472 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs @@ -78,7 +78,7 @@ public static class Program .AddEdge( emailAnalysisExecutor, databaseAccessExecutor, - condition: analysisResult => analysisResult is not null && analysisResult.EmailLength <= LongEmailThreshold) + condition: analysisResult => analysisResult?.EmailLength <= LongEmailThreshold) // Save the analysis result to the database with summary .AddEdge(emailSummaryExecutor, databaseAccessExecutor); var workflow = builder.Build(); @@ -141,61 +141,40 @@ public static class Program /// Create an email analysis agent. /// /// A ChatClientAgent configured for email analysis - private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) - { - string instructions = "You are a spam detection assistant that identifies spam emails."; - var agentOptions = new ChatClientAgentOptions(instructions: instructions) + private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) => + new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(AnalysisResult)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(AnalysisResult))) } - }; - - return new ChatClientAgent(chatClient, agentOptions); - } + }); /// /// Creates an email assistant agent. /// /// A ChatClientAgent configured for email assistance - private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) - { - string instructions = "You are an email assistant that helps users draft responses to emails with professionalism."; - var agentOptions = new ChatClientAgentOptions(instructions: instructions) + private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) => + new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))) } - }; - - return new ChatClientAgent(chatClient, agentOptions); - } + }); /// /// Creates an agent that summarizes emails. /// /// A ChatClientAgent configured for email summarization - private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) - { - string instructions = "You are an assistant that helps users summarize emails."; - var agentOptions = new ChatClientAgentOptions(instructions: instructions) + private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) => + new(chatClient, new ChatClientAgentOptions(instructions: "You are an assistant that helps users summarize emails.") { ChatOptions = new() { - ResponseFormat = ChatResponseFormatJson.ForJsonSchema( - schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailSummary)) - ) + ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailSummary))) } - }; - - return new ChatClientAgent(chatClient, agentOptions); - } + }); } internal static class EmailStateConstants @@ -271,7 +250,7 @@ internal sealed class EmailAnalysisExecutor : ReflectingExecutor(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope); + await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope); // Invoke the agent var response = await this._emailAnalysisAgent.RunAsync(message); @@ -335,10 +314,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor /// Simulate the sending of an email. /// - public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) - { + public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) => await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}")); - } } /// @@ -437,7 +414,7 @@ internal sealed class DatabaseAccessExecutor() : ReflectingExecutor(message.EmailId, scopeName: EmailStateConstants.EmailStateScope); + await context.ReadStateAsync(message.EmailId, scopeName: EmailStateConstants.EmailStateScope); await Task.Delay(100); // Simulate database access delay // 2. Save the analysis result diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs index 858ddf7157..6f06880506 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs @@ -99,8 +99,8 @@ internal sealed class Program { Configuration = this.Configuration }; - Workflow workflow = DeclarativeWorkflowBuilder.Build(this.WorkflowFile, options); - return workflow; + + return DeclarativeWorkflowBuilder.Build(this.WorkflowFile, options); } private const string DefaultWorkflow = "HelloWorld.yaml"; @@ -164,7 +164,7 @@ internal sealed class Program Debug.WriteLine($"REQUEST #{requestInfo.Request.RequestId}"); if (response is not null) { - ExternalResponse requestResponse = requestInfo.Request.CreateResponse(response); + ExternalResponse requestResponse = requestInfo.Request.CreateResponse(response); await run.Run.SendResponseAsync(requestResponse).ConfigureAwait(false); response = null; } @@ -257,7 +257,7 @@ internal sealed class Program private static InputResponse HandleExternalRequest(ExternalRequest request) { InputRequest? message = request.Data.As(); - string? userInput = null; + string? userInput; do { Console.ForegroundColor = ConsoleColor.DarkGreen; diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs index 98bc65e4ad..7e56f6b7d5 100644 --- a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs @@ -52,18 +52,17 @@ public static class Program { if (request.DataIs()) { - var signal = request.DataAs(); - switch (signal) + switch (request.DataAs()) { case NumberSignal.Init: int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: "); - return request.CreateResponse(initialGuess); + return request.CreateResponse(initialGuess); case NumberSignal.Above: int lowerGuess = ReadIntegerFromConsole("You previously guessed too large. Please provide a new guess: "); - return request.CreateResponse(lowerGuess); + return request.CreateResponse(lowerGuess); case NumberSignal.Below: int higherGuess = ReadIntegerFromConsole("You previously guessed too small. Please provide a new guess: "); - return request.CreateResponse(higherGuess); + return request.CreateResponse(higherGuess); } } diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs index 7e11a4b39d..7f87393545 100644 --- a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs +++ b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs @@ -19,12 +19,10 @@ internal static class WorkflowHelper JudgeExecutor judgeExecutor = new(42); // Build the workflow by connecting executors in a loop - var workflow = new WorkflowBuilder(numberInputPort) + return new WorkflowBuilder(numberInputPort) .AddEdge(numberInputPort, judgeExecutor) .AddEdge(judgeExecutor, numberInputPort) .Build(); - - return workflow; } } @@ -44,7 +42,7 @@ internal enum NumberSignal internal sealed class JudgeExecutor() : ReflectingExecutor("Judge"), IMessageHandler { private readonly int _targetNumber; - private int _tries = 0; + private int _tries; /// /// Initializes a new instance of the class. diff --git a/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs b/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs index 97a21b8268..4f03d501ed 100644 --- a/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs @@ -108,7 +108,7 @@ internal sealed class GuessNumberExecutor : ReflectingExecutor, IMessageHandler { private readonly int _targetNumber; - private int _tries = 0; + private int _tries; /// /// Initializes a new instance of the class. diff --git a/dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs b/dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs index eee080c744..bd6dea3cf8 100644 --- a/dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs @@ -30,10 +30,10 @@ public static class Program var aggregate = new AggregationExecutor(); // Build the workflow by connecting executors sequentially - WorkflowBuilder builder = new(fileRead); - builder.AddFanOutEdge(fileRead, targets: [wordCount, paragraphCount]); - builder.AddFanInEdge(aggregate, sources: [wordCount, paragraphCount]); - var workflow = builder.Build(); + var workflow = new WorkflowBuilder(fileRead) + .AddFanOutEdge(fileRead, targets: [wordCount, paragraphCount]) + .AddFanInEdge(aggregate, sources: [wordCount, paragraphCount]) + .Build(); // Execute the workflow with input data Run run = await InProcessExecution.RunAsync(workflow, "Lorem_Ipsum.txt"); @@ -63,7 +63,7 @@ internal sealed class FileReadExecutor() : ReflectingExecutor( string fileContent = Resources.Read(message); // Store file content in a shared state for access by other executors string fileID = Guid.NewGuid().ToString(); - await context.QueueStateUpdateAsync(fileID, fileContent, scopeName: FileContentStateConstants.FileContentStateScope); + await context.QueueStateUpdateAsync(fileID, fileContent, scopeName: FileContentStateConstants.FileContentStateScope); return fileID; } diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs b/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs index 85fd569605..90d743c813 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Linq; using System.Threading.Tasks; using Microsoft.Agents.Workflows; using Microsoft.Agents.Workflows.Reflection; @@ -54,13 +55,8 @@ internal sealed class UppercaseExecutor() : ReflectingExecutorThe input text to convert /// Workflow context for accessing workflow services and adding events /// The input text converted to uppercase - public async ValueTask HandleAsync(string message, IWorkflowContext context) - { - string result = message.ToUpperInvariant(); - - // The return value will be sent as a message along an edge to subsequent executors - return result; - } + public async ValueTask HandleAsync(string message, IWorkflowContext context) => + message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors } /// @@ -76,9 +72,7 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutorThe input text reversed public async ValueTask HandleAsync(string message, IWorkflowContext context) { - char[] charArray = message.ToCharArray(); - System.Array.Reverse(charArray); - string result = new(charArray); + string result = string.Concat(message.Reverse()); // Signal that the workflow is complete await context.AddEventAsync(new WorkflowCompletedEvent(result)).ConfigureAwait(false); diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs b/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs index ce0db4a7fd..0b3f7c3712 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Linq; using System.Threading.Tasks; using Microsoft.Agents.Workflows; using Microsoft.Agents.Workflows.Reflection; @@ -53,13 +54,8 @@ internal sealed class UppercaseExecutor() : ReflectingExecutorThe input text to convert /// Workflow context for accessing workflow services and adding events /// The input text converted to uppercase - public async ValueTask HandleAsync(string message, IWorkflowContext context) - { - string result = message.ToUpperInvariant(); - - // The return value will be sent as a message along an edge to subsequent executors - return result; - } + public async ValueTask HandleAsync(string message, IWorkflowContext context) => + message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors } /// @@ -75,9 +71,7 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutorThe input text reversed public async ValueTask HandleAsync(string message, IWorkflowContext context) { - char[] charArray = message.ToCharArray(); - System.Array.Reverse(charArray); - string result = new(charArray); + string result = string.Concat(message.Reverse()); // Signal that the workflow is complete await context.AddEventAsync(new WorkflowCompletedEvent(result)).ConfigureAwait(false); diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs b/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs index 0456037eaf..4be46288f2 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs @@ -40,13 +40,14 @@ public static class Program AIAgent englishAgent = GetTranslationAgent("English", chatClient); // Build the workflow by adding executors and connecting them - WorkflowBuilder builder = new(frenchAgent); - builder.AddEdge(frenchAgent, spanishAgent); - builder.AddEdge(spanishAgent, englishAgent); - var workflow = builder.Build(); + var workflow = new WorkflowBuilder(frenchAgent) + .AddEdge(frenchAgent, spanishAgent) + .AddEdge(spanishAgent, englishAgent) + .Build(); // Execute the workflow StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!")); + // Must send the turn token to trigger the agents. // The agents are wrapped as executors. When they receive messages, // they will cache the messages and only start processing when they receive a TurnToken. @@ -66,9 +67,6 @@ public static class Program /// The target language for translation /// The chat client to use for the agent /// A ChatClientAgent configured for the specified language - private static ChatClientAgent GetTranslationAgent(string targetLanguage, IChatClient chatClient) - { - string instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}."; - return new ChatClientAgent(chatClient, instructions); - } + private static ChatClientAgent GetTranslationAgent(string targetLanguage, IChatClient chatClient) => + new(chatClient, $"You are a translation assistant that translates the provided text to {targetLanguage}."); } diff --git a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step01_Basics/Program.cs index 3d0dac58ba..45ea4e6a10 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step01_Basics/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step01_Basics/Program.cs @@ -14,10 +14,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -47,7 +47,7 @@ async Task SKAgent() await azureAgentClient.Administration.DeleteAgentAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step02_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step02_ToolCall/Program.cs index 9caaa2dd2b..14acfac819 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step02_ToolCall/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step02_ToolCall/Program.cs @@ -21,10 +21,10 @@ Console.WriteLine($"User Input: {userInput}"); static string GetWeather([Description("The location to get the weather for.")] string location) => $"The weather in {location} is cloudy with a high of 15°C."; -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -59,7 +59,7 @@ async Task SKAgent() await azureAgentClient.Administration.DeleteAgentAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step03_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step03_DependencyInjection/Program.cs index 02d8250fa7..b3526c9742 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step03_DependencyInjection/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step03_DependencyInjection/Program.cs @@ -16,10 +16,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -61,7 +61,7 @@ async Task SKAgent() await agent.Client.Administration.DeleteAgentAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); @@ -71,12 +71,10 @@ async Task AFAgent() { var azureAgentClient = sp.GetRequiredService(); - var aiAgent = azureAgentClient.CreateAIAgent( + return azureAgentClient.CreateAIAgent( deploymentName, name: "GenerateStory", instructions: "You are good at telling jokes."); - - return aiAgent; }); await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); diff --git a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step04_CodeInterpreter/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step04_CodeInterpreter/Program.cs index 436f6d2d46..ddc93f0021 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step04_CodeInterpreter/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureAIFoundry/Step04_CodeInterpreter/Program.cs @@ -17,10 +17,10 @@ var userInput = "Create a python code file using the code interpreter tool with Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -65,7 +65,7 @@ async Task SKAgent() await azureAgentClient.Administration.DeleteAgentAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/Program.cs index 5801959df3..51509f3294 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/Program.cs @@ -15,10 +15,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -46,7 +46,7 @@ async Task SKAgent() } } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/Program.cs index 7900e474ed..22415fb51c 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/Program.cs @@ -42,10 +42,10 @@ var userInput = Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -111,7 +111,7 @@ async Task SKAgent() } } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/Program.cs index b6fa282185..ff2dd3d05a 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/Program.cs @@ -21,10 +21,10 @@ Console.WriteLine($"User Input: {userInput}"); static string GetWeather([Description("The location to get the weather for.")] string location) => $"The weather in {location} is cloudy with a high of 15°C."; -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()) .GetOpenAIResponseClient(deploymentName)); @@ -43,7 +43,7 @@ async Task SKAgent() } } -async Task AFAgent() +async Task AFAgentAsync() { var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()) .GetOpenAIResponseClient(deploymentName) diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/Program.cs index f1a1ba9c93..acf8653fab 100644 --- a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/Program.cs @@ -16,10 +16,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -39,7 +39,7 @@ async Task SKAgent() Console.WriteLine(result.Message); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs index ab16e47e94..bd05155c15 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs @@ -12,10 +12,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -44,7 +44,7 @@ async Task SKAgent() } } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAI/Step02_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAI/Step02_ToolCall/Program.cs index 525e3d1ad4..e70a546585 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAI/Step02_ToolCall/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAI/Step02_ToolCall/Program.cs @@ -17,10 +17,10 @@ Console.WriteLine($"User Input: {userInput}"); static string GetWeather([Description("The location to get the weather for.")] string location) => $"The weather in {location} is cloudy with a high of 15°C."; -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey); @@ -40,7 +40,7 @@ async Task SKAgent() Console.WriteLine(result.Message); } -async Task AFAgent() +async Task AFAgentAsync() { var agent = new OpenAIClient(apiKey).GetChatClient(modelId).CreateAIAgent( instructions: "You are a helpful assistant", diff --git a/dotnet/samples/SemanticKernelMigration/OpenAI/Step03_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAI/Step03_DependencyInjection/Program.cs index b778dee67e..e659443f6b 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAI/Step03_DependencyInjection/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAI/Step03_DependencyInjection/Program.cs @@ -13,10 +13,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -36,7 +36,7 @@ async Task SKAgent() Console.WriteLine(result.Message); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs index 4d4898b0dc..bec48cee6a 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs @@ -3,7 +3,6 @@ #pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. using Microsoft.Extensions.AI.Agents; -using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Agents.OpenAI; using Microsoft.SemanticKernel.Connectors.OpenAI; using OpenAI; @@ -15,15 +14,13 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); - var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey); - var assistantsClient = new AssistantClient(apiKey); // Define the assistant @@ -53,7 +50,7 @@ async Task SKAgent() await assistantsClient.DeleteAssistantAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step02_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step02_ToolCall/Program.cs index 68203fb178..581e24c6de 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step02_ToolCall/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step02_ToolCall/Program.cs @@ -22,10 +22,10 @@ static string GetWeather([Description("The location to get the weather for.")] s Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -67,7 +67,7 @@ async Task SKAgent() await assistantsClient.DeleteAssistantAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step03_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step03_DependencyInjection/Program.cs index 2ffac58a8e..ef9d75da06 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step03_DependencyInjection/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step03_DependencyInjection/Program.cs @@ -16,10 +16,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -60,7 +60,7 @@ async Task SKAgent() await assistantsClient.DeleteAssistantAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); @@ -70,9 +70,7 @@ async Task AFAgent() { var assistantClient = sp.GetRequiredService(); - var agent = assistantClient.CreateAIAgent(modelId, name: "Joker", instructions: "You are good at telling jokes."); - - return agent; + return assistantClient.CreateAIAgent(modelId, name: "Joker", instructions: "You are good at telling jokes."); }); await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step04_CodeInterpreter/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step04_CodeInterpreter/Program.cs index 62de620c5e..d59efaa269 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step04_CodeInterpreter/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step04_CodeInterpreter/Program.cs @@ -19,15 +19,13 @@ var assistantsClient = new AssistantClient(apiKey); Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); - var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey); - // Define the assistant Assistant assistant = await assistantsClient.CreateAssistantAsync(modelId, enableCodeInterpreter: true); @@ -71,7 +69,7 @@ async Task SKAgent() await assistantsClient.DeleteAssistantAsync(agent.Id); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/Program.cs index dd2147c67c..8f821aa9b3 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/Program.cs @@ -13,10 +13,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -43,7 +43,7 @@ async Task SKAgent() } } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs index 13267811e0..ade717184f 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs @@ -40,10 +40,10 @@ var userInput = Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -108,7 +108,7 @@ async Task SKAgent() } } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/Program.cs index 2cfb51bdf2..81634b2ed2 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/Program.cs @@ -19,10 +19,10 @@ Console.WriteLine($"User Input: {userInput}"); static string GetWeather([Description("The location to get the weather for.")] string location) => $"The weather in {location} is cloudy with a high of 15°C."; -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey); @@ -42,7 +42,7 @@ async Task SKAgent() } } -async Task AFAgent() +async Task AFAgentAsync() { var agent = new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId).CreateAIAgent( instructions: "You are a helpful assistant", diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/Program.cs index be629d8e7e..3882231b2e 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/Program.cs @@ -14,10 +14,10 @@ var userInput = "Tell me a joke about a pirate."; Console.WriteLine($"User Input: {userInput}"); -await SKAgent(); -await AFAgent(); +await SKAgentAsync(); +await AFAgentAsync(); -async Task SKAgent() +async Task SKAgentAsync() { Console.WriteLine("\n=== SK Agent ===\n"); @@ -36,7 +36,7 @@ async Task SKAgent() Console.WriteLine(result.Message); } -async Task AFAgent() +async Task AFAgentAsync() { Console.WriteLine("\n=== AF Agent ===\n"); diff --git a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs index fc8eae8910..3c767e692c 100644 --- a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs +++ b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs @@ -4,7 +4,7 @@ namespace System.Diagnostics.CodeAnalysis; /// /// Indicates that certain members on a specified are accessed dynamically, -/// for example through . +/// for example through . /// /// /// This allows tools to understand which members are being accessed during the execution diff --git a/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs b/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs index 3464586054..d4f7e0d120 100644 --- a/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs +++ b/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs @@ -4,7 +4,7 @@ namespace System.Diagnostics.CodeAnalysis; /// /// Indicates that the specified method requires dynamic access to code that is not referenced -/// statically, for example through . +/// statically, for example through . /// /// /// This allows tools to understand which methods are unsafe to call when removing unreferenced diff --git a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs index 009c327e0e..66062a686e 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs @@ -83,12 +83,12 @@ public partial class ConcurrentOrchestration : OrchestratingAgent tasks.Add(Task.Run(async () => { AIAgent agent = this.Agents[localI]; - this.LogOrchestrationSubagentRunning(context, agent); + LogOrchestrationSubagentRunning(context, agent); completed[localI] = await RunAsync(agent, context, input, options: null, cancellationToken).ConfigureAwait(false); - this.LogOrchestrationSubagentCompleted(context, agent); - await this.CheckpointAsync(input, completed, context, cancellationToken).ConfigureAwait(false); + LogOrchestrationSubagentCompleted(context, agent); + await CheckpointAsync(input, completed, context, cancellationToken).ConfigureAwait(false); }, cancellationToken)); } } @@ -103,8 +103,8 @@ public partial class ConcurrentOrchestration : OrchestratingAgent return await this.AggregationFunc(completed!, cancellationToken).ConfigureAwait(false); } - private Task CheckpointAsync(IReadOnlyCollection messages, AgentRunResponse?[] completed, OrchestratingAgentContext context, CancellationToken cancellationToken) => - context.Runtime is not null ? base.WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(messages, completed), OrchestrationJsonContext.Default.ConcurrentState), context, cancellationToken) : + private static Task CheckpointAsync(IReadOnlyCollection messages, AgentRunResponse?[] completed, OrchestratingAgentContext context, CancellationToken cancellationToken) => + context.Runtime is not null ? WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(messages, completed), OrchestrationJsonContext.Default.ConcurrentState), context, cancellationToken) : Task.CompletedTask; internal sealed record ConcurrentState(IReadOnlyCollection Messages, AgentRunResponse?[] Completed); diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs index 00fc80e444..7763995f8d 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs @@ -59,7 +59,7 @@ public abstract class GroupChatManager /// The chat history to filter. /// A cancellation token that can be used to cancel the operation. /// A containing the filtered result as a string. - protected internal abstract ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default); + protected internal abstract ValueTask> FilterResultsAsync(IReadOnlyCollection history, CancellationToken cancellationToken = default); /// /// Selects the next agent to participate in the group chat based on the provided chat history and team. @@ -68,7 +68,7 @@ public abstract class GroupChatManager /// The group of agents participating in the chat. /// A cancellation token that can be used to cancel the operation. /// A containing the identifier of the next agent as a string. - protected internal abstract ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default); + protected internal abstract ValueTask> SelectNextAgentAsync(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default); /// /// Determines whether user input should be requested based on the provided chat history. @@ -76,7 +76,7 @@ public abstract class GroupChatManager /// The chat history to consider. /// A cancellation token that can be used to cancel the operation. /// A indicating whether user input should be requested. - protected internal abstract ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default); + protected internal abstract ValueTask> ShouldRequestUserInputAsync(IReadOnlyCollection history, CancellationToken cancellationToken = default); /// /// Determines whether the group chat should be terminated based on the provided chat history and invocation count. @@ -84,7 +84,7 @@ public abstract class GroupChatManager /// The chat history to consider. /// A cancellation token that can be used to cancel the operation. /// A indicating whether the chat should be terminated. - protected internal virtual ValueTask> ShouldTerminate(IReadOnlyCollection history, CancellationToken cancellationToken = default) + protected internal virtual ValueTask> ShouldTerminateAsync(IReadOnlyCollection history, CancellationToken cancellationToken = default) { bool resultValue = false; string reason = "Maximum number of invocations has not been reached."; diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs index 5afd420a27..282d4fb760 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs @@ -76,54 +76,51 @@ public sealed partial class GroupChatOrchestration : OrchestratingAgent // First, check if we should request user input. if (interactiveCallback is not null) { - var userInputResult = await this._manager.ShouldRequestUserInput(allMessages, cancellationToken).ConfigureAwait(false); - if (userInputResult.Value) + var userInputResult = await this._manager.ShouldRequestUserInputAsync(allMessages, cancellationToken).ConfigureAwait(false); + if (userInputResult.Value && interactiveCallback is not null) { - if (interactiveCallback is not null) + ChatMessage userMessage = await interactiveCallback().ConfigureAwait(false); + allMessages.Add(userMessage); + + // Broadcast the user input + if (this.ResponseCallback is not null) { - ChatMessage userMessage = await interactiveCallback().ConfigureAwait(false); - allMessages.Add(userMessage); - - // Broadcast the user input - if (this.ResponseCallback is not null) - { - await this.ResponseCallback([userMessage]).ConfigureAwait(false); - } - - await this.CheckpointAsync(allMessages, originalMessageCount, context, cancellationToken).ConfigureAwait(false); - continue; + await this.ResponseCallback([userMessage]).ConfigureAwait(false); } + + await CheckpointAsync(allMessages, originalMessageCount, context, cancellationToken).ConfigureAwait(false); + continue; } } // Check if we should terminate the conversation - var terminateResult = await this._manager.ShouldTerminate(allMessages, cancellationToken).ConfigureAwait(false); + var terminateResult = await this._manager.ShouldTerminateAsync(allMessages, cancellationToken).ConfigureAwait(false); if (terminateResult.Value) { // Filter and return final results - var filterResult = await this._manager.FilterResults(allMessages, cancellationToken).ConfigureAwait(false); + var filterResult = await this._manager.FilterResultsAsync(allMessages, cancellationToken).ConfigureAwait(false); return new AgentRunResponse([new ChatMessage(ChatRole.Assistant, filterResult.Value) { AuthorName = this.DisplayName }]); } // Select the next agent to speak - var nextAgentResult = await this._manager.SelectNextAgent(allMessages, team, cancellationToken).ConfigureAwait(false); + var nextAgentResult = await this._manager.SelectNextAgentAsync(allMessages, team, cancellationToken).ConfigureAwait(false); AIAgent nextAgent = this.FindAgentByName(nextAgentResult.Value) ?? throw new InvalidOperationException($"AIAgent '{nextAgentResult.Value}' not found in the orchestration."); // Run the selected agent with all messages. - this.LogOrchestrationSubagentRunning(context, nextAgent); + LogOrchestrationSubagentRunning(context, nextAgent); AgentRunResponse response = await RunAsync(nextAgent, context, allMessages, options: null, cancellationToken).ConfigureAwait(false); allMessages.AddRange(response.Messages); // Add the agent's response to the conversation. - this.LogOrchestrationSubagentCompleted(context, nextAgent); + LogOrchestrationSubagentCompleted(context, nextAgent); - await this.CheckpointAsync(allMessages, originalMessageCount, context, cancellationToken).ConfigureAwait(false); + await CheckpointAsync(allMessages, originalMessageCount, context, cancellationToken).ConfigureAwait(false); } } private AIAgent? FindAgentByName(string name) => this.Agents.FirstOrDefault(a => a.DisplayName == name); - private Task CheckpointAsync(List allMessages, int originalMessageCount, OrchestratingAgentContext context, CancellationToken cancellationToken) => - context.Runtime is not null ? base.WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(allMessages, originalMessageCount), OrchestrationJsonContext.Default.GroupChatState), context, cancellationToken) : + private static Task CheckpointAsync(List allMessages, int originalMessageCount, OrchestratingAgentContext context, CancellationToken cancellationToken) => + context.Runtime is not null ? WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(allMessages, originalMessageCount), OrchestrationJsonContext.Default.GroupChatState), context, cancellationToken) : Task.CompletedTask; internal sealed record GroupChatState(List AllMessages, int OriginalMessageCount); diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs index d01faf3dd8..dd7d00e3da 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs @@ -19,7 +19,7 @@ public class RoundRobinGroupChatManager : GroupChatManager private int _currentAgentIndex; /// - protected internal override ValueTask> FilterResults( + protected internal override ValueTask> FilterResultsAsync( IReadOnlyCollection history, CancellationToken cancellationToken = default) { GroupChatManagerResult result = new(history.LastOrDefault()?.Text ?? string.Empty) { Reason = "Default result filter provides the final chat message." }; @@ -27,7 +27,7 @@ public class RoundRobinGroupChatManager : GroupChatManager } /// - protected internal override ValueTask> SelectNextAgent( + protected internal override ValueTask> SelectNextAgentAsync( IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default) { string nextAgent = team.Skip(this._currentAgentIndex).First().Key; @@ -37,7 +37,7 @@ public class RoundRobinGroupChatManager : GroupChatManager } /// - protected internal override ValueTask> ShouldRequestUserInput( + protected internal override ValueTask> ShouldRequestUserInputAsync( IReadOnlyCollection history, CancellationToken cancellationToken = default) { GroupChatManagerResult result = new(false) { Reason = "The default round-robin group chat manager does not request user input." }; diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs index 23d29f9f69..9dfc48334d 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs @@ -85,13 +85,13 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent while (agent is not null) { - this.LogOrchestrationSubagentRunning(context, agent); + LogOrchestrationSubagentRunning(context, agent); if (!this._handoffs.Targets.TryGetValue(agent, out var handoffs) || handoffs.Count == 0) { // If no handoff is available, we can run the agent directly and return its response. response = await RunAsync(agent, context, allMessages, context.Options, cancellationToken).ConfigureAwait(false); - this.LogOrchestrationSubagentCompleted(context, agent); + LogOrchestrationSubagentCompleted(context, agent); allMessages.AddRange(response.Messages); agent = null; await CheckpointAsync().ConfigureAwait(false); @@ -100,7 +100,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent // Create the options for the next agent request, including handoff functions. HandoffContext handoffCtx = new(handoffs); - ChatClientAgentRunOptions? options = null; + ChatClientAgentRunOptions? options; List handoffTools = handoffCtx.CreateHandoffFunctions(this.InteractiveCallback is not null); if (context.Options is ChatClientAgentRunOptions contextOptions) { @@ -115,7 +115,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent // Invoke the next agent with all of the messages collected so far. response = await RunAsync(agent, context, allMessages, options, cancellationToken).ConfigureAwait(false); - this.LogOrchestrationSubagentCompleted(context, agent); + LogOrchestrationSubagentCompleted(context, agent); allMessages.AddRange(response.Messages); agent = handoffCtx.TargetedAgent; RemoveHandoffFunctionCalls(response, handoffTools); @@ -139,7 +139,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent return response; Task CheckpointAsync() => context.Runtime is not null ? - base.WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(agent?.Id, allMessages, originalMessageCount), OrchestrationJsonContext.Default.HandoffState), context, cancellationToken) : + WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(agent?.Id, allMessages, originalMessageCount), OrchestrationJsonContext.Default.HandoffState), context, cancellationToken) : Task.CompletedTask; } diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/Handoffs.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/Handoffs.cs index a40878149a..f49b655253 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/Handoffs.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/Handoffs.cs @@ -17,7 +17,7 @@ public sealed class Handoffs : IReadOnlyDictionary> { /// - /// Initializes a new instance of the class with no handoff relationships. + /// Initializes a new instance of the class with no handoff relationships. /// /// The first agent to be invoked (prior to any handoff). private Handoffs(AIAgent initialAgent) @@ -41,7 +41,7 @@ public sealed class Handoffs : /// Creates a new collection of handoffs that start with the specified agent. /// /// The initial agent. - /// The new instance. + /// The new instance. public static Handoffs StartWith(AIAgent initialAgent) => new(initialAgent); /// Creates a new from the described handoffs. @@ -54,7 +54,7 @@ public sealed class Handoffs : /// /// The source agent. /// The target agents to add as handoff targets for the source agent. - /// The updated instance. + /// The updated instance. /// The handoff reason for each target is derived from its description or name. public Handoffs Add(AIAgent source, AIAgent[] targets) { @@ -79,7 +79,7 @@ public sealed class Handoffs : /// The source agent. /// The target agent. /// The reason the should hand off to the . - /// The updated instance. + /// The updated instance. public Handoffs Add(AIAgent source, AIAgent target, string? handoffReason = null) { Throw.IfNull(source); @@ -127,7 +127,7 @@ public sealed class Handoffs : /// IEnumerator IEnumerable.GetEnumerator() => - ((IReadOnlyDictionary>)this).GetEnumerator(); + ((IReadOnlyDictionary>)this).GetEnumerator(); /// bool IReadOnlyDictionary>.TryGetValue(AIAgent key, out IEnumerable value) diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs index 18c70cab3a..29d03fe384 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs @@ -129,7 +129,7 @@ public abstract partial class OrchestratingAgent : AIAgent CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cancellationToken = cts.Token; - JsonElement? checkpoint = await this.ReadCheckpointAsync(context, cancellationToken).ConfigureAwait(false); + JsonElement? checkpoint = await ReadCheckpointAsync(context, cancellationToken).ConfigureAwait(false); Task completion = checkpoint is null ? this.RunCoreAsync(readonlyCollectionMessages, context, cancellationToken) : this.ResumeCoreAsync(checkpoint.Value, readonlyCollectionMessages, context, cancellationToken); @@ -207,7 +207,7 @@ public abstract partial class OrchestratingAgent : AIAgent /// The context for the orchestrating operation. /// A cancellation token that can be used to cancel the operation. /// A Task that completes when the asynchronous operation quiesces. - protected async Task WriteCheckpointAsync(JsonElement state, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected static async Task WriteCheckpointAsync(JsonElement state, OrchestratingAgentContext context, CancellationToken cancellationToken) { _ = Throw.IfNull(context); @@ -236,7 +236,7 @@ public abstract partial class OrchestratingAgent : AIAgent /// The context for the orchestrating operation. /// A cancellation token that can be used to cancel the operation. /// The loaded state, or null if it doesn't exist. - protected async ValueTask ReadCheckpointAsync(OrchestratingAgentContext context, CancellationToken cancellationToken) + protected static async ValueTask ReadCheckpointAsync(OrchestratingAgentContext context, CancellationToken cancellationToken) { _ = Throw.IfNull(context); @@ -276,10 +276,10 @@ public abstract partial class OrchestratingAgent : AIAgent [LoggerMessage(Level = LogLevel.Trace, Message = "{Orchestration} completed agent '{Agent}' ('{Id}')")] private static partial void LogOrchestrationSubagentCompleted(ILogger logger, string orchestration, string id, string agent); - private protected void LogOrchestrationSubagentRunning(OrchestratingAgentContext context, AIAgent agent) => + private protected static void LogOrchestrationSubagentRunning(OrchestratingAgentContext context, AIAgent agent) => LogOrchestrationSubagentRunning(context.Logger, context.ToString(), context.Id, agent.DisplayName); - private protected void LogOrchestrationSubagentCompleted(OrchestratingAgentContext context, AIAgent agent) => + private protected static void LogOrchestrationSubagentCompleted(OrchestratingAgentContext context, AIAgent agent) => LogOrchestrationSubagentCompleted(context.Logger, context.ToString(), context.Id, agent.DisplayName); private static async Task LogCompletionAsync(ILogger logger, OrchestratingAgentContext context, Task completion) diff --git a/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs index 14a4bb1cdd..851ab12aca 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs @@ -48,20 +48,20 @@ public sealed partial class SequentialOrchestration : OrchestratingAgent AgentRunResponse? response = null; for (; i < this.Agents.Count; i++) { - this.LogOrchestrationSubagentRunning(context, this.Agents[i]); + LogOrchestrationSubagentRunning(context, this.Agents[i]); response = await RunAsync(this.Agents[i], context, input, options: null, cancellationToken).ConfigureAwait(false); input = response.Messages as IReadOnlyCollection ?? [.. response.Messages]; - await this.CheckpointAsync(i + 1, input, context, cancellationToken).ConfigureAwait(false); + await CheckpointAsync(i + 1, input, context, cancellationToken).ConfigureAwait(false); } Debug.Assert(response is not null, "Response should not be null after processing a positive number of agents."); return response!; } - private Task CheckpointAsync(int index, IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => - context.Runtime is not null ? base.WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(index, messages), OrchestrationJsonContext.Default.SequentialState), context, cancellationToken) : + private static Task CheckpointAsync(int index, IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => + context.Runtime is not null ? WriteCheckpointAsync(JsonSerializer.SerializeToElement(new(index, messages), OrchestrationJsonContext.Default.SequentialState), context, cancellationToken) : Task.CompletedTask; internal sealed record SequentialState(int Index, IReadOnlyCollection Messages); diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/AzureAgentProvider.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/AzureAgentProvider.cs index f88b7fa239..5c8fd35192 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/AzureAgentProvider.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/AzureAgentProvider.cs @@ -90,12 +90,8 @@ public sealed class AzureAgentProvider(string projectEndpoint, TokenCredential p } /// - public override async Task GetAgentAsync(string agentId, CancellationToken cancellationToken = default) - { - AIAgent agent = await this.GetAgentsClient().GetAIAgentAsync(agentId, chatOptions: null, cancellationToken).ConfigureAwait(false); - - return agent; - } + public override async Task GetAgentAsync(string agentId, CancellationToken cancellationToken = default) => + await this.GetAgentsClient().GetAIAgentAsync(agentId, chatOptions: null, cancellationToken).ConfigureAwait(false); /// public override async Task GetMessageAsync(string conversationId, string messageId, CancellationToken cancellationToken = default) diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowBuilder.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowBuilder.cs index 2225921310..0affa26d4c 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowBuilder.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowBuilder.cs @@ -31,7 +31,7 @@ public static class DeclarativeWorkflowBuilder where TInput : notnull { using StreamReader yamlReader = File.OpenText(workflowFile); - return Build(yamlReader, options, inputTransform); + return Build(yamlReader, options, inputTransform); } /// diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/BotElementExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/BotElementExtensions.cs index bb409944f2..1ae9c48a02 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/BotElementExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/BotElementExtensions.cs @@ -8,14 +8,12 @@ internal static class BotElementExtensions { public static string? GetParentId(this BotElement element) => element.Parent?.GetId(); - public static string GetId(this BotElement element) - { - return element switch + public static string GetId(this BotElement element) => + element switch { DialogAction action => action.Id.Value, ConditionItem conditionItem => conditionItem.Id ?? throw new DeclarativeModelException($"Undefined identifier for {nameof(ConditionItem)} that is member of {conditionItem.GetParentId() ?? "(root)"}."), OnActivity activity => activity.Id.Value, _ => throw new DeclarativeModelException($"Unknown identify for element type: {element.GetType().Name}"), }; - } } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/ChatMessageExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/ChatMessageExtensions.cs index 34e7efdb80..66f7254d6f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/ChatMessageExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/ChatMessageExtensions.cs @@ -13,7 +13,7 @@ namespace Microsoft.Agents.Workflows.Declarative.Extensions; internal static class ChatMessageExtensions { public static RecordValue ToRecord(this ChatMessage message) => - RecordValue.NewRecordFromFields(message.GetMessageFields()); + FormulaValue.NewRecordFromFields(message.GetMessageFields()); public static TableValue ToTable(this IEnumerable messages) => FormulaValue.NewTable(s_messageRecordType, messages.Select(message => message.ToRecord())); diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/DataValueExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/DataValueExtensions.cs index 7fa82066e1..e0dab7116a 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/DataValueExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/DataValueExtensions.cs @@ -14,7 +14,7 @@ internal static class DataValueExtensions value switch { null => FormulaValue.NewBlank(), - BlankDataValue => BlankValue.NewBlank(), + BlankDataValue => FormulaValue.NewBlank(), BooleanDataValue boolValue => FormulaValue.New(boolValue.Value), NumberDataValue numberValue => FormulaValue.New(numberValue.Value), FloatDataValue floatValue => FormulaValue.New(floatValue.Value), diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/FormulaValueExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/FormulaValueExtensions.cs index f815486686..e9d393c1ff 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/FormulaValueExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/FormulaValueExtensions.cs @@ -71,8 +71,8 @@ internal static class FormulaValueExtensions DateTimeValue datetimeValue => DateTimeDataValue.Create(datetimeValue.GetConvertedValue(TimeZoneInfo.Utc)), TimeValue timeValue => TimeDataValue.Create(timeValue.Value), StringValue stringValue => StringDataValue.Create(stringValue.Value), - BlankValue blankValue => DataValue.Blank(), - VoidValue voidValue => DataValue.Blank(), + BlankValue => DataValue.Blank(), + VoidValue => DataValue.Blank(), RecordValue recordValue => recordValue.ToRecord(), TableValue tableValue => tableValue.ToTable(), _ => throw new DeclarativeModelException($"Unsupported variable type: {value.GetType().Name}"), @@ -141,10 +141,10 @@ internal static class FormulaValueExtensions }; public static TableDataValue ToTable(this TableValue value) => - TableDataValue.TableFromRecords(value.Rows.Select(row => row.Value.ToRecord()).ToImmutableArray()); + DataValue.TableFromRecords(value.Rows.Select(row => row.Value.ToRecord()).ToImmutableArray()); public static RecordDataValue ToRecord(this RecordValue value) => - RecordDataValue.RecordFromFields(value.OriginalFields.Select(field => field.GetKeyValuePair())); + DataValue.RecordFromFields(value.OriginalFields.Select(field => field.GetKeyValuePair())); private static RecordValue ToRecord(this IDictionary value) { @@ -229,7 +229,7 @@ internal static class FormulaValueExtensions GuidValue guidValue => JsonValue.Create(guidValue.Value), RecordValue recordValue => recordValue.ToJson(), TableValue tableValue => tableValue.ToJson(), - BlankValue blankValue => JsonValue.Create(string.Empty), + BlankValue => JsonValue.Create(string.Empty), _ => $"[{value.GetType().Name}]", }; diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/RecordDataTypeExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/RecordDataTypeExtensions.cs index 977a0217db..f915d150db 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/RecordDataTypeExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/RecordDataTypeExtensions.cs @@ -23,12 +23,12 @@ internal static class RecordDataTypeExtensions FormulaValue? parsedValue = property.Value.Type switch { - StringDataType => StringValue.New(propertyElement.GetString()), - NumberDataType => NumberValue.New(propertyElement.GetDecimal()), - BooleanDataType => BooleanValue.New(propertyElement.GetBoolean()), - DateTimeDataType => DateTimeValue.New(propertyElement.GetDateTime()), - DateDataType => DateValue.New(propertyElement.GetDateTime()), - TimeDataType => TimeValue.New(propertyElement.GetDateTimeOffset().TimeOfDay), + StringDataType => FormulaValue.New(propertyElement.GetString()), + NumberDataType => FormulaValue.New(propertyElement.GetDecimal()), + BooleanDataType => FormulaValue.New(propertyElement.GetBoolean()), + DateTimeDataType => FormulaValue.New(propertyElement.GetDateTime()), + DateDataType => FormulaValue.New(propertyElement.GetDateTime()), + TimeDataType => FormulaValue.New(propertyElement.GetDateTimeOffset().TimeOfDay), RecordDataType recordType => recordType.ParseRecord(propertyElement), TableDataType tableType => ParseTable(tableType, propertyElement), _ => throw new InvalidOperationException($"Unsupported data type '{property.Value.Type}' for property '{property.Key}'"), diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/StringExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/StringExtensions.cs index 83099fddee..7bd31b2505 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/StringExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/StringExtensions.cs @@ -5,19 +5,24 @@ using Microsoft.PowerFx.Types; namespace Microsoft.Agents.Workflows.Declarative.Extensions; -internal static class StringExtensions +internal static partial class StringExtensions { - private static readonly Regex s_regex = new(@"^```(?:\w*)\s*([\s\S]*?)\s*```$", RegexOptions.Compiled | RegexOptions.Multiline); +#if NET + [GeneratedRegex(@"^```(?:\w*)\s*([\s\S]*?)\s*```$", RegexOptions.Multiline)] + private static partial Regex TrimJsonDelimiterRegex(); +#else + private static Regex TrimJsonDelimiterRegex() => s_trimJsonDelimiterRegex; + private static readonly Regex s_trimJsonDelimiterRegex = new(@"^```(?:\w*)\s*([\s\S]*?)\s*```$", RegexOptions.Compiled | RegexOptions.Multiline); +#endif public static string TrimJsonDelimiter(this string value) { - Match match = s_regex.Match(value.Trim()); - if (match.Success) - { - return match.Groups[1].Value.Trim(); - } + value = value.Trim(); - return value.Trim(); + Match match = TrimJsonDelimiterRegex().Match(value); + return match.Success ? + match.Groups[1].Value.Trim() : + value; } public static FormulaValue ToFormula(this string? value) => diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/TemplateExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/TemplateExtensions.cs index bf13587a38..860d9e991d 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/TemplateExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Extensions/TemplateExtensions.cs @@ -4,21 +4,18 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Bot.ObjectModel; using Microsoft.PowerFx; -using Microsoft.PowerFx.Types; namespace Microsoft.Agents.Workflows.Declarative.Extensions; internal static class TemplateExtensions { - public static string Format(this RecalcEngine engine, IEnumerable template) - { - return string.Concat(template.Select(line => engine.Format(line))); - } + public static string Format(this RecalcEngine engine, IEnumerable template) => + string.Concat(template.Select(engine.Format)); - public static string Format(this RecalcEngine engine, TemplateLine? line) - { - return string.Concat(line?.Segments.Select(segment => engine.Format(segment)) ?? [string.Empty]); - } + public static string Format(this RecalcEngine engine, TemplateLine? line) => + line is not null ? + string.Concat(line.Segments.Select(engine.Format)) : + string.Empty; public static string Format(this RecalcEngine engine, TemplateSegment segment) { @@ -27,20 +24,16 @@ internal static class TemplateExtensions return textSegment.Value ?? string.Empty; } - if (segment is ExpressionSegment expressionSegment) + if (segment is ExpressionSegment { Expression: not null } expressionSegment) { - if (expressionSegment.Expression is not null) + if (expressionSegment.Expression.ExpressionText is not null) { - if (expressionSegment.Expression.ExpressionText is not null) - { - FormulaValue expressionValue = engine.Eval(expressionSegment.Expression.ExpressionText); - return expressionValue.Format(); - } - if (expressionSegment.Expression.VariableReference is not null) - { - FormulaValue expressionValue = engine.Eval(expressionSegment.Expression.VariableReference.ToString()); - return expressionValue.Format(); - } + return engine.Eval(expressionSegment.Expression.ExpressionText).Format(); + } + + if (expressionSegment.Expression.VariableReference is not null) + { + return engine.Eval(expressionSegment.Expression.VariableReference.ToString()).Format(); } } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowModel.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowModel.cs index b78c935020..d43099df77 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowModel.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowModel.cs @@ -23,7 +23,7 @@ internal sealed class DeclarativeWorkflowModel public int GetDepth(string? nodeId) { - if (nodeId == null) + if (nodeId is null) { return 0; } @@ -112,15 +112,7 @@ internal sealed class DeclarativeWorkflowModel workflowBuilder.AddEdge(GetExecutorIsh(link.Source), GetExecutorIsh(targetNode), link.Condition); } - ExecutorIsh GetExecutorIsh(ModelNode node) - { - if (node.Port is not null) - { - return node.Port; - } - - return node.Executor; - } + static ExecutorIsh GetExecutorIsh(ModelNode node) => node.Port ?? (ExecutorIsh)node.Executor; } private ModelNode DefineNode(Executor executor, ModelNode? parentNode = null, Action? completionHandler = null) @@ -148,7 +140,7 @@ internal sealed class DeclarativeWorkflowModel return null; } - while (itemId != null) + while (itemId is not null) { if (!this.Nodes.TryGetValue(itemId, out ModelNode? itemNode)) { @@ -180,7 +172,7 @@ internal sealed class DeclarativeWorkflowModel public List Children { get; } = []; - public int Depth => this.Parent?.Depth + 1 ?? 0; + public int Depth => (this.Parent?.Depth + 1) ?? 0; public Action? CompletionHandler => completionHandler; } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs index 61f00a5b50..5ab02699c5 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs @@ -137,7 +137,7 @@ internal sealed class WorkflowActionVisitor : DialogActionVisitor // Create conditional link for else action string stepId = ConditionGroupExecutor.Steps.Else(item); - this._workflowModel.AddLink(action.Id, stepId, (result) => action.IsElse(result)); + this._workflowModel.AddLink(action.Id, stepId, action.IsElse); } } @@ -355,170 +355,74 @@ internal sealed class WorkflowActionVisitor : DialogActionVisitor this.NotSupported(item); } - protected override void Visit(GetActivityMembers item) - { - this.NotSupported(item); - } + protected override void Visit(GetActivityMembers item) => this.NotSupported(item); - protected override void Visit(UpdateActivity item) - { - this.NotSupported(item); - } + protected override void Visit(UpdateActivity item) => this.NotSupported(item); - protected override void Visit(ActivateExternalTrigger item) - { - this.NotSupported(item); - } + protected override void Visit(ActivateExternalTrigger item) => this.NotSupported(item); - protected override void Visit(DisableTrigger item) - { - this.NotSupported(item); - } + protected override void Visit(DisableTrigger item) => this.NotSupported(item); - protected override void Visit(WaitForConnectorTrigger item) - { - this.NotSupported(item); - } + protected override void Visit(WaitForConnectorTrigger item) => this.NotSupported(item); - protected override void Visit(InvokeConnectorAction item) - { - this.NotSupported(item); - } + protected override void Visit(InvokeConnectorAction item) => this.NotSupported(item); - protected override void Visit(InvokeCustomModelAction item) - { - this.NotSupported(item); - } + protected override void Visit(InvokeCustomModelAction item) => this.NotSupported(item); - protected override void Visit(InvokeFlowAction item) - { - this.NotSupported(item); - } + protected override void Visit(InvokeFlowAction item) => this.NotSupported(item); - protected override void Visit(InvokeAIBuilderModelAction item) - { - this.NotSupported(item); - } + protected override void Visit(InvokeAIBuilderModelAction item) => this.NotSupported(item); - protected override void Visit(InvokeSkillAction item) - { - this.NotSupported(item); - } + protected override void Visit(InvokeSkillAction item) => this.NotSupported(item); - protected override void Visit(AdaptiveCardPrompt item) - { - this.NotSupported(item); - } + protected override void Visit(AdaptiveCardPrompt item) => this.NotSupported(item); protected override void Visit(CSATQuestion item) { this.NotSupported(item); } - protected override void Visit(OAuthInput item) - { - this.NotSupported(item); - } + protected override void Visit(OAuthInput item) => this.NotSupported(item); - protected override void Visit(BeginDialog item) - { - this.NotSupported(item); - } + protected override void Visit(BeginDialog item) => this.NotSupported(item); - protected override void Visit(UnknownDialogAction item) - { - this.NotSupported(item); - } + protected override void Visit(UnknownDialogAction item) => this.NotSupported(item); - protected override void Visit(EndDialog item) - { - this.NotSupported(item); - } + protected override void Visit(EndDialog item) => this.NotSupported(item); - protected override void Visit(RepeatDialog item) - { - this.NotSupported(item); - } + protected override void Visit(RepeatDialog item) => this.NotSupported(item); - protected override void Visit(ReplaceDialog item) - { - this.NotSupported(item); - } + protected override void Visit(ReplaceDialog item) => this.NotSupported(item); - protected override void Visit(CancelAllDialogs item) - { - this.NotSupported(item); - } + protected override void Visit(CancelAllDialogs item) => this.NotSupported(item); - protected override void Visit(CancelDialog item) - { - this.NotSupported(item); - } + protected override void Visit(CancelDialog item) => this.NotSupported(item); - protected override void Visit(EmitEvent item) - { - this.NotSupported(item); - } + protected override void Visit(EmitEvent item) => this.NotSupported(item); - protected override void Visit(GetConversationMembers item) - { - this.NotSupported(item); - } + protected override void Visit(GetConversationMembers item) => this.NotSupported(item); - protected override void Visit(HttpRequestAction item) - { - this.NotSupported(item); - } + protected override void Visit(HttpRequestAction item) => this.NotSupported(item); - protected override void Visit(RecognizeIntent item) - { - this.NotSupported(item); - } + protected override void Visit(RecognizeIntent item) => this.NotSupported(item); - protected override void Visit(TransferConversation item) - { - this.NotSupported(item); - } + protected override void Visit(TransferConversation item) => this.NotSupported(item); - protected override void Visit(TransferConversationV2 item) - { - this.NotSupported(item); - } + protected override void Visit(TransferConversationV2 item) => this.NotSupported(item); - protected override void Visit(SignOutUser item) - { - this.NotSupported(item); - } + protected override void Visit(SignOutUser item) => this.NotSupported(item); - protected override void Visit(LogCustomTelemetryEvent item) - { - this.NotSupported(item); - } + protected override void Visit(LogCustomTelemetryEvent item) => this.NotSupported(item); - protected override void Visit(DisconnectedNodeContainer item) - { - this.NotSupported(item); - } + protected override void Visit(DisconnectedNodeContainer item) => this.NotSupported(item); - protected override void Visit(CreateSearchQuery item) - { - this.NotSupported(item); - } + protected override void Visit(CreateSearchQuery item) => this.NotSupported(item); - protected override void Visit(SearchKnowledgeSources item) - { - this.NotSupported(item); - } + protected override void Visit(SearchKnowledgeSources item) => this.NotSupported(item); - protected override void Visit(SearchAndSummarizeWithCustomModel item) - { - this.NotSupported(item); - } + protected override void Visit(SearchAndSummarizeWithCustomModel item) => this.NotSupported(item); - protected override void Visit(SearchAndSummarizeContent item) - { - this.NotSupported(item); - } + protected override void Visit(SearchAndSummarizeContent item) => this.NotSupported(item); #endregion @@ -563,10 +467,8 @@ internal sealed class WorkflowActionVisitor : DialogActionVisitor this.HasUnsupportedActions = true; } - private void Trace(BotElement item) - { + private void Trace(BotElement item) => Debug.WriteLine($"> VISIT: {new string('\t', this._workflowModel.GetDepth(item.GetParentId()))}{FormatItem(item)} => {FormatParent(item)}"); - } private void Trace(DialogAction item) { @@ -575,6 +477,7 @@ internal sealed class WorkflowActionVisitor : DialogActionVisitor { parentId = Steps.Root(parentId); } + Debug.WriteLine($"> VISIT: {new string('\t', this._workflowModel.GetDepth(parentId))}{FormatItem(item)} => {FormatParent(item)}"); } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ClearAllVariablesExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ClearAllVariablesExecutor.cs index f0fee71ade..d80479186e 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ClearAllVariablesExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ClearAllVariablesExecutor.cs @@ -15,7 +15,7 @@ internal sealed class ClearAllVariablesExecutor(ClearAllVariables model, Workflo { protected override ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) { - EvaluationResult variablesResult = this.State.Evaluator.GetValue(this.Model.Variables); + EvaluationResult variablesResult = this.State.Evaluator.GetValue(this.Model.Variables); variablesResult.Value.Handle(new ScopeHandler(this.Id, this.State)); @@ -24,20 +24,16 @@ internal sealed class ClearAllVariablesExecutor(ClearAllVariables model, Workflo private sealed class ScopeHandler(string executorId, WorkflowFormulaState state) : IEnumVariablesToClearHandler { - public void HandleAllGlobalVariables() - { + public void HandleAllGlobalVariables() => this.ClearAll(VariableScopeNames.Global); - } public void HandleConversationHistory() { // Not supported.... } - public void HandleConversationScopedVariables() - { + public void HandleConversationScopedVariables() => this.ClearAll(WorkflowFormulaState.DefaultScopeName); - } public void HandleUnknownValue() { diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs index 01b88ec6d6..8321046a0d 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs @@ -47,9 +47,7 @@ internal sealed class ConditionGroupExecutor : DeclarativeActionExecutor ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) -#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { for (int index = 0; index < this.Model.Conditions.Length; ++index) { @@ -69,8 +67,6 @@ internal sealed class ConditionGroupExecutor : DeclarativeActionExecutor await context.RaiseCompletionEventAsync(this.Model).ConfigureAwait(false); - } } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs index 689c7d6e1f..fdecde8518 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs @@ -61,7 +61,7 @@ internal sealed class InvokeAzureAgentExecutor(InvokeAzureAgent model, WorkflowA await foreach (AgentRunResponseUpdate update in agentUpdates.ConfigureAwait(false)) { - await AssignConversationId(((ChatResponseUpdate?)update.RawRepresentation)?.ConversationId).ConfigureAwait(false); + await AssignConversationIdAsync(((ChatResponseUpdate?)update.RawRepresentation)?.ConversationId).ConfigureAwait(false); if (autoSend) { @@ -72,7 +72,7 @@ internal sealed class InvokeAzureAgentExecutor(InvokeAzureAgent model, WorkflowA } } - async ValueTask AssignConversationId(string? assignValue) + async ValueTask AssignConversationIdAsync(string? assignValue) { if (assignValue is not null && conversationId is null) { diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ParseValueExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ParseValueExecutor.cs index 26318a81bc..e7efc2071c 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ParseValueExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ParseValueExecutor.cs @@ -42,9 +42,9 @@ internal sealed class ParseValueExecutor(ParseValue model, WorkflowFormulaState parsedResult = this.Model.ValueType switch { - StringDataType => StringValue.New(stringValue.Value), - NumberDataType => NumberValue.New(stringValue.Value), - BooleanDataType => BooleanValue.New(stringValue.Value), + StringDataType => FormulaValue.New(stringValue.Value), + NumberDataType => FormulaValue.New(stringValue.Value), + BooleanDataType => FormulaValue.New(stringValue.Value), RecordDataType recordType => ParseRecord(recordType, stringValue.Value), _ => null }; @@ -63,11 +63,10 @@ internal sealed class ParseValueExecutor(ParseValue model, WorkflowFormulaState RecordValue ParseRecord(RecordDataType recordType, string rawText) { string jsonText = rawText.TrimJsonDelimiter(); - JsonDocument json = JsonDocument.Parse(jsonText); - JsonElement currentElement = json.RootElement; + using JsonDocument json = JsonDocument.Parse(jsonText); try { - return recordType.ParseRecord(currentElement); + return recordType.ParseRecord(json.RootElement); } catch (Exception exception) { diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ResetVariableExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ResetVariableExecutor.cs index e1ade47533..5bbb64e23e 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ResetVariableExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ResetVariableExecutor.cs @@ -15,7 +15,7 @@ internal sealed class ResetVariableExecutor(ResetVariable model, WorkflowFormula { protected override ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) { - PropertyPath variablePath = Throw.IfNull(this.Model.Variable, $"{nameof(this.Model)}.{nameof(model.Variable)}"); + Throw.IfNull(this.Model.Variable, $"{nameof(this.Model)}.{nameof(model.Variable)}"); this.State.Reset(this.Model.Variable); Debug.WriteLine( diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/SystemScope.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/SystemScope.cs index 33f4f4a5bf..d11588a9a0 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/SystemScope.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/SystemScope.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Frozen; -using System.Collections.Generic; using System.Globalization; using Microsoft.Agents.Workflows.Declarative.Extensions; using Microsoft.Bot.ObjectModel; @@ -31,22 +30,20 @@ internal static class SystemScope public const string UserLanguage = nameof(UserLanguage); } - public static FrozenSet AllNames { get; } = GetNames().ToFrozenSet(); - - public static IEnumerable GetNames() - { - yield return Names.Activity; - yield return Names.Bot; - yield return Names.Conversation; - yield return Names.ConversationId; - yield return Names.InternalId; - yield return Names.LastMessage; - yield return Names.LastMessageId; - yield return Names.LastMessageText; - yield return Names.Recognizer; - yield return Names.User; - yield return Names.UserLanguage; - } + public static FrozenSet AllNames { get; } = + [ + Names.Activity, + Names.Bot, + Names.Conversation, + Names.ConversationId, + Names.InternalId, + Names.LastMessage, + Names.LastMessageId, + Names.LastMessageText, + Names.Recognizer, + Names.User, + Names.UserLanguage, + ]; public static void InitializeSystem(this WorkflowFormulaState scopes) { @@ -59,7 +56,7 @@ internal static class SystemScope scopes.Set( Names.Conversation, - RecordValue.NewRecordFromFields( + FormulaValue.NewRecordFromFields( new NamedValue("Id", FormulaType.String.NewBlank()), new NamedValue("LocalTimeZone", FormulaValue.New(TimeZoneInfo.Local.StandardName)), new NamedValue("LocalTimeZoneOffset", FormulaValue.New(TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow))), @@ -70,17 +67,17 @@ internal static class SystemScope scopes.Set( Names.Recognizer, - RecordValue.NewRecordFromFields( + FormulaValue.NewRecordFromFields( new NamedValue("Id", FormulaType.String.NewBlank()), new NamedValue("Text", FormulaType.String.NewBlank())), VariableScopeNames.System); scopes.Set( Names.User, - RecordValue.NewRecordFromFields( - new NamedValue("Language", StringValue.New(CultureInfo.CurrentCulture.TwoLetterISOLanguageName))), + FormulaValue.NewRecordFromFields( + new NamedValue("Language", FormulaValue.New(CultureInfo.CurrentCulture.TwoLetterISOLanguageName))), VariableScopeNames.System); - scopes.Set(Names.UserLanguage, StringValue.New(CultureInfo.CurrentCulture.TwoLetterISOLanguageName), VariableScopeNames.System); + scopes.Set(Names.UserLanguage, FormulaValue.New(CultureInfo.CurrentCulture.TwoLetterISOLanguageName), VariableScopeNames.System); void Set(string key, string? value = null) { diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowDiagnostics.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowDiagnostics.cs index db3c860d54..a55e7821d5 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowDiagnostics.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowDiagnostics.cs @@ -53,19 +53,17 @@ internal static class WorkflowDiagnostics { foreach (VariableInformationDiagnostic variableDiagnostic in semanticModel.GetVariables(schemaName).Where(x => !x.IsSystemVariable).Select(v => v.ToDiagnostic())) { - if (variableDiagnostic is null || variableDiagnostic?.Path?.VariableName is null) + if (variableDiagnostic?.Path?.VariableName is null) { continue; } FormulaValue defaultValue = variableDiagnostic.ConstantValue?.ToFormula() ?? variableDiagnostic.Type.NewBlank(); - if (variableDiagnostic.Path.VariableScopeName?.Equals(VariableScopeNames.System, StringComparison.OrdinalIgnoreCase) ?? false) + if (variableDiagnostic.Path.VariableScopeName?.Equals(VariableScopeNames.System, StringComparison.OrdinalIgnoreCase) is true && + !SystemScope.AllNames.Contains(variableDiagnostic.Path.VariableName)) { - if (!SystemScope.AllNames.Contains(variableDiagnostic.Path.VariableName)) - { - throw new DeclarativeModelException($"Variable '{variableDiagnostic.Path.VariableName}' is not a supported system variable."); - } + throw new DeclarativeModelException($"Variable '{variableDiagnostic.Path.VariableName}' is not a supported system variable."); } scopes.Set(variableDiagnostic.Path.VariableName, defaultValue, variableDiagnostic.Path.VariableScopeName ?? WorkflowFormulaState.DefaultScopeName); diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowExpressionEngine.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowExpressionEngine.cs index 68fb410aaa..7991f23cf0 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowExpressionEngine.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowExpressionEngine.cs @@ -13,7 +13,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Declarative.PowerFx; -internal class WorkflowExpressionEngine +internal sealed class WorkflowExpressionEngine { private readonly RecalcEngine _engine; @@ -39,7 +39,7 @@ internal class WorkflowExpressionEngine public ImmutableArray GetValue(ArrayExpressionOnly expression) => this.Evaluate(expression).Value; public EvaluationResult GetValue(EnumExpression expression) where TValue : EnumWrapper => - this.Evaluate(expression); + this.Evaluate(expression); private EvaluationResult Evaluate(BoolExpression expression) { @@ -186,7 +186,7 @@ internal class WorkflowExpressionEngine { Throw.IfNull(expression, nameof(expression)); - if (expression.LiteralValue != null) + if (expression.LiteralValue is not null) { return new EvaluationResult(expression.LiteralValue, SensitivityLevel.None); } @@ -240,7 +240,7 @@ internal class WorkflowExpressionEngine { if (value is BlankValue) { - return ImmutableArray.Empty; + return []; } if (value is not TableValue tableValue) @@ -254,8 +254,7 @@ internal class WorkflowExpressionEngine List list = []; foreach (RecordDataValue row in tableDataValue.Values) { - TValue? s = TableItemParser.Parse(row); - if (s != null) + if (TableItemParser.Parse(row) is TValue s) { list.Add(s); } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowFormulaState.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowFormulaState.cs index c067d8bc8d..cbe213a8dd 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowFormulaState.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/PowerFx/WorkflowFormulaState.cs @@ -105,10 +105,11 @@ internal sealed class WorkflowFormulaState foreach (string key in keys) { object? value = await context.ReadStateAsync(key, scopeName).ConfigureAwait(false); - if (value is null || value is UnassignedValue) + if (value is null or UnassignedValue) { value = FormulaValue.NewBlank(); } + this.Set(key, value.ToFormula(), scopeName); } @@ -142,7 +143,7 @@ internal sealed class WorkflowFormulaState private WorkflowScope GetScope(string? scopeName) { - scopeName ??= WorkflowFormulaState.DefaultScopeName; + scopeName ??= DefaultScopeName; if (!VariableScopeNames.IsValidName(scopeName)) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/AIAgentsAbstractionsExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows/AIAgentsAbstractionsExtensions.cs index d627d01788..cab241d4a4 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/AIAgentsAbstractionsExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/AIAgentsAbstractionsExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System.Collections.Generic; using System.Diagnostics; using Microsoft.Extensions.AI; using Microsoft.Extensions.AI.Agents; @@ -9,34 +8,29 @@ namespace Microsoft.Agents.Workflows; internal static class AIAgentsAbstractionsExtensions { - public static ChatMessage ToChatMessage(this AgentRunResponseUpdate update) - { - return new ChatMessage + public static ChatMessage ToChatMessage(this AgentRunResponseUpdate update) => + new() { AuthorName = update.AuthorName, Contents = update.Contents, Role = update.Role ?? ChatRole.User, CreatedAt = update.CreatedAt, MessageId = update.MessageId, - RawRepresentation = update.RawRepresentation, + RawRepresentation = update.RawRepresentation ?? update, }; - } public static ChatMessage UpdateWith(this ChatMessage baseMessage, AgentRunResponseUpdate update) { - Debug.Assert(update.MessageId == null || baseMessage.MessageId == update.MessageId); + Debug.Assert(update.MessageId is null || baseMessage.MessageId == update.MessageId); - List mergedContent = new(baseMessage.Contents); - mergedContent.AddRange(update.Contents); - - return new ChatMessage + return new() { AuthorName = update.AuthorName ?? baseMessage.AuthorName, - Contents = mergedContent, + Contents = [.. baseMessage.Contents, .. update.Contents], Role = update.Role ?? baseMessage.Role, CreatedAt = update.CreatedAt ?? baseMessage.CreatedAt, MessageId = baseMessage.MessageId, - RawRepresentation = update.RawRepresentation ?? baseMessage.RawRepresentation, + RawRepresentation = update.RawRepresentation ?? baseMessage.RawRepresentation ?? update, }; } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/AgentRunResponseEvent.cs b/dotnet/src/Microsoft.Agents.Workflows/AgentRunResponseEvent.cs index a323f6eb9b..d4453ad8ae 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/AgentRunResponseEvent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/AgentRunResponseEvent.cs @@ -1,11 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.Extensions.AI.Agents; +using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows; /// -/// Event triggered when an agent run produces an update. +/// Represents an event triggered when an agent run produces an update. /// public class AgentRunResponseEvent : ExecutorEvent { @@ -13,14 +14,14 @@ public class AgentRunResponseEvent : ExecutorEvent /// Initializes a new instance of the class. /// /// The identifier of the executor that generated this event. - /// + /// The agent run response. public AgentRunResponseEvent(string executorId, AgentRunResponse response) : base(executorId, data: response) { - this.Response = response; + this.Response = Throw.IfNull(response); } /// - /// Gets the content of the agent response. + /// Gets the agent run response. /// public AgentRunResponse Response { get; } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/AgentRunUpdateEvent.cs b/dotnet/src/Microsoft.Agents.Workflows/AgentRunUpdateEvent.cs index 0972fdd871..e73e8fa29d 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/AgentRunUpdateEvent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/AgentRunUpdateEvent.cs @@ -2,11 +2,12 @@ using System.Collections.Generic; using Microsoft.Extensions.AI.Agents; +using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows; /// -/// Event triggered when an agent run produces an update. +/// Represents an event triggered when an agent run produces an update. /// public class AgentRunUpdateEvent : ExecutorEvent { @@ -14,14 +15,14 @@ public class AgentRunUpdateEvent : ExecutorEvent /// Initializes a new instance of the class. /// /// The identifier of the executor that generated this event. - /// + /// The agent run response update. public AgentRunUpdateEvent(string executorId, AgentRunResponseUpdate update) : base(executorId, data: update) { - this.Update = update; + this.Update = Throw.IfNull(update); } /// - /// Gets the content of the agent response. + /// Gets the agent run response update. /// public AgentRunResponseUpdate Update { get; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/CheckpointInfo.cs b/dotnet/src/Microsoft.Agents.Workflows/CheckpointInfo.cs index a03b59e2a3..981cee57e8 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/CheckpointInfo.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/CheckpointInfo.cs @@ -9,7 +9,7 @@ namespace Microsoft.Agents.Workflows; /// /// Represents a checkpoint with a unique identifier and a timestamp indicating when it was created. /// -public class CheckpointInfo : IEquatable +public sealed class CheckpointInfo : IEquatable { /// /// Gets the unique identifier for the current run. @@ -37,27 +37,16 @@ public class CheckpointInfo : IEquatable } /// - public bool Equals(CheckpointInfo? other) - { - if (other == null) - { - return false; - } - - return this.RunId == other.RunId && this.CheckpointId == other.CheckpointId; - } + public bool Equals(CheckpointInfo? other) => + other is not null && + this.RunId == other.RunId && + this.CheckpointId == other.CheckpointId; /// - public override bool Equals(object? obj) - { - return this.Equals(obj as CheckpointInfo); - } + public override bool Equals(object? obj) => this.Equals(obj as CheckpointInfo); /// - public override int GetHashCode() - { - return HashCode.Combine(this.RunId, this.CheckpointId); - } + public override int GetHashCode() => HashCode.Combine(this.RunId, this.CheckpointId); /// public override string ToString() => $"CheckpointInfo(RunId: {this.RunId}, CheckpointId: {this.CheckpointId})"; diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointed.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointed.cs index ec9499b56c..9bebf7c405 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointed.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointed.cs @@ -11,21 +11,21 @@ namespace Microsoft.Agents.Workflows; /// /// Represents a workflow run that supports checkpointing. /// -/// The type of the underlying workflow run handle +/// The type of the underlying workflow run handle. /// /// /// /// public class Checkpointed { + private readonly ICheckpointingRunner _runner; + internal Checkpointed(TRun run, ICheckpointingRunner runner) { this.Run = Throw.IfNull(run); this._runner = Throw.IfNull(runner); } - private readonly ICheckpointingRunner _runner; - /// /// Gets the workflow run associated with this instance. /// @@ -41,7 +41,14 @@ public class Checkpointed /// /// Gets the most recent checkpoint information. /// - public CheckpointInfo? LastCheckpoint => this.Checkpoints.Count > 0 ? this.Checkpoints[this.Checkpoints.Count - 1] : null; + public CheckpointInfo? LastCheckpoint + { + get + { + var checkpoints = this.Checkpoints; + return checkpoints.Count > 0 ? checkpoints[checkpoints.Count - 1] : null; + } + } /// public ValueTask RestoreCheckpointAsync(CheckpointInfo checkpointInfo, CancellationToken cancellation = default) diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/Checkpoint.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/Checkpoint.cs index 79335b64e3..aceb12e2fb 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/Checkpoint.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/Checkpoint.cs @@ -7,7 +7,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Checkpointing; -internal class Checkpoint +internal sealed class Checkpoint { [JsonConstructor] internal Checkpoint( @@ -33,8 +33,8 @@ internal class Checkpoint public WorkflowInfo Workflow { get; } public RunnerStateData RunnerData { get; } - public Dictionary StateData { get; } = new(); - public Dictionary EdgeStateData { get; } = new(); + public Dictionary StateData { get; } = []; + public Dictionary EdgeStateData { get; } = []; public CheckpointInfo? Parent { get; } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/DirectEdgeInfo.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/DirectEdgeInfo.cs index 48a725ba9b..4319947025 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/DirectEdgeInfo.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/DirectEdgeInfo.cs @@ -10,7 +10,7 @@ namespace Microsoft.Agents.Workflows.Checkpointing; /// public sealed class DirectEdgeInfo : EdgeInfo { - internal DirectEdgeInfo(DirectEdgeData data) : this(data.Condition != null, data.Connection) { } + internal DirectEdgeInfo(DirectEdgeData data) : this(data.Condition is not null, data.Connection) { } [JsonConstructor] internal DirectEdgeInfo(bool hasCondition, EdgeConnection connection) : base(EdgeKind.Direct, connection) @@ -26,6 +26,6 @@ public sealed class DirectEdgeInfo : EdgeInfo internal override bool IsMatchInternal(EdgeData edgeData) { return edgeData is DirectEdgeData directEdge - && this.HasCondition == (directEdge.Condition != null); + && this.HasCondition == (directEdge.Condition is not null); } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ExecutorInfo.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ExecutorInfo.cs index 546cb824af..4d5df46572 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ExecutorInfo.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ExecutorInfo.cs @@ -2,23 +2,17 @@ namespace Microsoft.Agents.Workflows.Checkpointing; -internal record class ExecutorInfo(TypeId ExecutorType, string ExecutorId) +internal sealed record class ExecutorInfo(TypeId ExecutorType, string ExecutorId) { - public bool IsMatch() where T : Executor - { - return this.ExecutorType.IsMatch() + public bool IsMatch() where T : Executor => + this.ExecutorType.IsMatch() && this.ExecutorId == typeof(T).Name; - } - public bool IsMatch(Executor executor) - { - return this.ExecutorType.IsMatch(executor.GetType()) + public bool IsMatch(Executor executor) => + this.ExecutorType.IsMatch(executor.GetType()) && this.ExecutorId == executor.Id; - } - public bool IsMatch(ExecutorRegistration registration) - { - return this.ExecutorType.IsMatch(registration.ExecutorType) + public bool IsMatch(ExecutorRegistration registration) => + this.ExecutorType.IsMatch(registration.ExecutorType) && this.ExecutorId == registration.Id; - } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FanOutEdgeInfo.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FanOutEdgeInfo.cs index 74b6bf333d..98a5f8e9a5 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FanOutEdgeInfo.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FanOutEdgeInfo.cs @@ -10,7 +10,7 @@ namespace Microsoft.Agents.Workflows.Checkpointing; /// public sealed class FanOutEdgeInfo : EdgeInfo { - internal FanOutEdgeInfo(FanOutEdgeData data) : this(data.EdgeAssigner != null, data.Connection) { } + internal FanOutEdgeInfo(FanOutEdgeData data) : this(data.EdgeAssigner is not null, data.Connection) { } [JsonConstructor] internal FanOutEdgeInfo(bool hasAssigner, EdgeConnection connection) : base(EdgeKind.FanOut, connection) @@ -26,6 +26,6 @@ public sealed class FanOutEdgeInfo : EdgeInfo internal override bool IsMatchInternal(EdgeData edgeData) { return edgeData is FanOutEdgeData fanOutEdge - && this.HasAssigner == (fanOutEdge.EdgeAssigner != null); + && this.HasAssigner == (fanOutEdge.EdgeAssigner is not null); } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FileSystemJsonCheckpointStore.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FileSystemJsonCheckpointStore.cs index 46187e10bb..752f92b1bb 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FileSystemJsonCheckpointStore.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/FileSystemJsonCheckpointStore.cs @@ -55,12 +55,11 @@ public sealed class FileSystemJsonCheckpointStore : JsonCheckpointStore, IDispos try { // read the lines of indexfile and parse them as CheckpointInfos - this.CheckpointIndex = new HashSet(); + this.CheckpointIndex = []; using StreamReader reader = new(this._indexFile, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: -1, leaveOpen: true); while (reader.ReadLine() is string line) { - CheckpointInfo? info = JsonSerializer.Deserialize(line, this.KeyTypeInfo); - if (info != null) + if (JsonSerializer.Deserialize(line, this.KeyTypeInfo) is { } info) { this.CheckpointIndex.Add(info); } @@ -83,7 +82,7 @@ public sealed class FileSystemJsonCheckpointStore : JsonCheckpointStore, IDispos Justification = "Throw helper does not exist in NetFx 4.7.2")] private void CheckDisposed() { - if (this._indexFile == null) + if (this._indexFile is null) { throw new ObjectDisposedException($"{nameof(FileSystemJsonCheckpointStore)}({this.Directory.FullName})"); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/InMemoryCheckpointManager.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/InMemoryCheckpointManager.cs index 2a2e0f2f54..eb312b2ea0 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/InMemoryCheckpointManager.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/InMemoryCheckpointManager.cs @@ -10,7 +10,7 @@ namespace Microsoft.Agents.Workflows.Checkpointing; /// internal sealed class InMemoryCheckpointManager : ICheckpointManager { - private readonly Dictionary> _store = new(); + private readonly Dictionary> _store = []; private RunCheckpointCache GetRunStore(string runId) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterBase.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterBase.cs index fbcd75bd9c..701401b5ff 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterBase.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterBase.cs @@ -18,18 +18,11 @@ internal abstract class JsonConverterBase : JsonConverter public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { SequencePosition position = reader.Position; - - T? maybeValue = JsonSerializer.Deserialize(ref reader, this.TypeInfo); - if (maybeValue is null) - { + return + JsonSerializer.Deserialize(ref reader, this.TypeInfo) ?? throw new JsonException($"Could not deserialize a {typeof(T).Name} from JSON at position {position}"); - } - - return maybeValue; } - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) - { + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, value, this.TypeInfo); - } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterDictionarySupportBase.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterDictionarySupportBase.cs index d4ddefbeff..6ee10d2d2d 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterDictionarySupportBase.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonConverterDictionarySupportBase.cs @@ -23,7 +23,7 @@ internal abstract class JsonConverterDictionarySupportBase : JsonConverterBas SequencePosition position = reader.Position; string? propertyName = reader.GetString(); - if (propertyName == null) + if (propertyName is null) { throw new JsonException($"Got null trying to read property name at position {position}"); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonMarshaller.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonMarshaller.cs index 14c313cc90..b9d87bdc88 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonMarshaller.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonMarshaller.cs @@ -6,7 +6,7 @@ using System.Text.Json.Serialization.Metadata; namespace Microsoft.Agents.Workflows.Checkpointing; -internal class JsonMarshaller : IWireMarshaller +internal sealed class JsonMarshaller : IWireMarshaller { private readonly JsonSerializerOptions _internalOptions; private readonly JsonSerializerOptions? _externalOptions; @@ -26,7 +26,7 @@ internal class JsonMarshaller : IWireMarshaller { if (!this._internalOptions.TryGetTypeInfo(type, out JsonTypeInfo? typeInfo)) { - if (this._externalOptions == null || + if (this._externalOptions is null || !this._externalOptions.TryGetTypeInfo(type, out typeInfo)) { throw new InvalidOperationException($"No JSON type info is available for type '{type}'."); @@ -44,13 +44,8 @@ internal class JsonMarshaller : IWireMarshaller public TValue Marshal(JsonElement data) { - Type type = typeof(TValue); - object? value = JsonSerializer.Deserialize(data, this.LookupTypeInfo(type)); - - if (value is null) - { + object value = data.Deserialize(this.LookupTypeInfo(typeof(TValue))) ?? throw new InvalidOperationException($"Could not deserialize the value as the expected type {typeof(TValue)}."); - } if (value is TValue typedValue) { @@ -62,12 +57,8 @@ internal class JsonMarshaller : IWireMarshaller public object Marshal(Type targetType, JsonElement data) { - object? value = JsonSerializer.Deserialize(data, this.LookupTypeInfo(targetType)); - - if (value is null) - { + object value = data.Deserialize(this.LookupTypeInfo(targetType)) ?? throw new InvalidOperationException($"Could not deserialize the value as the expected type {targetType}."); - } if (targetType.IsInstanceOfType(value)) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonWireSerializedValue.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonWireSerializedValue.cs index d07d874cc5..07eb3983a5 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonWireSerializedValue.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/JsonWireSerializedValue.cs @@ -23,7 +23,7 @@ internal sealed class JsonWireSerializedValue(JsonMarshaller serializer, JsonEle public override bool Equals(object? obj) { - if (obj == null) + if (obj is null) { return false; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/PortableValueConverter.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/PortableValueConverter.cs index c4456f5635..512a459216 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/PortableValueConverter.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/PortableValueConverter.cs @@ -24,7 +24,7 @@ internal sealed class PortableValueConverter(JsonMarshaller marshaller) : JsonCo SequencePosition initial = reader.Position; JsonTypeInfo baseTypeInfo = WorkflowsJsonUtilities.JsonContext.Default.PortableValue; - PortableValue? maybeValue = JsonSerializer.Deserialize(ref reader, baseTypeInfo); + PortableValue? maybeValue = JsonSerializer.Deserialize(ref reader, baseTypeInfo); if (maybeValue is null) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/RunCheckpointCache.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/RunCheckpointCache.cs index 2771d97be7..7241d5121b 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/RunCheckpointCache.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/RunCheckpointCache.cs @@ -7,8 +7,8 @@ namespace Microsoft.Agents.Workflows.Checkpointing; internal sealed class RunCheckpointCache { - private readonly HashSet _checkpointIndex = new(); - private readonly Dictionary _cache = new(); + private readonly HashSet _checkpointIndex = []; + private readonly Dictionary _cache = []; public IEnumerable Index => this._checkpointIndex; diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ScopeKeyConverter.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ScopeKeyConverter.cs index bfbe6742bc..fc49129099 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ScopeKeyConverter.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/ScopeKeyConverter.cs @@ -39,12 +39,12 @@ internal sealed class ScopeKeyConverter : JsonConverterDictionarySupportBase /// A representation of a type's identity, including its assembly and type names. /// -public class TypeId +public sealed class TypeId : IEquatable { /// public string AssemblyName { get; } @@ -43,15 +43,29 @@ public class TypeId /// public override bool Equals(object? obj) - => obj is TypeId other - && this.AssemblyName == other.AssemblyName - && this.TypeName == other.TypeName; + => this.Equals(obj as TypeId); + + /// + public bool Equals(TypeId? other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return this.AssemblyName == other.AssemblyName && this.TypeName == other.TypeName; + } /// public override int GetHashCode() => HashCode.Combine(this.AssemblyName, this.TypeName); /// - public static bool operator ==(TypeId? left, TypeId? right) => object.ReferenceEquals(left, right) || (!object.ReferenceEquals(left, null) && left.Equals(right)); + public static bool operator ==(TypeId? left, TypeId? right) => left is null ? right is null : left.Equals(right); /// public static bool operator !=(TypeId? left, TypeId? right) => !(left == right); @@ -84,7 +98,7 @@ public class TypeId { Type? candidateType = type; - while (candidateType != null) + while (candidateType is not null) { if (this.IsMatch(candidateType)) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/WorkflowInfo.cs b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/WorkflowInfo.cs index 0f5fdd723c..380ce7fe0e 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/WorkflowInfo.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Checkpointing/WorkflowInfo.cs @@ -8,7 +8,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Checkpointing; -internal class WorkflowInfo +internal sealed class WorkflowInfo { [JsonConstructor] internal WorkflowInfo( @@ -27,12 +27,12 @@ internal class WorkflowInfo this.InputType = Throw.IfNull(inputType); this.StartExecutorId = Throw.IfNullOrEmpty(startExecutorId); - if (outputType != null && outputCollectorId != null) + if (outputType is not null && outputCollectorId is not null) { this.OutputType = outputType; this.OutputCollectorId = outputCollectorId; } - else if (outputCollectorId != null) + else if (outputCollectorId is not null) { throw new InvalidOperationException( $"Either both or none of OutputType and OutputCollectorId must be set. ({nameof(outputType)}: {outputType} vs. {nameof(outputCollectorId)}: {outputCollectorId})" @@ -108,6 +108,6 @@ internal class WorkflowInfo public bool IsMatch(Workflow workflow) => this.IsMatch(workflow as Workflow) - && this.OutputType != null && this.OutputType.IsMatch(typeof(TResult)) - && this.OutputCollectorId != null && this.OutputCollectorId == workflow.OutputCollectorId; + && this.OutputType?.IsMatch(typeof(TResult)) is true + && this.OutputCollectorId is not null && this.OutputCollectorId == workflow.OutputCollectorId; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Config.cs b/dotnet/src/Microsoft.Agents.Workflows/Config.cs index cdd2ce1534..9833f58934 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Config.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Config.cs @@ -3,13 +3,13 @@ namespace Microsoft.Agents.Workflows; /// -/// Configuration for an object with a string identifier. For example, object. +/// Represents a configuration for an object with a string identifier. For example, object. /// /// A unique identifier for the configurable object. public class Config(string? id = null) { /// - /// A unique identifier for the configurable object. + /// Gets a unique identifier for the configurable object. /// /// /// If not provided, the configured object will generate its own identifier. @@ -18,7 +18,7 @@ public class Config(string? id = null) } /// -/// Configuration for an object with a string identifier and options of type . +/// Represents a configuration for an object with a string identifier and options of type . /// /// The type of options for the configurable object. /// The options for the configurable object. @@ -26,7 +26,7 @@ public class Config(string? id = null) public class Config(TOptions? options = default, string? id = null) : Config(id) { /// - /// Options for the configured object. + /// Gets the options for the configured object. /// public TOptions? Options => options; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs index c3309116c6..7ded4d31e1 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs @@ -3,7 +3,7 @@ namespace Microsoft.Agents.Workflows; /// -/// Extensions methods for creating Configured objects +/// Provides extensions methods for creating objects /// public static class ConfigurationExtensions { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Configured.cs b/dotnet/src/Microsoft.Agents.Workflows/Configured.cs index d88da9277f..5447937f08 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Configured.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Configured.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Microsoft.Agents.Workflows; /// -/// Helper methods for creating instances. +/// Provides methods for creating instances. /// public static class Configured { @@ -29,20 +29,20 @@ public static class Configured { if (subject is IIdentified identified) { - if (id != null && identified.Id != id) + if (id is not null && identified.Id != id) { throw new ArgumentException($"Provided ID '{id}' does not match subject's ID '{identified.Id}'.", nameof(id)); } - return new Configured((_) => new(subject), id: identified.Id, raw: raw ?? subject); + return new Configured(_ => new(subject), id: identified.Id, raw: raw ?? subject); } - if (id == null) + if (id is null) { throw new ArgumentNullException(nameof(id), "ID must be provided when the subject does not implement IIdentified."); } - return new Configured((_) => new(subject), id, raw: raw ?? subject); + return new Configured(_ => new(subject), id, raw: raw ?? subject); } } @@ -56,7 +56,7 @@ public static class Configured public class Configured(Func> factoryAsync, string id, object? raw = null) { /// - /// The raw representation of the configured object, if any. + /// Gets the raw representation of the configured object, if any. /// public object? Raw => raw; @@ -137,7 +137,7 @@ public class Configured(Func, ValueTask /// public override bool Equals(object? obj) { - if (obj == null) + if (obj is null) { return false; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/CallResult.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/CallResult.cs index 6e2ff3c494..a32fa2da65 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/CallResult.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/CallResult.cs @@ -19,18 +19,18 @@ internal sealed class CallResult /// If the call was successful, this property contains the result of the call. For calls to /// void handlers, this will be null. /// - public object? Result { get; init; } = null; + public object? Result { get; init; } /// /// If the call failed, this property contains the exception that was raised during the call. /// - public Exception? Exception { get; init; } = null; + public Exception? Exception { get; init; } /// /// Indicates whether the call was successful. A call is considered successful if it returned /// without throwing an exception. /// - public bool IsSuccess => this.Exception == null; + public bool IsSuccess => this.Exception is null; private CallResult(bool isVoid = false) { @@ -43,19 +43,13 @@ internal sealed class CallResult /// /// The result to return. /// A indicating the result of the call. - public static CallResult ReturnResult(object? result = null) - { - return new() { Result = result }; - } + public static CallResult ReturnResult(object? result = null) => new() { Result = result }; /// /// Create a indicating a successful call that returned no result (void). /// /// A indicating the result of the call. - public static CallResult ReturnVoid() - { - return new(isVoid: true); - } + public static CallResult ReturnVoid() => new(isVoid: true); /// /// Create a indicating that an exception was raised during the call. diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/DirectEdgeRunner.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/DirectEdgeRunner.cs index ed44fbb555..e39ebdeedf 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/DirectEdgeRunner.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/DirectEdgeRunner.cs @@ -5,26 +5,23 @@ using System.Threading.Tasks; namespace Microsoft.Agents.Workflows.Execution; -internal class DirectEdgeRunner(IRunnerContext runContext, DirectEdgeData edgeData) : +internal sealed class DirectEdgeRunner(IRunnerContext runContext, DirectEdgeData edgeData) : EdgeRunner(runContext, edgeData) { public IWorkflowContext WorkflowContext { get; } = runContext.Bind(edgeData.SinkId); - private async ValueTask FindRouterAsync(IStepTracer? tracer) - { - return await this.RunContext.EnsureExecutorAsync(this.EdgeData.SinkId, tracer) + private async ValueTask FindRouterAsync(IStepTracer? tracer) => await this.RunContext.EnsureExecutorAsync(this.EdgeData.SinkId, tracer) .ConfigureAwait(false); - } public async ValueTask> ChaseAsync(MessageEnvelope envelope, IStepTracer? tracer) { - if (envelope.TargetId != null && this.EdgeData.SinkId != envelope.TargetId) + if (envelope.TargetId is not null && this.EdgeData.SinkId != envelope.TargetId) { return []; } object message = envelope.Message; - if (this.EdgeData.Condition != null && !this.EdgeData.Condition(message)) + if (this.EdgeData.Condition is not null && !this.EdgeData.Condition(message)) { return []; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeConnection.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeConnection.cs index 819c25038c..429ab8add4 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeConnection.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeConnection.cs @@ -15,7 +15,7 @@ namespace Microsoft.Agents.Workflows.Execution; /// Ordering is relevant because in at least one case, the order of sinks is significant for the execution of /// the edge: . /// -public class EdgeConnection : IEquatable +public sealed class EdgeConnection : IEquatable { /// /// Create an instance with the specified source and sink IDs. @@ -65,7 +65,7 @@ public class EdgeConnection : IEquatable return false; } - if (object.ReferenceEquals(this, other)) + if (ReferenceEquals(this, other)) { return true; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeMap.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeMap.cs index 695b39748a..cc2c7309a0 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeMap.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/EdgeMap.cs @@ -8,10 +8,10 @@ using Microsoft.Agents.Workflows.Checkpointing; namespace Microsoft.Agents.Workflows.Execution; -internal class EdgeMap +internal sealed class EdgeMap { - private readonly Dictionary _edgeRunners = new(); - private readonly Dictionary _fanInState = new(); + private readonly Dictionary _edgeRunners = []; + private readonly Dictionary _fanInState = []; private readonly Dictionary _portEdgeRunners; private readonly InputEdgeRunner _inputRunner; private readonly IStepTracer? _stepTracer; @@ -68,14 +68,14 @@ internal class EdgeMap // between the Runners, we can normalize it behind an IFace. case EdgeKind.Direct: { - DirectEdgeRunner runner = (DirectEdgeRunner)this._edgeRunners[id]; + DirectEdgeRunner runner = (DirectEdgeRunner)edgeRunner; edgeResults = await runner.ChaseAsync(message, this._stepTracer).ConfigureAwait(false); break; } case EdgeKind.FanOut: { - FanOutEdgeRunner runner = (FanOutEdgeRunner)this._edgeRunners[id]; + FanOutEdgeRunner runner = (FanOutEdgeRunner)edgeRunner; edgeResults = await runner.ChaseAsync(message, this._stepTracer).ConfigureAwait(false); break; } @@ -83,7 +83,7 @@ internal class EdgeMap case EdgeKind.FanIn: { FanInEdgeState state = this._fanInState[id]; - FanInEdgeRunner runner = (FanInEdgeRunner)this._edgeRunners[id]; + FanInEdgeRunner runner = (FanInEdgeRunner)edgeRunner; edgeResults = [await runner.ChaseAsync(sourceId, message, state, this._stepTracer).ConfigureAwait(false)]; break; } @@ -114,7 +114,7 @@ internal class EdgeMap internal ValueTask> ExportStateAsync() { - Dictionary exportedStates = new(); + Dictionary exportedStates = []; // Right now there is only fan-in state foreach (EdgeId id in this._fanInState.Keys) diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/ExecutorIdentity.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/ExecutorIdentity.cs index b612a735bb..8161a701d8 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/ExecutorIdentity.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/ExecutorIdentity.cs @@ -7,25 +7,23 @@ namespace Microsoft.Agents.Workflows.Execution; internal readonly struct ExecutorIdentity : IEquatable { - public static ExecutorIdentity None { get; } = new ExecutorIdentity(); + public static ExecutorIdentity None { get; } public string? Id { get; init; } - public bool Equals(ExecutorIdentity other) - { - return this.Id == null - ? other.Id == null - : other.Id != null && StringComparer.OrdinalIgnoreCase.Equals(this.Id, other.Id); - } + public bool Equals(ExecutorIdentity other) => + this.Id is null + ? other.Id is null + : other.Id is not null && StringComparer.OrdinalIgnoreCase.Equals(this.Id, other.Id); public override bool Equals([NotNullWhen(true)] object? obj) { - if (this.Id == null) + if (this.Id is null) { - return obj == null; + return obj is null; } - if (obj == null) + if (obj is null) { return false; } @@ -43,18 +41,9 @@ internal readonly struct ExecutorIdentity : IEquatable return false; } - public override int GetHashCode() - { - return this.Id == null ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(this.Id); - } + public override int GetHashCode() => this.Id is null ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(this.Id); - public static implicit operator ExecutorIdentity(string? id) - { - return new ExecutorIdentity { Id = id }; - } + public static implicit operator ExecutorIdentity(string? id) => new() { Id = id }; - public static implicit operator string?(ExecutorIdentity identity) - { - return identity.Id; - } + public static implicit operator string?(ExecutorIdentity identity) => identity.Id; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeRunner.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeRunner.cs index 24dd2788bc..386c580619 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeRunner.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeRunner.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Microsoft.Agents.Workflows.Execution; -internal class FanInEdgeRunner(IRunnerContext runContext, FanInEdgeData edgeData) : +internal sealed class FanInEdgeRunner(IRunnerContext runContext, FanInEdgeData edgeData) : EdgeRunner(runContext, edgeData) { private IWorkflowContext BoundContext { get; } = runContext.Bind(edgeData.SinkId); @@ -15,13 +15,12 @@ internal class FanInEdgeRunner(IRunnerContext runContext, FanInEdgeData edgeData public ValueTask> ChaseAsync(string sourceId, MessageEnvelope envelope, FanInEdgeState state, IStepTracer? tracer) { - if (envelope.TargetId != null && this.EdgeData.SinkId != envelope.TargetId) + if (envelope.TargetId is not null && this.EdgeData.SinkId != envelope.TargetId) { // This message is not for us. return new([]); } - object message = envelope.Message; IEnumerable? releasedMessages = state.ProcessMessage(sourceId, envelope); if (releasedMessages is null) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeState.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeState.cs index cb6c5576c7..f819107764 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeState.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/FanInEdgeState.cs @@ -8,7 +8,7 @@ using Microsoft.Agents.Workflows.Checkpointing; namespace Microsoft.Agents.Workflows.Execution; -internal class FanInEdgeState +internal sealed class FanInEdgeState { private List _pendingMessages; public FanInEdgeState(FanInEdgeData fanInEdge) diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/FanOutEdgeRunner.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/FanOutEdgeRunner.cs index b7e4723c2e..3613fcee05 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/FanOutEdgeRunner.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/FanOutEdgeRunner.cs @@ -6,26 +6,27 @@ using System.Threading.Tasks; namespace Microsoft.Agents.Workflows.Execution; -internal class FanOutEdgeRunner(IRunnerContext runContext, FanOutEdgeData edgeData) : +internal sealed class FanOutEdgeRunner(IRunnerContext runContext, FanOutEdgeData edgeData) : EdgeRunner(runContext, edgeData) { private Dictionary BoundContexts { get; } = edgeData.SinkIds.ToDictionary( sinkId => sinkId, - sinkId => runContext.Bind(sinkId)); + runContext.Bind); public async ValueTask> ChaseAsync(MessageEnvelope envelope, IStepTracer? tracer) { object message = envelope.Message; List targets = - this.EdgeData.EdgeAssigner == null + this.EdgeData.EdgeAssigner is null ? this.EdgeData.SinkIds : this.EdgeData.EdgeAssigner(message, this.BoundContexts.Count) .Select(i => this.EdgeData.SinkIds[i]).ToList(); - IEnumerable filteredTargets = envelope.TargetId != null - ? targets.Where(IsValidTarget) - : targets; + IEnumerable filteredTargets = + envelope.TargetId is not null + ? targets.Where(IsValidTarget) + : targets; object?[] result = await Task.WhenAll(filteredTargets.Select(ProcessTargetAsync)).ConfigureAwait(false); return result.Where(r => r is not null); @@ -47,7 +48,7 @@ internal class FanOutEdgeRunner(IRunnerContext runContext, FanOutEdgeData edgeDa bool IsValidTarget(string targetId) { - return envelope.TargetId == null || targetId == envelope.TargetId; + return envelope.TargetId is null || targetId == envelope.TargetId; } } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/InputEdgeRunner.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/InputEdgeRunner.cs index 7de23a000a..e78129c912 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/InputEdgeRunner.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/InputEdgeRunner.cs @@ -6,7 +6,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Execution; -internal class InputEdgeRunner(IRunnerContext runContext, string sinkId) +internal sealed class InputEdgeRunner(IRunnerContext runContext, string sinkId) : EdgeRunner(runContext, sinkId) { public IWorkflowContext WorkflowContext { get; } = runContext.Bind(sinkId); @@ -19,10 +19,7 @@ internal class InputEdgeRunner(IRunnerContext runContext, string sinkId) return new InputEdgeRunner(runContext, port.Id); } - private async ValueTask FindExecutorAsync(IStepTracer? tracer) - { - return await this.RunContext.EnsureExecutorAsync(this.EdgeData, tracer).ConfigureAwait(false); - } + private async ValueTask FindExecutorAsync(IStepTracer? tracer) => await this.RunContext.EnsureExecutorAsync(this.EdgeData, tracer).ConfigureAwait(false); public async ValueTask ChaseAsync(MessageEnvelope envelope, IStepTracer? tracer) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/MessageRouter.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/MessageRouter.cs index ae54c397e9..1083c85c41 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/MessageRouter.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/MessageRouter.cs @@ -16,7 +16,7 @@ using MessageHandlerF = namespace Microsoft.Agents.Workflows.Execution; -internal class MessageRouter +internal sealed class MessageRouter { private readonly Dictionary _typedHandlers; private readonly Dictionary _runtimeTypeMap; diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs index 08ef92828b..c29fac8557 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs @@ -5,7 +5,7 @@ using Microsoft.Agents.Workflows.Checkpointing; namespace Microsoft.Agents.Workflows.Execution; -internal class RunnerStateData(HashSet instantiatedExecutors, Dictionary> queuedMessages, List outstandingRequests) +internal sealed class RunnerStateData(HashSet instantiatedExecutors, Dictionary> queuedMessages, List outstandingRequests) { public HashSet InstantiatedExecutors { get; } = instantiatedExecutors; public Dictionary> QueuedMessages { get; } = queuedMessages; diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/StateManager.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/StateManager.cs index c4554fbf7f..4e2b4dbeea 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/StateManager.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/StateManager.cs @@ -9,10 +9,10 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Execution; -internal class StateManager +internal sealed class StateManager { - private readonly Dictionary _scopes = new(); - private readonly Dictionary _queuedUpdates = new(); + private readonly Dictionary _scopes = []; + private readonly Dictionary _queuedUpdates = []; private StateScope GetOrCreateScope(ScopeId scopeId) { @@ -125,15 +125,14 @@ internal class StateManager } public ValueTask WriteStateAsync(string executorId, string? scopeName, string key, T value) - => this.WriteStateAsync(new ScopeId(Throw.IfNullOrEmpty(executorId), scopeName), key, value); + => this.WriteStateAsync(new ScopeId(Throw.IfNullOrEmpty(executorId), scopeName), key, value); public ValueTask WriteStateAsync(ScopeId scopeId, string key, T value) { Throw.IfNullOrEmpty(key); UpdateKey stateKey = new(scopeId, key); - StateUpdate update = StateUpdate.Update(key, value); - this._queuedUpdates[stateKey] = update; + this._queuedUpdates[stateKey] = StateUpdate.Update(key, value); return default; } @@ -169,7 +168,7 @@ internal class StateManager stateUpdates.Add(this._queuedUpdates[key]); } - if (tracer != null && (updatesByScope.Count > 0)) + if (tracer is not null && (updatesByScope.Count > 0)) { tracer.TraceStatePublished(); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/StateScope.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/StateScope.cs index 3fcb754895..a5d4dd5d0f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/StateScope.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/StateScope.cs @@ -8,9 +8,9 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Execution; -internal class StateScope +internal sealed class StateScope { - private readonly Dictionary _stateData = new(); + private readonly Dictionary _stateData = []; public ScopeId ScopeId { get; } public StateScope(ScopeId scopeId) @@ -63,7 +63,7 @@ internal class StateScope foreach (string key in updates.Keys) { - if (updates == null || updates[key].Count == 0) + if (updates is null || updates[key].Count == 0) { continue; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/StateUpdate.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/StateUpdate.cs index 8924e243a2..2b00c7e89d 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/StateUpdate.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/StateUpdate.cs @@ -17,10 +17,7 @@ internal sealed class StateUpdate this.IsDelete = isDelete; } - public static StateUpdate Update(string key, T? value) - { - return new StateUpdate(key, value, value is null); - } + public static StateUpdate Update(string key, T? value) => new(key, value, value is null); public static StateUpdate Delete(string key) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/StepContext.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/StepContext.cs index 53eb22d0e3..5ebdb9af52 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/StepContext.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/StepContext.cs @@ -6,9 +6,9 @@ using Microsoft.Agents.Workflows.Checkpointing; namespace Microsoft.Agents.Workflows.Execution; -internal class StepContext +internal sealed class StepContext { - public Dictionary> QueuedMessages { get; } = new(); + public Dictionary> QueuedMessages { get; } = []; public bool HasMessages => this.QueuedMessages.Values.Any(messageList => messageList.Count > 0); @@ -16,7 +16,7 @@ internal class StepContext { if (!this.QueuedMessages.TryGetValue(executorId, out var messages)) { - this.QueuedMessages[executorId] = messages = new(); + this.QueuedMessages[executorId] = messages = []; } return messages; @@ -29,8 +29,7 @@ internal class StepContext return this.QueuedMessages.Keys.ToDictionary( keySelector: identity => identity, elementSelector: identity => this.QueuedMessages[identity] - .Select(v => new PortableMessageEnvelope(v)) - .ToList() + .ConvertAll(v => new PortableMessageEnvelope(v)) ); } @@ -38,9 +37,9 @@ internal class StepContext { foreach (ExecutorIdentity identity in messages.Keys) { - this.QueuedMessages[identity] = messages[identity].Select(UnwrapExportedState).ToList(); + this.QueuedMessages[identity] = messages[identity].ConvertAll(UnwrapExportedState); } - MessageEnvelope UnwrapExportedState(PortableMessageEnvelope es) => es.ToMessageEnvelope(); + static MessageEnvelope UnwrapExportedState(PortableMessageEnvelope es) => es.ToMessageEnvelope(); } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/UpdateKey.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/UpdateKey.cs index 3772d5b400..ece35fd29f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/UpdateKey.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/UpdateKey.cs @@ -15,7 +15,7 @@ namespace Microsoft.Agents.Workflows.Execution; /// appropriate) and published during a step transition. /// /// -internal class UpdateKey(ScopeId scopeId, string key) +internal sealed class UpdateKey(ScopeId scopeId, string key) { public ScopeId ScopeId { get; } = Throw.IfNull(scopeId); public string Key { get; } = Throw.IfNullOrEmpty(key); @@ -24,15 +24,9 @@ internal class UpdateKey(ScopeId scopeId, string key) : this(new ScopeId(Throw.IfNullOrEmpty(executorId), scopeName), key) { } - public override string ToString() - { - return $"{this.ScopeId}/{this.Key}"; - } + public override string ToString() => $"{this.ScopeId}/{this.Key}"; - public bool IsMatchingScope(ScopeId scopeId, bool strict = false) - { - return this.ScopeId == scopeId && (!strict || this.ScopeId.ExecutorId == scopeId.ExecutorId); - } + public bool IsMatchingScope(ScopeId scopeId, bool strict = false) => this.ScopeId == scopeId && (!strict || this.ScopeId.ExecutorId == scopeId.ExecutorId); public override bool Equals(object? obj) { @@ -46,8 +40,5 @@ internal class UpdateKey(ScopeId scopeId, string key) return false; } - public override int GetHashCode() - { - return HashCode.Combine(this.ScopeId.ExecutorId, this.ScopeId.ScopeName, this.Key); - } + public override int GetHashCode() => HashCode.Combine(this.ScopeId.ExecutorId, this.ScopeId.ScopeName, this.Key); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs index 9635d97174..41f7f8286b 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs @@ -41,12 +41,12 @@ public abstract class Executor : IIdentified /// protected abstract RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder); - private MessageRouter? _router = null; + private MessageRouter? _router; internal MessageRouter Router { get { - if (this._router == null) + if (this._router is null) { RouteBuilder routeBuilder = this.ConfigureRoutes(new RouteBuilder()); this._router = routeBuilder.Build(); @@ -74,7 +74,7 @@ public abstract class Executor : IIdentified .ConfigureAwait(false); ExecutorEvent executionResult; - if (result == null || result.IsSuccess) + if (result?.IsSuccess is not false) { executionResult = new ExecutorCompletedEvent(this.Id, result?.Result); } @@ -85,7 +85,7 @@ public abstract class Executor : IIdentified await context.AddEventAsync(executionResult).ConfigureAwait(false); - if (result == null) + if (result is null) { throw new NotSupportedException( $"No handler found for message type {message.GetType().Name} in executor {this.GetType().Name}."); @@ -102,7 +102,7 @@ public abstract class Executor : IIdentified } // If we had a real return type, raise it as a SendMessage; TODO: Should we have a way to disable this behaviour? - if (result.Result != null && this._options.AutoSendMessageHandlerResultObject) + if (result.Result is not null && this._options.AutoSendMessageHandlerResultObject) { await context.SendMessageAsync(result.Result).ConfigureAwait(false); } @@ -156,10 +156,8 @@ public abstract class Executor(string? id = null, ExecutorOptions? optio : Executor(id, options), IMessageHandler { /// - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler(this.HandleAsync); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler(this.HandleAsync); /// public abstract ValueTask HandleAsync(TInput message, IWorkflowContext context); @@ -177,10 +175,8 @@ public abstract class Executor(string? id = null, ExecutorOptio IMessageHandler { /// - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler(this.HandleAsync); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler(this.HandleAsync); /// public abstract ValueTask HandleAsync(TInput message, IWorkflowContext context); diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorEvent.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorEvent.cs index 9e67c49097..2491419922 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorEvent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorEvent.cs @@ -18,13 +18,8 @@ public class ExecutorEvent(string executorId, object? data) : WorkflowEvent(data public string ExecutorId => executorId; /// - public override string ToString() - { - if (this.Data != null) - { - return $"{this.GetType().Name}(Executor = {this.ExecutorId}, Data: {this.Data.GetType()} = {this.Data})"; - } - - return $"{this.GetType().Name}(Executor = {this.ExecutorId})"; - } + public override string ToString() => + this.Data is not null ? + $"{this.GetType().Name}(Executor = {this.ExecutorId}, Data: {this.Data.GetType()} = {this.Data})" : + $"{this.GetType().Name}(Executor = {this.ExecutorId})"; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs index 53a2fb201f..b834d59d5f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs @@ -39,21 +39,15 @@ public static class ExecutorIshConfigurationExtensions return new ExecutorIsh(configured.Super(), typeof(TExecutor), ExecutorIsh.Type.Executor); } - private static ExecutorIsh ToExecutorIsh(this FunctionExecutor executor, Delegate raw) - { - return new ExecutorIsh(Configured.FromInstance(executor, raw: raw) + private static ExecutorIsh ToExecutorIsh(this FunctionExecutor executor, Delegate raw) => new(Configured.FromInstance(executor, raw: raw) .Super, Executor>(), typeof(FunctionExecutor), ExecutorIsh.Type.Function); - } - private static ExecutorIsh ToExecutorIsh(this FunctionExecutor executor, Delegate raw) - { - return new ExecutorIsh(Configured.FromInstance(executor, raw: raw) + private static ExecutorIsh ToExecutorIsh(this FunctionExecutor executor, Delegate raw) => new(Configured.FromInstance(executor, raw: raw) .Super, Executor>(), typeof(FunctionExecutor), ExecutorIsh.Type.Function); - } /// /// Configures a function-based asynchronous message handler as an executor with the specified identifier and @@ -141,7 +135,7 @@ public sealed class ExecutorIsh : this._idValue = Throw.IfNull(id); } - internal ExecutorIsh(Configured configured, System.Type configuredExecutorType, ExecutorIsh.Type type) + internal ExecutorIsh(Configured configured, System.Type configuredExecutorType, Type type) { this.ExecutorType = type; this._configuredExecutor = configured; @@ -256,73 +250,42 @@ public sealed class ExecutorIsh : /// Defines an implicit conversion from a string to an instance. /// /// The string ID to convert to an . - public static implicit operator ExecutorIsh(string id) - { - return new ExecutorIsh(id); - } + public static implicit operator ExecutorIsh(string id) => new(id); /// - public bool Equals(ExecutorIsh? other) - { - return other is not null && - other.Id == this.Id; - } + public bool Equals(ExecutorIsh? other) => + other is not null && other.Id == this.Id; /// - public bool Equals(IIdentified? other) - { - return other is not null && - other.Id == this.Id; - } + public bool Equals(IIdentified? other) => + other is not null && other.Id == this.Id; /// - public bool Equals(string? other) - { - return other is not null && - other == this.Id; - } + public bool Equals(string? other) => + other is not null && other == this.Id; /// - public override bool Equals(object? obj) - { - if (obj is null) + public override bool Equals(object? obj) => + obj switch { - return false; - } - - if (obj is ExecutorIsh ish) - { - return this.Equals(ish); - } - else if (obj is IIdentified identified) - { - return this.Equals(identified); - } - else if (obj is string str) - { - return this.Equals(str); - } - - return false; - } - - /// - public override int GetHashCode() - { - return this.Id.GetHashCode(); - } - - /// - public override string ToString() - { - return this.ExecutorType switch - { - Type.Unbound => $"'{this.Id}':", - Type.Executor => $"'{this.Id}':{this._configuredExecutorType!.Name}", - Type.InputPort => $"'{this.Id}':Input({this._inputPortValue!.Request.Name}->{this._inputPortValue!.Response.Name})", - Type.Agent => $"{this.Id}':AIAgent(@{this._aiAgentValue!.GetType().Name})", - Type.Function => $"'{this.Id}':{this._configuredExecutorType!.Name}", - _ => $"'{this.Id}':" + null => false, + ExecutorIsh ish => this.Equals(ish), + IIdentified identified => this.Equals(identified), + string str => this.Equals(str), + _ => false }; - } + + /// + public override int GetHashCode() => this.Id.GetHashCode(); + + /// + public override string ToString() => this.ExecutorType switch + { + Type.Unbound => $"'{this.Id}':", + Type.Executor => $"'{this.Id}':{this._configuredExecutorType!.Name}", + Type.InputPort => $"'{this.Id}':Input({this._inputPortValue!.Request.Name}->{this._inputPortValue!.Response.Name})", + Type.Agent => $"{this.Id}':AIAgent(@{this._aiAgentValue!.GetType().Name})", + Type.Function => $"'{this.Id}':{this._configuredExecutorType!.Name}", + _ => $"'{this.Id}':" + }; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs index 76dbba8ed3..1b40c5792d 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs @@ -8,7 +8,7 @@ using ExecutorFactoryF = System.Func(Func /// A synchronous function to execute for each input message and workflow context. public FunctionExecutor(Action handlerSync) : this(WrapAction(handlerSync)) - { } + { + } } /// @@ -61,12 +62,15 @@ public class FunctionExecutor(Func(result); } } + /// public override ValueTask HandleAsync(TInput message, IWorkflowContext context) => handlerAsync(message, context, default); + /// /// Creates a new instance of the class. /// /// A synchronous function to execute for each input message and workflow context. public FunctionExecutor(Func handlerSync) : this(WrapFunc(handlerSync)) - { } + { + } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcStepTracer.cs b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcStepTracer.cs index e126624f8e..7735378cb6 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcStepTracer.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcStepTracer.cs @@ -10,11 +10,11 @@ namespace Microsoft.Agents.Workflows.InProc; internal sealed class InProcStepTracer : IStepTracer { - private int _nextStepNumber = 0; + private int _nextStepNumber; public int StepNumber => this._nextStepNumber - 1; - public bool StateUpdated { get; private set; } = false; - public CheckpointInfo? Checkpoint { get; private set; } = null; + public bool StateUpdated { get; private set; } + public CheckpointInfo? Checkpoint { get; private set; } public HashSet Instantiated { get; } = []; public HashSet Activated { get; } = []; @@ -60,32 +60,33 @@ internal sealed class InProcStepTracer : IStepTracer }); } - public SuperStepCompletedEvent Complete(bool nextStepHasActions, bool hasPendingRequests) + public SuperStepCompletedEvent Complete(bool nextStepHasActions, bool hasPendingRequests) => new(this.StepNumber, new SuperStepCompletionInfo(this.Activated, this.Instantiated) { - return new SuperStepCompletedEvent(this.StepNumber, new SuperStepCompletionInfo(this.Activated, this.Instantiated) - { - HasPendingMessages = nextStepHasActions, - HasPendingRequests = hasPendingRequests, - StateUpdated = this.StateUpdated, - Checkpoint = this.Checkpoint, - }); - } + HasPendingMessages = nextStepHasActions, + HasPendingRequests = hasPendingRequests, + StateUpdated = this.StateUpdated, + Checkpoint = this.Checkpoint, + }); public override string ToString() { StringBuilder sb = new(); + if (this.Instantiated.Count != 0) { - sb.Append("Instantiated: "); - sb.Append(string.Join(", ", this.Instantiated.OrderBy(id => id, StringComparer.Ordinal))); - sb.AppendLine(); + sb.Append("Instantiated: ").Append(string.Join(", ", this.Instantiated.OrderBy(id => id, StringComparer.Ordinal))); } + if (this.Activated.Count != 0) { - sb.Append("Activated: "); - sb.Append(string.Join(", ", this.Activated.OrderBy(id => id, StringComparer.Ordinal))); - sb.AppendLine(); + if (sb.Length != 0) + { + sb.AppendLine(); + } + + sb.Append("Activated: ").Append(string.Join(", ", this.Activated.OrderBy(id => id, StringComparer.Ordinal))); } + return sb.ToString(); } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs index 05d5fa27be..56360ef29b 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs @@ -20,7 +20,7 @@ namespace Microsoft.Agents.Workflows.InProc; /// within the current process, without distributed coordination. It is primarily intended for testing, debugging, or /// scenarios where workflow execution does not require executor distribution. /// The type of input accepted by the workflow. Must be non-nullable. -internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner where TInput : notnull +internal sealed class InProcessRunner : ISuperStepRunner, ICheckpointingRunner where TInput : notnull { public InProcessRunner(Workflow workflow, ICheckpointManager? checkpointManager, string? runId = null) { @@ -61,7 +61,7 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner return false; } - await this.RunContext.AddExternalMessageAsync(message).ConfigureAwait(false); + await this.RunContext.AddExternalMessageAsync(message).ConfigureAwait(false); return true; } @@ -71,7 +71,6 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner } private InProcStepTracer StepTracer { get; } = new(); - private Dictionary PendingCalls { get; } = new(); private Workflow Workflow { get; init; } private InProcessRunnerContext RunContext { get; init; } private ICheckpointManager? CheckpointManager { get; } @@ -90,14 +89,9 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner this.WorkflowEvent?.Invoke(this, workflowEvent); } - private bool IsResponse(object message) - { - return message is ExternalResponse; - } - private ValueTask> RouteExternalMessageAsync(MessageEnvelope envelope) { - Debug.Assert(envelope.TargetId == null, "External Messages cannot be targeted to a specific executor."); + Debug.Assert(envelope.TargetId is null, "External Messages cannot be targeted to a specific executor."); object message = envelope.Message; return message is ExternalResponse response @@ -155,7 +149,6 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner bool ISuperStepRunner.HasUnprocessedMessages => this.RunContext.NextStepHasActions; public IReadOnlyList Checkpoints => this._checkpoints; - private CheckpointInfo? LastCheckpoint => this.Checkpoints[this.Checkpoints.Count - 1]; async ValueTask ISuperStepRunner.RunSuperStepAsync(CancellationToken cancellation) { @@ -190,7 +183,7 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner this.RaiseWorkflowEvent(this.StepTracer.Advance(currentStep)); // Deliver the messages and queue the next step - List>> edgeTasks = new(); + List>> edgeTasks = []; foreach (ExecutorIdentity sender in currentStep.QueuedMessages.Keys) { IEnumerable senderMessages = currentStep.QueuedMessages[sender]; @@ -220,11 +213,11 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner this.RaiseWorkflowEvent(this.StepTracer.Complete(this.RunContext.NextStepHasActions, this.RunContext.HasUnservicedRequests)); } - private WorkflowInfo? _workflowInfoCache = null; + private WorkflowInfo? _workflowInfoCache; private readonly List _checkpoints = []; internal async ValueTask CheckpointAsync(CancellationToken cancellation = default) { - if (this.CheckpointManager == null) + if (this.CheckpointManager is null) { // Always publish the state updates, even in the absence of a CheckpointManager. await this.RunContext.StateManager.PublishUpdatesAsync(this.StepTracer).ConfigureAwait(false); @@ -235,10 +228,7 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner Task prepareTask = this.RunContext.PrepareForCheckpointAsync(cancellation); // Create a representation of the current workflow if it does not already exist. - if (this._workflowInfoCache == null) - { - this._workflowInfoCache = this.Workflow.ToWorkflowInfo(); - } + this._workflowInfoCache ??= this.Workflow.ToWorkflowInfo(); Dictionary edgeData = await this.EdgeMap.ExportStateAsync().ConfigureAwait(false); @@ -284,13 +274,11 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner this.StepTracer.Reload(this.StepTracer.StepNumber); } - protected virtual bool CheckWorkflowMatch(Checkpoint checkpoint) - { - return checkpoint.Workflow.IsMatch(this.Workflow); - } + private bool CheckWorkflowMatch(Checkpoint checkpoint) => + checkpoint.Workflow.IsMatch(this.Workflow); } -internal class InProcessRunner : IRunnerWithOutput, ICheckpointingRunner where TInput : notnull +internal sealed class InProcessRunner : IRunnerWithOutput, ICheckpointingRunner where TInput : notnull { private readonly Workflow _workflow; private readonly InProcessRunner _innerRunner; @@ -299,8 +287,7 @@ internal class InProcessRunner : IRunnerWithOutput, IC { this._workflow = Throw.IfNull(workflow); - InProcessRunner runner = new(workflow, checkpointManager, runId); - this._innerRunner = runner; + this._innerRunner = new(workflow, checkpointManager, runId); } internal async ValueTask> ResumeStreamAsync(CheckpointInfo checkpoint, CancellationToken cancellation = default) diff --git a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs index aec275b2ef..db8a271849 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs @@ -14,7 +14,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.InProc; -internal class InProcessRunnerContext : IRunnerContext +internal sealed class InProcessRunnerContext : IRunnerContext { private StepContext _nextStep = new(); private readonly Dictionary _executorRegistrations; @@ -66,10 +66,7 @@ internal class InProcessRunnerContext : IRunnerContext public bool NextStepHasActions => this._nextStep.HasMessages; public bool HasUnservicedRequests => this._externalRequests.Count > 0; - public StepContext Advance() - { - return Interlocked.Exchange(ref this._nextStep, new StepContext()); - } + public StepContext Advance() => Interlocked.Exchange(ref this._nextStep, new StepContext()); public ValueTask AddEventAsync(WorkflowEvent workflowEvent) { @@ -83,10 +80,7 @@ internal class InProcessRunnerContext : IRunnerContext return default; } - public IWorkflowContext Bind(string executorId) - { - return new BoundContext(this, executorId); - } + public IWorkflowContext Bind(string executorId) => new BoundContext(this, executorId); public ValueTask PostAsync(ExternalRequest request) { @@ -100,7 +94,7 @@ internal class InProcessRunnerContext : IRunnerContext internal StateManager StateManager { get; } = new(); - private class BoundContext(InProcessRunnerContext RunnerContext, string ExecutorId) : IWorkflowContext + private sealed class BoundContext(InProcessRunnerContext RunnerContext, string ExecutorId) : IWorkflowContext { public ValueTask AddEventAsync(WorkflowEvent workflowEvent) => RunnerContext.AddEventAsync(workflowEvent); public ValueTask SendMessageAsync(object message, string? targetId = null) => RunnerContext.SendMessageAsync(ExecutorId, message, targetId); @@ -118,15 +112,9 @@ internal class InProcessRunnerContext : IRunnerContext => RunnerContext.StateManager.ClearStateAsync(ExecutorId, scopeName); } - internal Task PrepareForCheckpointAsync(CancellationToken cancellation = default) - { - return Task.WhenAll(this._executors.Values.Select(executor => executor.OnCheckpointingAsync(this.Bind(executor.Id), cancellation).AsTask())); - } + internal Task PrepareForCheckpointAsync(CancellationToken cancellation = default) => Task.WhenAll(this._executors.Values.Select(executor => executor.OnCheckpointingAsync(this.Bind(executor.Id), cancellation).AsTask())); - internal Task NotifyCheckpointLoadedAsync(CancellationToken cancellationToken = default) - { - return Task.WhenAll(this._executors.Values.Select(executor => executor.OnCheckpointRestoredAsync(this.Bind(executor.Id), cancellationToken).AsTask())); - } + internal Task NotifyCheckpointLoadedAsync(CancellationToken cancellationToken = default) => Task.WhenAll(this._executors.Values.Select(executor => executor.OnCheckpointRestoredAsync(this.Bind(executor.Id), cancellationToken).AsTask())); internal ValueTask ExportStateAsync() { diff --git a/dotnet/src/Microsoft.Agents.Workflows/InputPort.cs b/dotnet/src/Microsoft.Agents.Workflows/InputPort.cs index b5afb2f233..2263591a50 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/InputPort.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/InputPort.cs @@ -10,7 +10,7 @@ namespace Microsoft.Agents.Workflows; /// /// /// -public record InputPort(string Id, Type Request, Type Response) +public sealed record InputPort(string Id, Type Request, Type Response) { /// /// Creates a new instance configured for the specified request and response types. diff --git a/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs b/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs index 52e0df960c..1a7e0c7b63 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs @@ -9,14 +9,14 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows; -internal class MessageMerger +internal sealed class MessageMerger { - private class ResponseMergeState(string? responseId) + private sealed class ResponseMergeState(string? responseId) { public string? ResponseId { get; } = responseId; - public Dictionary> UpdatesByMessageId { get; } = new(); - public List DanglingUpdates { get; } = new(); + public Dictionary> UpdatesByMessageId { get; } = []; + public List DanglingUpdates { get; } = []; public void AddUpdate(AgentRunResponseUpdate update) { @@ -28,15 +28,13 @@ internal class MessageMerger { if (!this.UpdatesByMessageId.TryGetValue(update.MessageId, out List? updates)) { - this.UpdatesByMessageId[update.MessageId] = updates = new List(); + this.UpdatesByMessageId[update.MessageId] = updates = []; } updates.Add(update); } } - private AgentRunResponse ComputeResponse(List updates) => updates.ToAgentRunResponse(); - public AgentRunResponse ComputeMerged(string messageId) { if (this.UpdatesByMessageId.TryGetValue(Throw.IfNull(messageId), out List? updates)) @@ -80,7 +78,7 @@ internal class MessageMerger return updates.Aggregate(null, (ChatMessage? previous, AgentRunResponseUpdate current) => { - return previous == null + return previous is null ? current.ToChatMessage() : previous.UpdateWith(current); })!; @@ -88,7 +86,7 @@ internal class MessageMerger } } - private readonly Dictionary _mergeStates = new(); + private readonly Dictionary _mergeStates = []; private readonly ResponseMergeState _danglingState = new(null); public void AddUpdate(AgentRunResponseUpdate update) @@ -133,8 +131,8 @@ internal class MessageMerger public AgentRunResponse ComputeMerged(string primaryResponseId, string? primaryAgentId = null, string? primaryAgentName = null) { List messages = []; - Dictionary responses = new(); - HashSet agentIds = new(); + Dictionary responses = []; + HashSet agentIds = []; foreach (string responseId in this._mergeStates.Keys) { @@ -153,11 +151,11 @@ internal class MessageMerger UsageDetails? usage = null; AdditionalPropertiesDictionary? additionalProperties = null; - HashSet createdTimes = new(); + HashSet createdTimes = []; foreach (AgentRunResponse response in responses.Values) { - if (response.AgentId != null) + if (response.AgentId is not null) { agentIds.Add(response.AgentId); } @@ -183,7 +181,7 @@ internal class MessageMerger AdditionalProperties = additionalProperties }; - AgentRunResponse MergeResponses(AgentRunResponse? current, AgentRunResponse incoming) + static AgentRunResponse MergeResponses(AgentRunResponse? current, AgentRunResponse incoming) { if (current is null) { @@ -237,12 +235,12 @@ internal class MessageMerger static AdditionalPropertiesDictionary? MergeProperties(AdditionalPropertiesDictionary? current, AdditionalPropertiesDictionary? incoming) { - if (current == null) + if (current is null) { return incoming; } - if (incoming == null) + if (incoming is null) { return current; } @@ -258,22 +256,22 @@ internal class MessageMerger static UsageDetails? MergeUsage(UsageDetails? current, UsageDetails? incoming) { - if (current == null) + if (current is null) { return incoming; } AdditionalPropertiesDictionary? additionalCounts = current.AdditionalCounts; - if (incoming == null) + if (incoming is null) { return current; } - if (additionalCounts == null) + if (additionalCounts is null) { additionalCounts = incoming.AdditionalCounts; } - else if (incoming.AdditionalCounts != null) + else if (incoming.AdditionalCounts is not null) { foreach (string key in incoming.AdditionalCounts.Keys) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/PortableValue.cs b/dotnet/src/Microsoft.Agents.Workflows/PortableValue.cs index ab3fe80db9..623a253fbd 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/PortableValue.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/PortableValue.cs @@ -30,7 +30,7 @@ public sealed class PortableValue /// public override bool Equals(object? obj) { - if (obj == null) + if (obj is null) { return false; } @@ -38,12 +38,12 @@ public sealed class PortableValue if (obj is not PortableValue other) { Type targetType = obj.GetType(); - return this.AsType(targetType)?.Equals(obj) ?? false; + return this.AsType(targetType)?.Equals(obj) is true; } return this.TypeId == other.TypeId - && ((this.Value == null && other.Value == null) - || this.Value != null && this.Value.Equals(other.Value)); + && ((this.Value is null && other.Value is null) + || this.Value?.Equals(other.Value) is true); } /// @@ -75,10 +75,10 @@ public sealed class PortableValue internal bool IsDelayedDeserialization => this.Value is IDelayedDeserialization; [JsonIgnore] - internal bool IsDeserialized => this._deserializedValueCache != null; + internal bool IsDeserialized => this._deserializedValueCache is not null; private readonly object _value; - private object? _deserializedValueCache = null; + private object? _deserializedValueCache; /// /// Gets the raw underlying value represented by this instance. @@ -103,10 +103,7 @@ public sealed class PortableValue { if (this.Value is IDelayedDeserialization delayedDeserialization) { - if (this._deserializedValueCache == null) - { - this._deserializedValueCache = delayedDeserialization.Deserialize(); - } + this._deserializedValueCache ??= delayedDeserialization.Deserialize(); } if (this.Value is TValue typedValue) @@ -172,5 +169,5 @@ public sealed class PortableValue /// /// The type to compare with the current instance. Cannot be null. /// true if the current instance can be assigned to targetType; otherwise, false. - public bool IsType(Type targetType) => this.AsType(targetType) != null; + public bool IsType(Type targetType) => this.AsType(targetType) is not null; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Reflection/MessageHandlerInfo.cs b/dotnet/src/Microsoft.Agents.Workflows/Reflection/MessageHandlerInfo.cs index db5fc9c4b3..df9778f824 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Reflection/MessageHandlerInfo.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Reflection/MessageHandlerInfo.cs @@ -10,13 +10,13 @@ using Microsoft.Agents.Workflows.Execution; namespace Microsoft.Agents.Workflows.Reflection; -internal struct MessageHandlerInfo +internal readonly struct MessageHandlerInfo { public Type InType { get; init; } - public Type? OutType { get; init; } = null; + public Type? OutType { get; init; } public MethodInfo HandlerInfo { get; init; } - public Func>? Unwrapper { get; init; } = null; + public Func>? Unwrapper { get; init; } public MessageHandlerInfo(MethodInfo handlerInfo) { @@ -67,7 +67,7 @@ internal struct MessageHandlerInfo async ValueTask InvokeHandlerAsync(object message, IWorkflowContext workflowContext) { - bool expectingVoid = resultType == null || resultType == typeof(void); + bool expectingVoid = resultType is null || resultType == typeof(void); try { @@ -86,14 +86,14 @@ internal struct MessageHandlerInfo $"{maybeValueTask?.GetType().Name ?? "null"}."); } - Debug.Assert(resultType != null, "Expected resultType to be non-null when not expecting void."); - if (unwrapper == null) + Debug.Assert(resultType is not null, "Expected resultType to be non-null when not expecting void."); + if (unwrapper is null) { throw new InvalidOperationException( $"Handler method is expected to return ValueTask<{resultType!.Name}>, but no unwrapper is available."); } - if (maybeValueTask == null) + if (maybeValueTask is null) { throw new InvalidOperationException( $"Handler method returned null, but a ValueTask<{resultType!.Name}> was expected."); @@ -101,7 +101,7 @@ internal struct MessageHandlerInfo object? result = await unwrapper(maybeValueTask).ConfigureAwait(false); - if (checkType && result != null && !resultType.IsInstanceOfType(result)) + if (checkType && result is not null && !resultType.IsInstanceOfType(result)) { throw new InvalidOperationException( $"Handler method returned an incompatible type: expected {resultType.Name}, got {result.GetType().Name}."); @@ -126,11 +126,11 @@ internal struct MessageHandlerInfo where TExecutor : ReflectingExecutor { MethodInfo handlerMethod = this.HandlerInfo; - return MessageHandlerInfo.Bind(InvokeHandler, checkType, this.OutType, this.Unwrapper); + return Bind(InvokeHandler, checkType, this.OutType, this.Unwrapper); object? InvokeHandler(object message, IWorkflowContext workflowContext) { - return handlerMethod.Invoke(executor, new object[] { message, workflowContext }); + return handlerMethod.Invoke(executor, [message, workflowContext]); } } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs index f5854bf29d..388bc46c28 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs @@ -16,13 +16,12 @@ public class ReflectingExecutor< ] TExecutor > : Executor where TExecutor : ReflectingExecutor { - /// + /// protected ReflectingExecutor(string? id = null, ExecutorOptions? options = null) : base(id, options) - { } + { + } /// - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.ReflectHandlers(this); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.ReflectHandlers(this); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Reflection/RouteBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows/Reflection/RouteBuilderExtensions.cs index 9489a8fb9a..ca0b9afce3 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Reflection/RouteBuilderExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Reflection/RouteBuilderExtensions.cs @@ -15,7 +15,7 @@ internal static class IMessageHandlerReflection internal static readonly MethodInfo HandleAsync_1 = typeof(IMessageHandler<>).GetMethod(Nameof_HandleAsync, BindingFlags.Public | BindingFlags.Instance)!; internal static readonly MethodInfo HandleAsync_2 = typeof(IMessageHandler<,>).GetMethod(Nameof_HandleAsync, BindingFlags.Public | BindingFlags.Instance)!; - internal static MethodInfo ReflectHandleAsync(this Type specializedType, int genericArgumentCount) + internal static MethodInfo ReflectHandle(this Type specializedType, int genericArgumentCount) { Debug.Assert(specializedType.IsGenericType && (specializedType.GetGenericTypeDefinition() == typeof(IMessageHandler<>) || @@ -60,16 +60,16 @@ internal static class RouteBuilderExtensions // Get the generic arguments of the interface. Type[] genericArguments = interfaceType.GetGenericArguments(); - if (genericArguments.Length < 1 || genericArguments.Length > 2) + if (genericArguments.Length is < 1 or > 2) { continue; // Invalid handler signature. } Type inType = genericArguments[0]; Type? outType = genericArguments.Length == 2 ? genericArguments[1] : null; - MethodInfo? method = interfaceType.ReflectHandleAsync(genericArguments.Length); + MethodInfo? method = interfaceType.ReflectHandle(genericArguments.Length); - if (method != null) + if (method is not null) { yield return new MessageHandlerInfo(method) { InType = inType, OutType = outType }; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Reflection/ValueTaskTypeErasure.cs b/dotnet/src/Microsoft.Agents.Workflows/Reflection/ValueTaskTypeErasure.cs index 19eb5fd359..8925baa89f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Reflection/ValueTaskTypeErasure.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Reflection/ValueTaskTypeErasure.cs @@ -68,9 +68,7 @@ internal static class ValueTaskTypeErasure Task task = (Task)asTaskMethod.ReflectionInvoke(maybeGenericVT)!; await task.ConfigureAwait(false); // TODO: Could we need to capture the context here? - object? result = getResultMethod.ReflectionInvoke(task); - - return result; + return getResultMethod.ReflectionInvoke(task); } } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/RouteBuilder.cs b/dotnet/src/Microsoft.Agents.Workflows/RouteBuilder.cs index ffc0467a7c..193a9ac0cc 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/RouteBuilder.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/RouteBuilder.cs @@ -23,7 +23,7 @@ namespace Microsoft.Agents.Workflows; /// public class RouteBuilder { - private readonly Dictionary _typedHandlers = new(); + private readonly Dictionary _typedHandlers = []; internal RouteBuilder AddHandler(Type messageType, MessageHandlerF handler, bool overwrite = false) { @@ -126,8 +126,5 @@ public class RouteBuilder } } - internal MessageRouter Build() - { - return new MessageRouter(this._typedHandlers); - } + internal MessageRouter Build() => new(this._typedHandlers); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/Run.cs b/dotnet/src/Microsoft.Agents.Workflows/Run.cs index 93e031c10f..8435a7658b 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Run.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Run.cs @@ -46,7 +46,7 @@ public class Run return result; } - private readonly List _eventSink = new(); + private readonly List _eventSink = []; private readonly StreamingRun _streamingRun; internal Run(StreamingRun streamingRun) { @@ -91,7 +91,7 @@ public class Run /// public IEnumerable OutgoingEvents => this._eventSink; - private int _lastBookmark = 0; + private int _lastBookmark; /// /// Gets all events emitted by the workflow since the last access to . diff --git a/dotnet/src/Microsoft.Agents.Workflows/ScopeId.cs b/dotnet/src/Microsoft.Agents.Workflows/ScopeId.cs index 3f4c64354b..42e45ab30a 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ScopeId.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ScopeId.cs @@ -12,7 +12,7 @@ namespace Microsoft.Agents.Workflows; /// The unique identifier for the executor associated with this ScopeId. /// The name of the scope, if any. If , this ScopeId /// corresponds to the Executor's private scope. -public class ScopeId(string executorId, string? scopeName = null) +public sealed class ScopeId(string executorId, string? scopeName = null) { /// /// Gets the unique identifier of the executor. @@ -25,10 +25,7 @@ public class ScopeId(string executorId, string? scopeName = null) public string? ScopeName { get; } = scopeName; /// - public override string ToString() - { - return $"{this.ExecutorId}/{this.ScopeName ?? "default"}"; - } + public override string ToString() => $"{this.ExecutorId}/{this.ScopeName ?? "default"}"; /// public override bool Equals(object? obj) @@ -39,14 +36,13 @@ public class ScopeId(string executorId, string? scopeName = null) { return this.ExecutorId == other.ExecutorId; } - else if (other.ScopeName is not null && this.ScopeName is not null) + + if (other.ScopeName is not null && this.ScopeName is not null) { return this.ScopeName == other.ScopeName; } - else - { - return false; // One has a scope name, the other does not. - } + + // One has a scope name, the other does not. } return false; @@ -55,7 +51,7 @@ public class ScopeId(string executorId, string? scopeName = null) /// public static bool operator ==(ScopeId? left, ScopeId? right) { - if (left is null && right == null) + if (left is null && right is null) { return true; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/ScopeKey.cs b/dotnet/src/Microsoft.Agents.Workflows/ScopeKey.cs index 68a3af93ca..9e9305557c 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ScopeKey.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ScopeKey.cs @@ -9,7 +9,7 @@ namespace Microsoft.Agents.Workflows; /// /// Represents a unique key within a specific scope, combining a scope identifier and a key string. /// -public class ScopeKey +public sealed class ScopeKey { /// /// The identifier for the scope associated with this key. diff --git a/dotnet/src/Microsoft.Agents.Workflows/Specialized/AIAgentHostExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/Specialized/AIAgentHostExecutor.cs index ea93b001a3..6de8204117 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Specialized/AIAgentHostExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Specialized/AIAgentHostExecutor.cs @@ -9,12 +9,12 @@ using Microsoft.Extensions.AI.Agents; namespace Microsoft.Agents.Workflows.Specialized; -internal class AIAgentHostExecutor : Executor +internal sealed class AIAgentHostExecutor : Executor { private readonly bool _emitEvents; private readonly AIAgent _agent; - private readonly List _pendingMessages = new(); - private AgentThread? _thread = null; + private readonly List _pendingMessages = []; + private AgentThread? _thread; public AIAgentHostExecutor(AIAgent agent, bool emitEvents = false) : base(id: agent.Id) { @@ -22,22 +22,13 @@ internal class AIAgentHostExecutor : Executor this._emitEvents = emitEvents; } - private AgentThread EnsureThread(IWorkflowContext context) - { - if (this._thread != null) - { - return this._thread; - } + private AgentThread EnsureThread(IWorkflowContext context) => + this._thread ??= this._agent.GetNewThread(); - return this._thread = this._agent.GetNewThread(); - } - - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler(this.QueueMessageAsync) - .AddHandler>(this.QueueMessagesAsync) - .AddHandler(this.TakeTurnAsync); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler(this.QueueMessageAsync) + .AddHandler>(this.QueueMessagesAsync) + .AddHandler(this.TakeTurnAsync); public ValueTask QueueMessagesAsync(List messages, IWorkflowContext context) { @@ -51,12 +42,12 @@ internal class AIAgentHostExecutor : Executor return default; } - private const string ThreadStateKey = nameof(AIAgentHostExecutor._thread); - private const string PendingMessagesStateKey = nameof(AIAgentHostExecutor._pendingMessages); + private const string ThreadStateKey = nameof(_thread); + private const string PendingMessagesStateKey = nameof(_pendingMessages); protected internal override async ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) { Task threadTask = Task.CompletedTask; - if (this._thread != null) + if (this._thread is not null) { JsonElement threadValue = await this._thread.SerializeAsync(cancellationToken: cancellation).ConfigureAwait(false); threadTask = context.QueueStateUpdateAsync(ThreadStateKey, threadValue).AsTask(); @@ -90,10 +81,10 @@ internal class AIAgentHostExecutor : Executor public async ValueTask TakeTurnAsync(TurnToken token, IWorkflowContext context) { - bool emitEvents = token.EmitEvents.HasValue ? token.EmitEvents.Value : this._emitEvents; + bool emitEvents = token.EmitEvents ?? this._emitEvents; IAsyncEnumerable agentStream = this._agent.RunStreamingAsync(this._pendingMessages, this.EnsureThread(context)); - List updates = new(); + List updates = []; ChatMessage? currentStreamingMessage = null; await foreach (AgentRunResponseUpdate update in agentStream.ConfigureAwait(false)) @@ -114,7 +105,7 @@ internal class AIAgentHostExecutor : Executor // providing some mechanisms to help the user complete the request, or route it out of the // workflow. - if (currentStreamingMessage == null || currentStreamingMessage.MessageId != update.MessageId) + if (currentStreamingMessage is null || currentStreamingMessage.MessageId != update.MessageId) { await PublishCurrentMessageAsync().ConfigureAwait(false); currentStreamingMessage = new(update.Role ?? ChatRole.Assistant, update.Contents) @@ -135,7 +126,7 @@ internal class AIAgentHostExecutor : Executor async ValueTask PublishCurrentMessageAsync() { - if (currentStreamingMessage != null) + if (currentStreamingMessage is not null) { currentStreamingMessage.Contents = updates; updates = []; diff --git a/dotnet/src/Microsoft.Agents.Workflows/Specialized/OutputCollectorExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/Specialized/OutputCollectorExecutor.cs index dc814529d3..3f6c1b241a 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Specialized/OutputCollectorExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Specialized/OutputCollectorExecutor.cs @@ -6,7 +6,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Specialized; -internal class OutputCollectorExecutor : Executor, IOutputSink +internal sealed class OutputCollectorExecutor : Executor, IOutputSink { private readonly StreamingAggregator _aggregator; private readonly Func? _completionCondition; @@ -19,10 +19,8 @@ internal class OutputCollectorExecutor : Executor, IOutputSink< this._completionCondition = completionCondition; } - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler(this.HandleAsync); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler(this.HandleAsync); public ValueTask HandleAsync(TInput message, IWorkflowContext context) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs index 1ae0fdeb01..29af39966a 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs @@ -7,7 +7,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows.Specialized; -internal class RequestInfoExecutor : Executor +internal sealed class RequestInfoExecutor : Executor { private InputPort Port { get; } private IExternalRequestSink? RequestSink { get; set; } @@ -20,7 +20,7 @@ internal class RequestInfoExecutor : Executor }; private readonly bool _allowWrapped; - public RequestInfoExecutor(InputPort port, bool allowWrapped = true) : base(port.Id, RequestInfoExecutor.DefaultOptions) + public RequestInfoExecutor(InputPort port, bool allowWrapped = true) : base(port.Id, DefaultOptions) { this.Port = port; @@ -30,25 +30,22 @@ internal class RequestInfoExecutor : Executor protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) { routeBuilder = routeBuilder - // Handle incoming requests (as raw request payloads) - .AddHandler(this.Port.Request, this.HandleAsync) - .AddHandler(typeof(object), this.HandleAsync); + // Handle incoming requests (as raw request payloads) + .AddHandler(this.Port.Request, this.HandleAsync) + .AddHandler(typeof(object), this.HandleAsync); if (this._allowWrapped) { routeBuilder = routeBuilder - .AddHandler((request, context) => this.HandleAsync(request.Data, context)); + .AddHandler((request, context) => this.HandleAsync(request.Data, context)); } return routeBuilder - // Handle incoming responses (as wrapped Response object) - .AddHandler(this.HandleAsync); + // Handle incoming responses (as wrapped Response object) + .AddHandler(this.HandleAsync); } - internal void AttachRequestSink(IExternalRequestSink requestSink) - { - this.RequestSink = Throw.IfNull(requestSink); - } + internal void AttachRequestSink(IExternalRequestSink requestSink) => this.RequestSink = Throw.IfNull(requestSink); public async ValueTask HandleAsync(object message, IWorkflowContext context) { @@ -65,13 +62,9 @@ internal class RequestInfoExecutor : Executor Throw.IfNull(message); Throw.IfNull(message.Data); - object? data = message.DataAs(this.Port.Response); - - if (data == null) - { + object data = message.DataAs(this.Port.Response) ?? throw new InvalidOperationException( $"Message type {message.Data.TypeId} is not assignable to the response type {this.Port.Response.Name} of input port {this.Port.Id}."); - } await context.SendMessageAsync(message).ConfigureAwait(false); await context.SendMessageAsync(data).ConfigureAwait(false); diff --git a/dotnet/src/Microsoft.Agents.Workflows/Specialized/WorkflowJsonUtilities.cs b/dotnet/src/Microsoft.Agents.Workflows/Specialized/WorkflowJsonUtilities.cs index 9ea4d5c864..5bef957b29 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Specialized/WorkflowJsonUtilities.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Specialized/WorkflowJsonUtilities.cs @@ -16,16 +16,12 @@ internal static partial class WorkflowJsonUtilities [JsonSerializable(typeof(List))] internal sealed partial class WorkflowJsonContext : JsonSerializerContext; - public static JsonElement SerializeToJson(this List messages) - { - return JsonSerializer.SerializeToElement(messages, Default.ListChatMessage); - } + public static JsonElement SerializeToJson(this List messages) => + JsonSerializer.SerializeToElement(messages, Default.ListChatMessage); public static JsonElement SerializeToJson(this IEnumerable messages) => messages.ToList().SerializeToJson(); - public static List DeserializeMessageList(this JsonElement element) - { - return element.Deserialize>(Default.ListChatMessage) ?? []; - } + public static List DeserializeMessageList(this JsonElement element) => + element.Deserialize(Default.ListChatMessage) ?? []; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/StreamingAggregators.cs b/dotnet/src/Microsoft.Agents.Workflows/StreamingAggregators.cs index e995f273d9..cf62b7e64b 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/StreamingAggregators.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/StreamingAggregators.cs @@ -104,7 +104,7 @@ public static class StreamingAggregators IEnumerable Aggregate(TInput input, IEnumerable? runningResult) { - return runningResult != null ? runningResult.Append(conversion(input)) : [conversion(input)]; + return runningResult is not null ? runningResult.Append(conversion(input)) : [conversion(input)]; } } @@ -120,9 +120,9 @@ public static class StreamingAggregators { return Aggregate; - IEnumerable Aggregate(TInput input, IEnumerable? runningResult) + static IEnumerable Aggregate(TInput input, IEnumerable? runningResult) { - return runningResult != null ? runningResult.Append(input) : new[] { input }; + return runningResult is not null ? runningResult.Append(input) : [input]; } } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/StreamingRun.cs b/dotnet/src/Microsoft.Agents.Workflows/StreamingRun.cs index f5c6943148..2c27c1bccc 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/StreamingRun.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/StreamingRun.cs @@ -17,7 +17,7 @@ namespace Microsoft.Agents.Workflows; /// public class StreamingRun { - private TaskCompletionSource? _waitForResponseSource = null; + private TaskCompletionSource? _waitForResponseSource; private readonly ISuperStepRunner _stepRunner; /// @@ -86,7 +86,7 @@ public class StreamingRun bool blockOnPendingRequest, [EnumeratorCancellation] CancellationToken cancellation = default) { - List eventSink = new(); + List eventSink = []; this._stepRunner.WorkflowEvent += OnWorkflowEvent; @@ -102,8 +102,7 @@ public class StreamingRun } bool hadCompletionEvent = false; - List outputEvents = Interlocked.Exchange(ref eventSink, new()); - foreach (WorkflowEvent raisedEvent in outputEvents) + foreach (WorkflowEvent raisedEvent in Interlocked.Exchange(ref eventSink, [])) { yield return raisedEvent; @@ -132,15 +131,9 @@ public class StreamingRun !this._stepRunner.HasUnprocessedMessages && this._stepRunner.HasUnservicedRequests) { - if (this._waitForResponseSource == null) - { - this._waitForResponseSource = new(); - } + this._waitForResponseSource ??= new(); - using CancellationTokenRegistration registration = cancellation.Register(() => - { - this._waitForResponseSource?.SetResult(new()); - }); + using CancellationTokenRegistration registration = cancellation.Register(() => this._waitForResponseSource?.SetResult(new())); await this._waitForResponseSource.Task.ConfigureAwait(false); this._waitForResponseSource = null; @@ -203,7 +196,7 @@ public static class StreamingRunExtensions await foreach (WorkflowEvent @event in handle.WatchStreamAsync(cancellation).ConfigureAwait(false)) { ExternalResponse? maybeResponse = eventCallback?.Invoke(@event); - if (maybeResponse != null) + if (maybeResponse is not null) { await handle.SendResponseAsync(maybeResponse).ConfigureAwait(false); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/SuperStepCompletionInfo.cs b/dotnet/src/Microsoft.Agents.Workflows/SuperStepCompletionInfo.cs index 53b235809b..582823a699 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/SuperStepCompletionInfo.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/SuperStepCompletionInfo.cs @@ -40,5 +40,5 @@ public sealed class SuperStepCompletionInfo(HashSet activatedExecutors, /// Gets the corresponding to the checkpoint created at the end of this SuperStep. /// if checkpointing was not enabled when the run was started. /// - public CheckpointInfo? Checkpoint { get; init; } = null; + public CheckpointInfo? Checkpoint { get; init; } } diff --git a/dotnet/src/Microsoft.Agents.Workflows/SuperStepEvent.cs b/dotnet/src/Microsoft.Agents.Workflows/SuperStepEvent.cs index 53c3dadce9..54afdc2fd1 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/SuperStepEvent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/SuperStepEvent.cs @@ -17,13 +17,8 @@ public class SuperStepEvent(int stepNumber, object? data = null) : WorkflowEvent public int StepNumber => stepNumber; /// - public override string ToString() - { - if (this.Data != null) - { - return $"{this.GetType().Name}(Step = {this.StepNumber}, Data: {this.Data.GetType()} = {this.Data})"; - } - - return $"{this.GetType().Name}(Step = {this.StepNumber})"; - } + public override string ToString() => + this.Data is not null ? + $"{this.GetType().Name}(Step = {this.StepNumber}, Data: {this.Data.GetType()} = {this.Data})" : + $"{this.GetType().Name}(Step = {this.StepNumber})"; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/TurnToken.cs b/dotnet/src/Microsoft.Agents.Workflows/TurnToken.cs index f6448b64a1..2c8e494fe8 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/TurnToken.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/TurnToken.cs @@ -6,7 +6,7 @@ namespace Microsoft.Agents.Workflows; /// /// Sent to an -based executor to request -/// a response to accumulated . +/// a response to accumulated . /// /// Whether to raise AgentRunEvents for this executor. public class TurnToken(bool? emitEvents = null) diff --git a/dotnet/src/Microsoft.Agents.Workflows/Workflow.cs b/dotnet/src/Microsoft.Agents.Workflows/Workflow.cs index d7af9497b8..6f64f9bcf9 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Workflow.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Workflow.cs @@ -17,9 +17,9 @@ public class Workflow /// /// A dictionary of executor providers, keyed by executor ID. /// - internal Dictionary Registrations { get; init; } = new(); + internal Dictionary Registrations { get; init; } = []; - internal Dictionary> Edges { get; init; } = new(); + internal Dictionary> Edges { get; init; } = []; /// /// Gets the collection of edges grouped by their source node identifier. @@ -32,7 +32,7 @@ public class Workflow ); } - internal Dictionary Ports { get; init; } = new(); + internal Dictionary Ports { get; init; } = []; /// /// Gets the collection of external request ports, keyed by their ID. diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs index 2c29173741..8c2af412e4 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs @@ -25,12 +25,12 @@ public class WorkflowBuilder public override string ToString() => $"{this.SourceId} -> {this.TargetId}"; } - private int _edgeCount = 0; - private readonly Dictionary _executors = new(); - private readonly Dictionary> _edges = new(); - private readonly HashSet _unboundExecutors = new(); - private readonly HashSet _conditionlessConnections = new(); - private readonly Dictionary _inputPorts = new(); + private int _edgeCount; + private readonly Dictionary _executors = []; + private readonly Dictionary> _edges = []; + private readonly HashSet _unboundExecutors = []; + private readonly HashSet _conditionlessConnections = []; + private readonly Dictionary _inputPorts = []; private readonly string _startExecutorId; @@ -65,8 +65,8 @@ public class WorkflowBuilder $"Cannot bind executor with ID '{executorish.Id}' because an executor with the same ID but a different type ({existing.ExecutorType.Name} vs {incoming.ExecutorType.Name}) is already bound."); } - if (existing.RawExecutorishData != null && - !object.ReferenceEquals(existing.RawExecutorishData, incoming.RawExecutorishData)) + if (existing.RawExecutorishData is not null && + !ReferenceEquals(existing.RawExecutorishData, incoming.RawExecutorishData)) { throw new InvalidOperationException( $"Cannot bind executor with ID '{executorish.Id}' because an executor with the same ID but different instance is already bound."); @@ -116,7 +116,7 @@ public class WorkflowBuilder // If it does not exist, create a new one. if (!this._edges.TryGetValue(sourceId, out HashSet? edges)) { - this._edges[sourceId] = edges = new HashSet(); + this._edges[sourceId] = edges = []; } return edges; @@ -136,7 +136,7 @@ public class WorkflowBuilder internal static Func? CreateConditionFunc(Func? condition) { - if (condition == null) + if (condition is null) { return null; } @@ -152,7 +152,7 @@ public class WorkflowBuilder internal static Func? CreateConditionFunc(Func? condition) { - if (condition == null) + if (condition is null) { return null; } @@ -188,7 +188,7 @@ public class WorkflowBuilder Throw.IfNull(target); EdgeConnection connection = new(source.Id, target.Id); - if (condition == null && this._conditionlessConnections.Contains(connection)) + if (condition is null && this._conditionlessConnections.Contains(connection)) { throw new InvalidOperationException( $"An edge from '{source.Id}' to '{target.Id}' already exists without a condition. " + @@ -216,7 +216,7 @@ public class WorkflowBuilder internal static Func>? CreateEdgeAssignerFunc(Func>? partitioner) { - if (partitioner == null) + if (partitioner is null) { return null; } @@ -252,7 +252,7 @@ public class WorkflowBuilder this.Track(source).Id, targets.Select(target => this.Track(target).Id).ToList(), this.TakeEdgeId(), - CreateEdgeAssignerFunc(partitioner)); + CreateEdgeAssignerFunc(partitioner)); this.EnsureEdgesFor(source.Id).Add(new(fanOutEdge)); @@ -302,9 +302,9 @@ public class WorkflowBuilder var culture = System.Globalization.CultureInfo.CurrentCulture; var uiCulture = System.Globalization.CultureInfo.CurrentUICulture; - return factory.StartNew(PropagateCultureAndInvoke).Unwrap().GetAwaiter().GetResult(); + return factory.StartNew(PropagateCultureAndInvokeAsync).Unwrap().GetAwaiter().GetResult(); - Task PropagateCultureAndInvoke() + Task PropagateCultureAndInvokeAsync() { // Set the culture and UI culture to the captured values System.Globalization.CultureInfo.CurrentCulture = culture; diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilderExtensions.cs index 4c2c547a2e..d13968fb6c 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilderExtensions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilderExtensions.cs @@ -35,15 +35,11 @@ public static class WorkflowBuilderExtensions return builder.AddEdge(source, executors[0], predicate); } - return builder.AddSwitch(source, - (switch_) => - { - switch_.AddCase(predicate, executors); - }); + return builder.AddSwitch(source, (switch_) => switch_.AddCase(predicate, executors)); // The reason we can check for "not null" here is that CreateConditionFunc will do the correct unwrapping // logic for PortableValues. - bool IsAllowedType(object? message) => message is not null; + static bool IsAllowedType(object? message) => message is not null; } /// @@ -65,15 +61,11 @@ public static class WorkflowBuilderExtensions return builder.AddEdge(source, executors[0], predicate); } - return builder.AddSwitch(source, - (switch_) => - { - switch_.AddCase(predicate, executors); - }); + return builder.AddSwitch(source, (switch_) => switch_.AddCase(predicate, executors)); // The reason we can check for "null" here is that CreateConditionFunc will do the correct unwrapping // logic for PortableValues. - bool IsAllowedType(object? message) => message is null; + static bool IsAllowedType(object? message) => message is null; } /// @@ -92,8 +84,7 @@ public static class WorkflowBuilderExtensions Throw.IfNull(builder); Throw.IfNull(source); - HashSet seenExecutors = new(); - seenExecutors.Add(source.Id); + HashSet seenExecutors = [source.Id]; for (int i = 0; i < executors.Length; i++) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowEvent.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowEvent.cs index b7f91a67ee..0bc33e0739 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowEvent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowEvent.cs @@ -22,13 +22,8 @@ public class WorkflowEvent(object? data = null) public object? Data => data; /// - public override string ToString() - { - if (this.Data != null) - { - return $"{this.GetType().Name}(Data: {this.Data.GetType()} = {this.Data})"; - } - - return $"{this.GetType().Name}()"; - } + public override string ToString() => + this.Data is not null ? + $"{this.GetType().Name}(Data: {this.Data.GetType()} = {this.Data})" : + $"{this.GetType().Name}()"; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs index 53d059522d..091260946f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs @@ -15,23 +15,23 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows; -internal class WorkflowHostAgent : AIAgent +internal sealed class WorkflowHostAgent : AIAgent { private readonly Workflow> _workflow; - private readonly string? _id, _name; + private readonly string? _id; - private readonly ConcurrentDictionary _assignedRunIds = new(); - private readonly Dictionary _runningWorkflows = new(); + private readonly ConcurrentDictionary _assignedRunIds = []; + private readonly Dictionary _runningWorkflows = []; public WorkflowHostAgent(Workflow> workflow, string? id = null, string? name = null) { this._workflow = Throw.IfNull(workflow, nameof(workflow)); this._id = id; - this._name = name; + this.Name = name; } - public override string? Name => this._name; + public override string? Name { get; } public override string Id => this._id ?? base.Id; private string GenerateNewId() @@ -65,7 +65,7 @@ internal class WorkflowHostAgent : AIAgent // in the case of new threads. if (!this._runningWorkflows.TryGetValue(runId, out StreamingRun? run)) { - run = await InProcessExecution.StreamAsync>(this._workflow, messages, cancellation) + run = await InProcessExecution.StreamAsync(this._workflow, messages, cancellation) .ConfigureAwait(false); this._runningWorkflows[runId] = run; } @@ -102,10 +102,7 @@ internal class WorkflowHostAgent : AIAgent private async ValueTask UpdateThreadAsync(IEnumerable messages, AgentThread? thread = null, CancellationToken cancellation = default) { - if (thread is null) - { - thread = this.GetNewThread(); - } + thread ??= this.GetNewThread(); if (thread is not WorkflowThread workflowThread) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs index 07e2ec965c..f83507c948 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs @@ -10,10 +10,10 @@ using Microsoft.Extensions.AI.Agents; namespace Microsoft.Agents.Workflows; -internal class WorkflowMessageStore : IChatMessageStore +internal sealed class WorkflowMessageStore : IChatMessageStore { - private int _bookmark = 0; - private readonly List _chatMessages = new(); + private int _bookmark; + private readonly List _chatMessages = []; public WorkflowMessageStore() { @@ -21,14 +21,13 @@ internal class WorkflowMessageStore : IChatMessageStore public WorkflowMessageStore(JsonElement serializedStoreState, JsonSerializerOptions? jsonSerializerOptions = null) { - if (serializedStoreState.ValueKind != JsonValueKind.Object) + if (serializedStoreState.ValueKind is not JsonValueKind.Object) { throw new ArgumentException("The provided JsonElement must be a json object", nameof(serializedStoreState)); } StoreState? state = - JsonSerializer.Deserialize( - serializedStoreState, + serializedStoreState.Deserialize( AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(StoreState))) as StoreState; if (state?.Messages is not null) @@ -39,16 +38,13 @@ internal class WorkflowMessageStore : IChatMessageStore this._bookmark = state?.Bookmark ?? 0; } - internal class StoreState + internal sealed class StoreState { public int Bookmark { get; set; } - public IList Messages { get; set; } = new List(); + public IList Messages { get; set; } = []; } - internal void AddMessages(params ChatMessage[] messages) - { - this._chatMessages.AddRange(messages); - } + internal void AddMessages(params ChatMessage[] messages) => this._chatMessages.AddRange(messages); public Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken) { @@ -57,10 +53,7 @@ internal class WorkflowMessageStore : IChatMessageStore return Task.CompletedTask; } - public Task> GetMessagesAsync(CancellationToken cancellationToken) - { - return Task.FromResult>(this._chatMessages.AsReadOnly()); - } + public Task> GetMessagesAsync(CancellationToken cancellationToken) => Task.FromResult>(this._chatMessages.AsReadOnly()); public IEnumerable GetFromBookmark() { @@ -70,10 +63,7 @@ internal class WorkflowMessageStore : IChatMessageStore } } - public void UpdateBookmark() - { - this._bookmark = this._chatMessages.Count; - } + public void UpdateBookmark() => this._bookmark = this._chatMessages.Count; public ValueTask SerializeStateAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowThread.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowThread.cs index f61e0e63f5..bbfb3302f5 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowThread.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowThread.cs @@ -10,19 +10,12 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows; -internal class WorkflowThread : AgentThread +internal sealed class WorkflowThread : AgentThread { - private readonly string _workflowId; - private readonly string? _workflowName; - private readonly WorkflowMessageStore _messageStore; - public WorkflowThread(string workflowId, string? workflowName, string runId) { - base.MessageStore = this._messageStore = new(); + base.MessageStore = this.MessageStore = new(); this.RunId = Throw.IfNullOrEmpty(runId, nameof(runId)); - - this._workflowId = Throw.IfNullOrEmpty(workflowId); - this._workflowName = workflowName; } public WorkflowThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) @@ -31,14 +24,11 @@ internal class WorkflowThread : AgentThread } public string RunId { get; } - public int Halts { get; } = 0; + public int Halts { get; } public string ResponseId => $"{this.RunId}@{this.Halts}"; - public override Task SerializeAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException("Pending Checkpointing work."); - } + public override Task SerializeAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException("Pending Checkpointing work."); public AgentRunResponseUpdate CreateUpdate(params AIContent[] parts) { @@ -56,5 +46,5 @@ internal class WorkflowThread : AgentThread } /// - public new WorkflowMessageStore MessageStore => this._messageStore; + public new WorkflowMessageStore MessageStore { get; } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs index 86f273bc0e..fa41e071b1 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs @@ -38,12 +38,12 @@ public sealed class A2AHostAgent /// /// The associated /// - public AIAgent? Agent { get; private set; } + public AIAgent? Agent { get; } /// /// The associated /// - public TaskManager? TaskManager => this._taskManager; + public TaskManager? TaskManager { get; private set; } /// /// Attach the to the provided @@ -53,7 +53,7 @@ public sealed class A2AHostAgent { Throw.IfNull(taskManager); - this._taskManager = taskManager; + this.TaskManager = taskManager; taskManager.OnMessageReceived = this.OnMessageReceivedAsync; taskManager.OnAgentCardQuery = this.GetAgentCardAsync; } @@ -68,7 +68,7 @@ public sealed class A2AHostAgent Throw.IfNull(messageSend); Throw.IfNull(this.Agent); - if (this._taskManager is null) + if (this.TaskManager is null) { throw new InvalidOperationException("TaskManager must be attached before handling an agent message."); } @@ -107,6 +107,5 @@ public sealed class A2AHostAgent #region private private readonly AgentCard _agentCard; - private TaskManager? _taskManager; #endregion } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AClientExtensions.cs index 730d1626b7..37bd78d233 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AClientExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AClientExtensions.cs @@ -35,8 +35,6 @@ public static class A2AClientExtensions /// The display name of the agent. /// Optional logger factory for enabling logging within the agent. /// An instance backed by the A2A agent. - public static AIAgent GetAIAgent(this A2AClient client, string? id = null, string? name = null, string? description = null, string? displayName = null, ILoggerFactory? loggerFactory = null) - { - return new A2AAgent(client, id, name, description, displayName, loggerFactory); - } + public static AIAgent GetAIAgent(this A2AClient client, string? id = null, string? name = null, string? description = null, string? displayName = null, ILoggerFactory? loggerFactory = null) => + new A2AAgent(client, id, name, description, displayName, loggerFactory); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs index 4e14b07ff5..e209a6c6ea 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs @@ -15,21 +15,21 @@ internal static class A2APartExtensions /// /// The A2A part to convert. /// The corresponding . - internal static AIContent ToAIContent(this Part part) - { - return part switch + internal static AIContent ToAIContent(this Part part) => + part switch { TextPart textPart => new TextContent(textPart.Text) { RawRepresentation = textPart, AdditionalProperties = textPart.Metadata.ToAdditionalProperties() }, + FilePart filePart when filePart.File is FileWithUri fileWithUrl => new HostedFileContent(fileWithUrl.Uri) { RawRepresentation = filePart, AdditionalProperties = filePart.Metadata.ToAdditionalProperties() }, + _ => throw new NotSupportedException($"Part type '{part.GetType().Name}' is not supported.") }; - } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs index eb80439e0e..35c23e7b47 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs @@ -33,13 +33,11 @@ internal static class AIContentExtensions /// /// AI content to convert. /// The corresponding A2A object. - internal static Part ToA2APart(this AIContent content) - { - return content switch + internal static Part ToA2APart(this AIContent content) => + content switch { TextContent textContent => new TextPart { Text = textContent.Text }, HostedFileContent hostedFileContent => new FilePart { File = new FileWithUri { Uri = hostedFileContent.FileId } }, _ => throw new NotSupportedException($"Unsupported content type: {content.GetType().Name}."), }; - } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs index 9905d63412..fabcddba53 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs @@ -106,10 +106,8 @@ public abstract class AIAgent public Task RunAsync( AgentThread? thread = null, AgentRunOptions? options = null, - CancellationToken cancellationToken = default) - { - return this.RunAsync((IEnumerable)[], thread, options, cancellationToken); - } + CancellationToken cancellationToken = default) => + this.RunAsync([], thread, options, cancellationToken); /// /// Run the agent with the provided message and arguments. @@ -188,10 +186,8 @@ public abstract class AIAgent public IAsyncEnumerable RunStreamingAsync( AgentThread? thread = null, AgentRunOptions? options = null, - CancellationToken cancellationToken = default) - { - return this.RunStreamingAsync((IEnumerable)[], thread, options, cancellationToken); - } + CancellationToken cancellationToken = default) => + this.RunStreamingAsync([], thread, options, cancellationToken); /// /// Run the agent with the provided message and arguments. diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIContextProvider.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIContextProvider.cs index fc6d244051..d54a6b6d1b 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIContextProvider.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIContextProvider.cs @@ -65,7 +65,7 @@ public abstract class AIContextProvider } /// - /// Contains the event context provided to . + /// Contains the event context provided to . /// public class InvokingContext { @@ -82,11 +82,11 @@ public abstract class AIContextProvider /// /// Gets the messages that will be sent to the Model/Agent/etc. for this invocation. /// - public IEnumerable RequestMessages { get; private set; } + public IEnumerable RequestMessages { get; } } /// - /// Contains the event conext provided to . + /// Contains the event conext provided to . /// public class InvokedContext { @@ -103,7 +103,7 @@ public abstract class AIContextProvider /// /// Gets the messages that were sent to the Model/Agent/etc. for this invocation. /// - public IEnumerable RequestMessages { get; private set; } + public IEnumerable RequestMessages { get; } /// /// Gets the collection of response messages generated by Model/Agent/etc. if the invocation succeeded. diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs index f2591a4081..9c0db6d1ed 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs @@ -250,11 +250,9 @@ public class AgentRunResponse return default; } - T? deserialized = default; - // If there's an exception here, we want it to propagate, since the Result property is meant to throw directly - deserialized = DeserializeFirstTopLevelObject(json!, (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(T))); + T? deserialized = DeserializeFirstTopLevelObject(json!, (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(T))); if (deserialized is null) { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs index 748c5b8f35..814d515ea8 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs @@ -39,13 +39,12 @@ public class AgentThread Func? chatMessageStoreFactory = null, Func? aiContextProviderFactory = null) { - if (serializedThreadState.ValueKind != JsonValueKind.Object) + if (serializedThreadState.ValueKind is not JsonValueKind.Object) { throw new ArgumentException("The serialized thread state must be a JSON object.", nameof(serializedThreadState)); } - var state = JsonSerializer.Deserialize( - serializedThreadState, + var state = serializedThreadState.Deserialize( AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ThreadState))) as ThreadState; this.AIContextProvider = aiContextProviderFactory?.Invoke(state?.AIContextProviderState ?? default, jsonSerializerOptions); @@ -59,11 +58,9 @@ public class AgentThread } this._messageStore = chatMessageStoreFactory?.Invoke(state?.StoreState ?? default, jsonSerializerOptions); - if (this._messageStore is null) - { - // If we didn't get a custom store, create an in-memory one. - this._messageStore = new InMemoryChatMessageStore(state?.StoreState ?? default, jsonSerializerOptions); - } + + // If we didn't get a custom store, create an in-memory one. + this._messageStore ??= new InMemoryChatMessageStore(state?.StoreState ?? default, jsonSerializerOptions); } /// diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs index f57d0a8acc..81662c000d 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs @@ -15,8 +15,6 @@ namespace Microsoft.Extensions.AI.Agents; /// public sealed class InMemoryChatMessageStore : IList, IChatMessageStore { - private readonly IChatReducer? _chatReducer; - private readonly ChatReducerTriggerEvent _reducerTriggerEvent; private List _messages; /// @@ -24,7 +22,7 @@ public sealed class InMemoryChatMessageStore : IList, IChatMessageS /// public InMemoryChatMessageStore() { - this._messages = new(); + this._messages = []; } /// @@ -57,37 +55,32 @@ public sealed class InMemoryChatMessageStore : IList, IChatMessageS /// The event that should trigger the reducer invocation. public InMemoryChatMessageStore(IChatReducer? chatReducer, JsonElement serializedStoreState, JsonSerializerOptions? jsonSerializerOptions = null, ChatReducerTriggerEvent reducerTriggerEvent = ChatReducerTriggerEvent.BeforeMessagesRetrieval) { - this._chatReducer = chatReducer; - this._reducerTriggerEvent = reducerTriggerEvent; + this.ChatReducer = chatReducer; + this.ReducerTriggerEvent = reducerTriggerEvent; - if (serializedStoreState.ValueKind != JsonValueKind.Object) + if (serializedStoreState.ValueKind is JsonValueKind.Object) { - this._messages = new(); - return; + var state = serializedStoreState.Deserialize( + AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(StoreState))) as StoreState; + if (state?.Messages is { } messages) + { + this._messages = messages; + return; + } } - var state = JsonSerializer.Deserialize( - serializedStoreState, - AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(StoreState))) as StoreState; - - if (state?.Messages is { Count: > 0 } messages) - { - this._messages = messages; - return; - } - - this._messages = new(); + this._messages = []; } /// /// Gets the chat reducer used to process or reduce chat messages. If null, no reduction logic will be applied. /// - public IChatReducer? ChatReducer => this._chatReducer; + public IChatReducer? ChatReducer { get; } /// /// Gets the event that triggers the reducer invocation in this store. /// - public ChatReducerTriggerEvent ReducerTriggerEvent => this._reducerTriggerEvent; + public ChatReducerTriggerEvent ReducerTriggerEvent { get; } /// public int Count => this._messages.Count; @@ -109,18 +102,18 @@ public sealed class InMemoryChatMessageStore : IList, IChatMessageS this._messages.AddRange(messages); - if (this._reducerTriggerEvent == ChatReducerTriggerEvent.AfterMessageAdded && this._chatReducer is not null) + if (this.ReducerTriggerEvent is ChatReducerTriggerEvent.AfterMessageAdded && this.ChatReducer is not null) { - this._messages = (await this._chatReducer.ReduceAsync(this._messages, cancellationToken).ConfigureAwait(false)).ToList(); + this._messages = (await this.ChatReducer.ReduceAsync(this._messages, cancellationToken).ConfigureAwait(false)).ToList(); } } /// public async Task> GetMessagesAsync(CancellationToken cancellationToken) { - if (this._reducerTriggerEvent == ChatReducerTriggerEvent.BeforeMessagesRetrieval && this._chatReducer is not null) + if (this.ReducerTriggerEvent is ChatReducerTriggerEvent.BeforeMessagesRetrieval && this.ChatReducer is not null) { - this._messages = (await this._chatReducer.ReduceAsync(this._messages, cancellationToken).ConfigureAwait(false)).ToList(); + this._messages = (await this.ChatReducer.ReduceAsync(this._messages, cancellationToken).ConfigureAwait(false)).ToList(); } return this._messages; @@ -179,7 +172,7 @@ public sealed class InMemoryChatMessageStore : IList, IChatMessageS internal sealed class StoreState { - public List Messages { get; set; } = new List(); + public List Messages { get; set; } = []; } /// diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs index 8748b581ab..d355a7c9e9 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs @@ -133,7 +133,6 @@ namespace Azure.AI.Agents.Persistent { // There was an active run; we need to cancel it before starting a new run. await _client!.Runs.CancelRunAsync(threadId, threadRun.Id, cancellationToken).ConfigureAwait(false); - threadRun = null; } // Now create a new run and stream the results. @@ -319,7 +318,7 @@ namespace Azure.AI.Agents.Persistent // But if they haven't, the only way we can provide our tools is via an override, whereas we'd really like to // just add them. To handle that, we'll get all of the agent's tools and add them to the override list // along with our tools. - if (runOptions.OverrideTools is null || !runOptions.OverrideTools.Any()) + if (runOptions.OverrideTools?.Any() is not true) { toolDefinitions.AddRange(_agent.Tools); } @@ -452,7 +451,7 @@ namespace Azure.AI.Agents.Persistent runOptions.ResponseFormat = BinaryData.FromString("""{ "type": "json_object" }"""); } } - else if (options.ResponseFormat is ChatResponseFormatText textFormat) + else if (options.ResponseFormat is ChatResponseFormatText) { runOptions.ResponseFormat = BinaryData.FromString("""{ "type": "text" }"""); } @@ -707,7 +706,7 @@ namespace Azure.AI.Agents.Persistent public static void AssertNull(T value, string name, string? message = null) { - if (value != null) + if (value is not null) { throw new ArgumentException(message ?? "Value must be null.", name); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentResponseExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentResponseExtensions.cs index d132387b5e..b675ba81a0 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentResponseExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentResponseExtensions.cs @@ -46,9 +46,7 @@ internal static class PersistentAgentResponseExtensions throw new ArgumentNullException(nameof(persistentAgentsClient)); } -#pragma warning disable CA2000 // Dispose objects before losing scope var chatClient = persistentAgentsClient.AsNewIChatClient(persistentAgentMetadata.Id); -#pragma warning restore CA2000 // Dispose objects before losing scope return new ChatClientAgent(chatClient, options: new() { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/ActivityProcessor.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/ActivityProcessor.cs index 7ef3119945..eb00d843ad 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/ActivityProcessor.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/ActivityProcessor.cs @@ -34,13 +34,11 @@ internal static class ActivityProcessor } } - private static ChatMessage CreateChatMessageFromActivity(IActivity activity, IEnumerable messageContent) - { - return new ChatMessage(ChatRole.Assistant, [.. messageContent]) + private static ChatMessage CreateChatMessageFromActivity(IActivity activity, IEnumerable messageContent) => + new(ChatRole.Assistant, [.. messageContent]) { AuthorName = activity.From?.Name, MessageId = activity.Id, RawRepresentation = activity }; - } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs index dbd15c2b6e..b742d71f15 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs @@ -26,7 +26,7 @@ public class CopilotStudioAgent : AIAgent /// public CopilotClient Client { get; } - private readonly static AIAgentMetadata s_agentMetadata = new("copilot-studio"); + private static readonly AIAgentMetadata s_agentMetadata = new("copilot-studio"); /// /// Initializes a new instance of the class. @@ -121,7 +121,7 @@ public class CopilotStudioAgent : AIAgent if (string.IsNullOrEmpty(conversationId)) { - throw new System.InvalidOperationException("Failed to start a new conversation."); + throw new InvalidOperationException("Failed to start a new conversation."); } return conversationId!; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/AIAgentExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/AIAgentExtensions.cs index e48e499903..b7fec8550a 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/AIAgentExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/AIAgentExtensions.cs @@ -60,12 +60,9 @@ public static class AIAgentExtensions taskManager.OnAgentCardQuery += (context, query) => { - if (agentCard.Url is null) - { - // A2A SDK assigns the url on its own - // we can help user if they did not set Url explicitly. - agentCard.Url = context; - } + // A2A SDK assigns the url on its own + // we can help user if they did not set Url explicitly. + agentCard.Url ??= context; return Task.FromResult(agentCard); }; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/ActorEntitiesConverter.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/ActorEntitiesConverter.cs index e3440ae9b1..a865636a8f 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/ActorEntitiesConverter.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/ActorEntitiesConverter.cs @@ -11,11 +11,9 @@ internal static class ActorEntitiesConverter { public static Message ToMessage(this ActorResponse response) { - var agentRunResponse = response.Data.Deserialize(AgentHostingJsonUtilities.DefaultOptions.GetTypeInfo(typeof(AgentRunResponse))) as AgentRunResponse; - if (agentRunResponse is null) - { + var agentRunResponse = + response.Data.Deserialize(AgentHostingJsonUtilities.DefaultOptions.GetTypeInfo(typeof(AgentRunResponse))) as AgentRunResponse ?? throw new ArgumentException("The ActorResponse data could not be deserialized to an AgentRunResponse.", nameof(response)); - } var contextId = response.ActorId.Key; var parts = agentRunResponse.Messages.ToParts(); @@ -32,11 +30,9 @@ internal static class ActorEntitiesConverter public static ActorRequestUpdate ToActorRequestUpdate(this Message message, RequestStatus status = RequestStatus.Completed) { // maybe we need to split to chatmessage-per-part, but the idea to map is clear - var chatMessage = message.ToChatMessage(); - if (chatMessage is null) - { + var chatMessage = + message.ToChatMessage() ?? throw new ArgumentException("The Message could not be converted to a ChatMessage.", nameof(message)); - } var agentRunResponseUpdate = new AgentRunResponseUpdate(ChatRole.Assistant, chatMessage.Contents); var updateTypeInfo = AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(AgentRunResponseUpdate)); @@ -47,11 +43,9 @@ internal static class ActorEntitiesConverter public static AgentRunResponse ToAgentRunResponse(this Message message) { // maybe we need to split to chatmessage-per-part, but the idea to map is clear - var chatMessage = message.ToChatMessage(); - if (chatMessage is null) - { + var chatMessage = + message.ToChatMessage() ?? throw new ArgumentException("The Message could not be converted to a ChatMessage.", nameof(message)); - } return new AgentRunResponse(chatMessage); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/MessageConverter.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/MessageConverter.cs index b95c241803..3a498b426b 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/MessageConverter.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Converters/MessageConverter.cs @@ -22,7 +22,7 @@ internal static class MessageConverter foreach (var content in chatMessage.Contents) { var part = ConvertAIContentToPart(content); - if (part != null) + if (part is not null) { parts.Add(part); } @@ -51,7 +51,7 @@ internal static class MessageConverter } var result = new List(); - if (messageSendParams.Message?.Parts != null) + if (messageSendParams.Message?.Parts is not null) { var chatMessage = ToChatMessage(messageSendParams.Message); if (chatMessage is not null) @@ -94,7 +94,7 @@ internal static class MessageConverter /// A ChatMessage object, or null if conversion is not possible. public static ChatMessage? ToChatMessage(this Message message) { - if (message?.Parts == null || message.Parts.Count == 0) + if (message?.Parts is not { Count: > 0 }) { return null; } @@ -151,10 +151,8 @@ internal static class MessageConverter /// The A2A part to convert. /// An AIContent object, or null if conversion is not possible. #pragma warning disable CA1859 // Use concrete types when possible for improved performance - private static AIContent? ConvertPartToAIContent(Part part) -#pragma warning restore CA1859 // Use concrete types when possible for improved performance - { - var result = part switch + private static AIContent? ConvertPartToAIContent(Part part) => + part switch { TextPart textPart => new TextContent(textPart.Text) { @@ -164,9 +162,6 @@ internal static class MessageConverter FilePart or DataPart or _ => throw new NotSupportedException($"Part type '{part.GetType().Name}' is not supported. Only TextPart is supported.") }; - return result; - } - /// /// Converts Microsoft.Extensions.AI ChatMessage back to A2A Message format. /// This is useful for the reverse operation. @@ -175,23 +170,23 @@ internal static class MessageConverter /// An A2A Message object. public static Message ToA2AMessage(this ChatMessage chatMessage) { - if (chatMessage == null) + if (chatMessage is null) { throw new ArgumentNullException(nameof(chatMessage)); } var message = new Message { - MessageId = chatMessage.MessageId ?? System.Guid.NewGuid().ToString(), + MessageId = chatMessage.MessageId ?? Guid.NewGuid().ToString(), Role = ConvertChatRoleToMessageRole(chatMessage.Role), - Parts = new List() + Parts = [] }; // Convert content to parts foreach (var content in chatMessage.Contents) { var part = ConvertAIContentToPart(content); - if (part != null) + if (part is not null) { message.Parts.Add(part); } @@ -231,10 +226,8 @@ internal static class MessageConverter /// The AIContent to convert. /// A Part object, or null if conversion is not possible. #pragma warning disable CA1859 // Use concrete types when possible for improved performance - private static Part? ConvertAIContentToPart(AIContent content) -#pragma warning restore CA1859 // Use concrete types when possible for improved performance - { - return content switch + private static Part? ConvertAIContentToPart(AIContent content) => + content switch { TextContent textContent => new TextPart { @@ -242,11 +235,10 @@ internal static class MessageConverter }, _ => throw new NotSupportedException($"Content type '{content.GetType().Name}' is not supported.") }; - } private static AdditionalPropertiesDictionary? ToAdditionalPropertiesDictionary(this Dictionary metadata) { - if (metadata == null || metadata.Count == 0) + if (metadata is not { Count: > 0 }) { return null; } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Internal/A2AAgentWrapper.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Internal/A2AAgentWrapper.cs index 9c97b3d88f..0a722ac0c9 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Internal/A2AAgentWrapper.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting.A2A/Internal/A2AAgentWrapper.cs @@ -8,7 +8,6 @@ using A2A; using Microsoft.Extensions.AI.Agents.Hosting.A2A.Converters; using Microsoft.Extensions.AI.Agents.Runtime; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.Extensions.AI.Agents.Hosting.A2A.Internal; @@ -17,7 +16,6 @@ namespace Microsoft.Extensions.AI.Agents.Hosting.A2A.Internal; /// internal sealed class A2AAgentWrapper { - private readonly ILogger _logger; private readonly AIAgent _innerAgent; private readonly IActorClient _actorClient; @@ -26,8 +24,6 @@ internal sealed class A2AAgentWrapper AIAgent innerAgent, ILoggerFactory? loggerFactory = null) { - this._logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); - this._actorClient = actorClient; this._innerAgent = innerAgent ?? throw new ArgumentNullException(nameof(innerAgent)); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentActor.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentActor.cs index 328d8a5ad8..68de921262 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentActor.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentActor.cs @@ -34,14 +34,11 @@ internal sealed class AgentActor( this._etag = response.ETag; var hasExistingThread = false; - if (response.Results[0] is GetValueResult threadResult) + if (response.Results[0] is GetValueResult { Value: { } threadJson }) { - if (threadResult.Value is { } threadJson) - { - // Deserialize the thread state if it exists - this._thread = agent.DeserializeThread(threadJson, cancellationToken: cancellationToken); - hasExistingThread = true; - } + // Deserialize the thread state if it exists + this._thread = agent.DeserializeThread(threadJson, cancellationToken: cancellationToken); + hasExistingThread = true; } this._thread ??= agent.GetNewThread(); @@ -89,7 +86,7 @@ internal sealed class AgentActor( { // Unsupported method, we can only handle "Run" requests. var data = JsonSerializer.SerializeToElement("Unsupported method.", AgentHostingJsonUtilities.DefaultOptions.GetTypeInfo(typeof(string))); - var writeResponse = await context.WriteAsync( + await context.WriteAsync( new(this._etag, [ new UpdateRequestOperation( requestId, diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs index 97588d3779..3544cdb53f 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs @@ -107,8 +107,7 @@ public sealed class AgentProxy : AIAgent yield break; } - var runResponseUpdate = (AgentRunResponseUpdate)update.Data.Deserialize(updateTypeInfo)!; - yield return runResponseUpdate; + yield return (AgentRunResponseUpdate)update.Data.Deserialize(updateTypeInfo)!; } } @@ -142,7 +141,6 @@ public sealed class AgentProxy : AIAgent messageId, method: AgentActorConstants.RunMethodName, @params: JsonSerializer.SerializeToElement(runRequest, AgentHostingJsonUtilities.DefaultOptions.GetTypeInfo(typeof(AgentRunRequest)))); - var handle = await this._client.SendRequestAsync(actorRequest, cancellationToken).ConfigureAwait(false); - return handle; + return await this._client.SendRequestAsync(actorRequest, cancellationToken).ConfigureAwait(false); } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/HostApplicationBuilderAgentExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/HostApplicationBuilderAgentExtensions.cs index e402e27d52..cf356eb65f 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/HostApplicationBuilderAgentExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/HostApplicationBuilderAgentExtensions.cs @@ -102,7 +102,7 @@ public static class HostApplicationBuilderAgentExtensions Throw.IfNull(builder); Throw.IfNull(name); Throw.IfNull(createAgentDelegate); - builder.Services.AddKeyedSingleton(name, (sp, key) => + builder.Services.AddKeyedSingleton(name, (sp, key) => { Throw.IfNull(key); var keyString = key as string; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/AsyncStreamingUpdateCollectionResult.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/AsyncStreamingUpdateCollectionResult.cs index e398a7d481..50717dd827 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/AsyncStreamingUpdateCollectionResult.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/AsyncStreamingUpdateCollectionResult.cs @@ -16,14 +16,10 @@ internal sealed class AsyncStreamingUpdateCollectionResult : AsyncCollectionResu public override ContinuationToken? GetContinuationToken(ClientResult page) => null; - public override IAsyncEnumerable GetRawPagesAsync() - { -#pragma warning disable CA2000 // Dispose objects before losing scope - return AsyncEnumerable.Repeat(ClientResult.FromValue(this._updates, new StreamingUpdatePipelineResponse(this._updates)), 1); -#pragma warning restore CA2000 // Dispose objects before losing scope - } + public override IAsyncEnumerable GetRawPagesAsync() => + AsyncEnumerable.Repeat(ClientResult.FromValue(this._updates, new StreamingUpdatePipelineResponse(this._updates)), 1); - protected async override IAsyncEnumerable GetValuesFromPageAsync(ClientResult page) + protected override async IAsyncEnumerable GetValuesFromPageAsync(ClientResult page) { var updates = ((ClientResult>)page).Value; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/StreamingUpdatePipelineResponse.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/StreamingUpdatePipelineResponse.cs index 70e3967445..1eaab794d9 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/StreamingUpdatePipelineResponse.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/ChatCompletion/StreamingUpdatePipelineResponse.cs @@ -38,18 +38,14 @@ internal sealed class StreamingUpdatePipelineResponse : PipelineResponse /// /// Buffering content is not supported for streaming responses. /// - public override BinaryData BufferContent(CancellationToken cancellationToken = default) - { + public override BinaryData BufferContent(CancellationToken cancellationToken = default) => throw new NotSupportedException("Buffering content is not supported for streaming responses."); - } /// /// Buffering content asynchronously is not supported for streaming responses. /// - public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) - { + public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException("Buffering content asynchronously is not supported for streaming responses."); - } /// /// Disposes resources. No resources to dispose for streaming response. @@ -61,11 +57,8 @@ internal sealed class StreamingUpdatePipelineResponse : PipelineResponse internal StreamingUpdatePipelineResponse(IAsyncEnumerable updates) { - this._updates = updates; } - private readonly IAsyncEnumerable _updates; - private sealed class EmptyPipelineResponseHeaders : PipelineResponseHeaders { public override bool TryGetValue(string name, out string? value) diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs index 37bee66e61..5d7f577d50 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs @@ -36,15 +36,14 @@ 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, AgentThread? thread = 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 chatCompletion = response.AsChatCompletion(); - return chatCompletion; + return response.AsChatCompletion(); } /// @@ -63,7 +62,7 @@ public static class AIAgentWithOpenAIExtensions /// This method converts the OpenAI chat messages to the Microsoft Extensions AI format using the appropriate conversion method, /// runs the agent, and then extracts the native OpenAI from the response using . /// - public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { Throw.IfNull(agent); Throw.IfNull(messages); diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AssistantClientResultExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AssistantClientResultExtensions.cs index 6faf65eacb..82db6f781b 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AssistantClientResultExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AssistantClientResultExtensions.cs @@ -25,7 +25,7 @@ public static class AssistantExtensions throw new ArgumentNullException(nameof(assistantClientResult)); } - return AssistantExtensions.AsAIAgent(assistantClientResult, assistantClient, chatOptions); + return AsAIAgent(assistantClientResult, assistantClient, chatOptions); } /// diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs index 60cd192480..8f43f82b5a 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs @@ -90,9 +90,8 @@ public static class OpenAIAssistantClientExtensions /// An instance backed by the OpenAI Assistant service. /// Thrown when or is . /// Thrown when is empty or whitespace. - public static AIAgent CreateAIAgent(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) - { - return client.CreateAIAgent( + public static AIAgent CreateAIAgent(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) => + client.CreateAIAgent( model, new ChatClientAgentOptions() { @@ -105,7 +104,6 @@ public static class OpenAIAssistantClientExtensions } }, loggerFactory); - } /// /// Creates an AI agent from an using the OpenAI Assistant API. @@ -165,10 +163,7 @@ public static class OpenAIAssistantClientExtensions } }; -#pragma warning disable CA2000 // Dispose objects before losing scope - var chatClient = client.AsIChatClient(assistantId); -#pragma warning restore CA2000 // Dispose objects before losing scope - return new ChatClientAgent(chatClient, agentOptions, loggerFactory); + return new ChatClientAgent(client.AsIChatClient(assistantId), agentOptions, loggerFactory); } /// @@ -184,9 +179,8 @@ public static class OpenAIAssistantClientExtensions /// An instance backed by the OpenAI Assistant service. /// Thrown when or is . /// Thrown when is empty or whitespace. - public static async Task CreateAIAgentAsync(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) - { - return await client.CreateAIAgentAsync( + public static async Task CreateAIAgentAsync(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) => + await client.CreateAIAgentAsync( model, new ChatClientAgentOptions() { @@ -199,7 +193,6 @@ public static class OpenAIAssistantClientExtensions } }, loggerFactory).ConfigureAwait(false); - } /// /// Creates an AI agent from an using the OpenAI Assistant API. diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIChatClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIChatClientExtensions.cs index 90e279eab0..e3e85cadd8 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIChatClientExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIChatClientExtensions.cs @@ -31,9 +31,8 @@ public static class OpenAIChatClientExtensions /// Optional logger factory for enabling logging within the agent. /// An instance backed by the OpenAI Chat Completion service. /// Thrown when is . - public static AIAgent CreateAIAgent(this ChatClient client, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) - { - return client.CreateAIAgent( + public static AIAgent CreateAIAgent(this ChatClient client, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) => + client.CreateAIAgent( new ChatClientAgentOptions() { Name = name, @@ -45,7 +44,6 @@ public static class OpenAIChatClientExtensions } }, loggerFactory); - } /// /// Creates an AI agent from an using the OpenAI Chat Completion API. @@ -61,7 +59,6 @@ public static class OpenAIChatClientExtensions Throw.IfNull(options); var chatClient = client.AsIChatClient(); - ChatClientAgent agent = new(chatClient, options, loggerFactory); - return agent; + return new ChatClientAgent(chatClient, options, loggerFactory); } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs index 5e22814402..4e1faaaa18 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs @@ -76,8 +76,7 @@ public class OpenAIChatClientAgent : AIAgent { var response = await this.RunAsync([.. messages.AsChatMessages()], thread, options, cancellationToken).ConfigureAwait(false); - var chatCompletion = response.AsChatCompletion(); - return chatCompletion; + return response.AsChatCompletion(); } /// diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorId.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorId.cs index a6a589a06a..e95fa7036a 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorId.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorId.cs @@ -149,13 +149,11 @@ public readonly struct ActorId : IEquatable } string? actorIdString = reader.GetString() ?? throw new JsonException("ActorId cannot be null"); - return ActorId.Parse(actorIdString); + return Parse(actorIdString); } /// - public override void Write(Utf8JsonWriter writer, ActorId value, JsonSerializerOptions options) - { + public override void Write(Utf8JsonWriter writer, ActorId value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString()); - } } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorResponse.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorResponse.cs index 47543f567e..527e45c565 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorResponse.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorResponse.cs @@ -38,7 +38,7 @@ public sealed class ActorResponse public override string ToString() { string dataString; - if (this.Data.ValueKind == JsonValueKind.Undefined) + if (this.Data.ValueKind is JsonValueKind.Undefined) { dataString = "undefined"; } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorType.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorType.cs index 67585803b0..f163e6dede 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorType.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorType.cs @@ -101,9 +101,7 @@ public readonly partial struct ActorType : IEquatable } /// - public override void Write(Utf8JsonWriter writer, ActorType value, JsonSerializerOptions options) - { + public override void Write(Utf8JsonWriter writer, ActorType value, JsonSerializerOptions options) => writer.WriteStringValue(value.Name); - } } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InMemoryActorStateStorage.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InMemoryActorStateStorage.cs index 2063420ab3..849323e2d3 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InMemoryActorStateStorage.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InMemoryActorStateStorage.cs @@ -74,16 +74,16 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage { private static readonly ActivitySource ActivitySource = new("Microsoft.Extensions.AI.Agents.Runtime.Abstractions.InMemoryActorStateStorage"); - private readonly ConcurrentDictionary _actorStates = new(); + private readonly ConcurrentDictionary _actorStates = []; private readonly object _lockObject = new(); - private long _globalETagCounter = 0; + private long _globalETagCounter; /// /// Represents the internal state of an actor including its key-value pairs and ETag. /// private sealed class ActorState { - public ConcurrentDictionary Data { get; } = new(); + public ConcurrentDictionary Data { get; } = []; public string ETag { get; set; } = "0"; } @@ -117,9 +117,10 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage // Check ETag for optimistic concurrency control if (actorState.ETag != etag) { - activity?.SetTag("state.success", false); - activity?.SetTag("error.type", "etag_mismatch"); - activity?.SetStatus(ActivityStatusCode.Error, "ETag mismatch - concurrent modification detected"); + activity? + .SetTag("state.success", false) + .SetTag("error.type", "etag_mismatch") + .SetStatus(ActivityStatusCode.Error, "ETag mismatch - concurrent modification detected"); // Return failure with current ETag return new ValueTask(new WriteResponse(actorState.ETag, success: false)); @@ -155,9 +156,10 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage // Set success attributes SetOperationStatus(activity, true); - activity?.SetTag("state.success", true); - activity?.SetTag("state.new_etag", newETag); - activity?.SetTag("state.operations", string.Join(",", operationTypes)); + activity? + .SetTag("state.success", true) + .SetTag("state.new_etag", newETag) + .SetTag("state.operations", string.Join(",", operationTypes)); return new ValueTask(new WriteResponse(newETag, success: true)); } @@ -240,9 +242,10 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage // Set success attributes SetOperationStatus(activity, true); - activity?.SetTag("state.etag", actorState.ETag); - activity?.SetTag("state.operations", string.Join(",", operationTypes)); - activity?.SetTag("state.success", true); + activity? + .SetTag("state.etag", actorState.ETag) + .SetTag("state.operations", string.Join(",", operationTypes)) + .SetTag("state.success", true); return new ValueTask(new ReadResponse(actorState.ETag, results.AsReadOnly())); } @@ -276,36 +279,26 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage /// /// The actor identifier. /// The number of keys stored for the specified actor, or 0 if the actor has no state. - public int GetKeyCount(ActorId actorId) - { - return this._actorStates.TryGetValue(actorId, out var state) ? state.Data.Count : 0; - } + public int GetKeyCount(ActorId actorId) => + this._actorStates.TryGetValue(actorId, out var state) ? state.Data.Count : 0; /// /// Gets the current ETag for a specific actor. /// /// The actor identifier. /// The current ETag for the specified actor, or "0" if the actor has no state. - public string GetETag(ActorId actorId) - { - return this._actorStates.TryGetValue(actorId, out var state) ? state.ETag : "0"; - } + public string GetETag(ActorId actorId) => + this._actorStates.TryGetValue(actorId, out var state) ? state.ETag : "0"; /// /// Sets actor attributes on an activity. /// /// The activity to set attributes on. /// The actor ID. - private static void SetActorAttributes(Activity? activity, ActorId actorId) - { - if (activity == null) - { - return; - } - - activity.SetTag("actor.id", actorId.ToString()); - activity.SetTag("actor.type", actorId.Type.Name); - } + private static void SetActorAttributes(Activity? activity, ActorId actorId) => + activity? + .SetTag("actor.id", actorId.ToString()) + .SetTag("actor.type", actorId.Type.Name); /// /// Sets state operation attributes on an activity. @@ -316,7 +309,7 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage /// Optional ETag value. private static void SetStateAttributes(Activity? activity, string operationType, int? operationCount = null, string? etag = null) { - if (activity == null) + if (activity is null) { return; } @@ -342,7 +335,7 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage /// Optional error message for failures. private static void SetOperationStatus(Activity? activity, bool success, string? errorMessage = null) { - if (activity == null) + if (activity is null) { return; } @@ -362,23 +355,15 @@ public sealed class InMemoryActorStateStorage : IActorStateStorage /// /// The activity to set error attributes on. /// The exception that occurred. - private static void SetErrorAttributes(Activity? activity, Exception exception) - { - if (activity == null) - { - return; - } - - activity.SetTag("error.type", exception.GetType().Name); - activity.SetTag("error.message", exception.Message); - activity.SetStatus(ActivityStatusCode.Error, exception.Message); - - // Add exception event - activity.AddEvent(new ActivityEvent("exception", DateTimeOffset.UtcNow, new ActivityTagsCollection - { - ["error.type"] = exception.GetType().Name, - ["error.message"] = exception.Message, - ["error.stack_trace"] = exception.StackTrace - })); - } + private static void SetErrorAttributes(Activity? activity, Exception exception) => + activity? + .SetTag("error.type", exception.GetType().Name) + .SetTag("error.message", exception.Message) + .SetStatus(ActivityStatusCode.Error, exception.Message) + .AddEvent(new ActivityEvent("exception", DateTimeOffset.UtcNow, new ActivityTagsCollection + { + ["error.type"] = exception.GetType().Name, + ["error.message"] = exception.Message, + ["error.stack_trace"] = exception.StackTrace + })); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosActorStateStorage.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosActorStateStorage.cs index e96254a993..c44b68bc15 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosActorStateStorage.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosActorStateStorage.cs @@ -137,7 +137,7 @@ public class CosmosActorStateStorage : IActorStateStorage, IAsyncDisposable var results = new List(); // Read root document first to get actor-level ETag - string actorETag = await this.GetActorETagAsync(container, actorId, cancellationToken).ConfigureAwait(false); + string actorETag = await GetActorETagAsync(container, actorId, cancellationToken).ConfigureAwait(false); var actorType = actorId.Type.ToString(); var actorKey = actorId.Key; @@ -236,7 +236,7 @@ public class CosmosActorStateStorage : IActorStateStorage, IAsyncDisposable /// Gets the current ETag for the actor's root document. /// Returns a generated ETag if no root document exists. /// - private async ValueTask GetActorETagAsync(Container container, ActorId actorId, CancellationToken cancellationToken) + private static async ValueTask GetActorETagAsync(Container container, ActorId actorId, CancellationToken cancellationToken) { try { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosIdSanitizer.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosIdSanitizer.cs index 38b6362157..26332185bd 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosIdSanitizer.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/CosmosIdSanitizer.cs @@ -27,9 +27,7 @@ internal static class CosmosIdSanitizer #if NET8_0_OR_GREATER return string.Create(input.Length + extraChars, input, (output, state) => - { - Encode(state.AsSpan(), output); - }); + Encode(state.AsSpan(), output)); #else var result = new char[input.Length + extraChars]; Encode(input.AsSpan(), result); @@ -48,9 +46,7 @@ internal static class CosmosIdSanitizer #if NET8_0_OR_GREATER return string.Create(input.Length - escapeCount, input, (output, state) => - { - Decode(state.AsSpan(), output); - }); + Decode(state.AsSpan(), output)); #else var result = new char[input.Length - escapeCount]; Decode(input.AsSpan(), result); diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/LazyCosmosContainer.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/LazyCosmosContainer.cs index af4be62a81..16daf0e6ef 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/LazyCosmosContainer.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/LazyCosmosContainer.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Collections.ObjectModel; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -14,7 +13,10 @@ namespace Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB; /// internal sealed class LazyCosmosContainer : IAsyncDisposable { - private readonly static Random s_random = new(); +#if !NET + [ThreadStatic] + private static Random? t_random; +#endif private readonly CosmosClient? _cosmosClient; private readonly string? _databaseName; @@ -24,7 +26,7 @@ internal sealed class LazyCosmosContainer : IAsyncDisposable private Task? _initTask; // internal for testing - internal readonly static string[] CosmosPartitionKeyPaths = ["/actorType", "/actorKey"]; + internal static readonly string[] CosmosPartitionKeyPaths = ["/actorType", "/actorKey"]; /// /// LazyCosmosContainer constructor that initializes the container lazily. @@ -73,7 +75,7 @@ internal sealed class LazyCosmosContainer : IAsyncDisposable if (ex.RetryAfter is not null && ex.RetryAfter > TimeSpan.Zero) { var retry = ex.RetryAfter.Value; - var jitterMs = this.RandomNextDouble() * retry.TotalMilliseconds; // 0..retry + var jitterMs = RandomNextDouble() * retry.TotalMilliseconds; // 0..retry var delay = retry + TimeSpan.FromMilliseconds(jitterMs); await Task.Delay(delay, cancellationToken).ConfigureAwait(false); previousDelay = delay; @@ -83,7 +85,7 @@ internal sealed class LazyCosmosContainer : IAsyncDisposable // sleep = min(maxDelay, random(baseDelay, previousDelay * 3)) var minMs = baseDelay.TotalMilliseconds; var maxMs = Math.Min(maxDelay.TotalMilliseconds, Math.Max(minMs, previousDelay.TotalMilliseconds * 3)); - var sleepMs = this.RandomNextDouble() * (maxMs - minMs) + minMs; + var sleepMs = (RandomNextDouble() * (maxMs - minMs)) + minMs; var jitterDelay = TimeSpan.FromMilliseconds(sleepMs); await Task.Delay(jitterDelay, cancellationToken).ConfigureAwait(false); @@ -109,41 +111,41 @@ internal sealed class LazyCosmosContainer : IAsyncDisposable }; // Add composite index for efficient queries - containerProperties.IndexingPolicy.CompositeIndexes.Add(new Collection - { + containerProperties.IndexingPolicy.CompositeIndexes.Add( + [ new() { Path = "/actorType", Order = CompositePathSortOrder.Ascending }, new() { Path = "/actorKey", Order = CompositePathSortOrder.Ascending }, new() { Path = "/key", Order = CompositePathSortOrder.Ascending } - }); + ]); var container = await database.Database.CreateContainerIfNotExistsAsync(containerProperties, cancellationToken: cancellationToken).ConfigureAwait(false); return container.Container; } - private static bool IsTransient(Exception exception) + private static bool IsTransient(Exception exception) => exception switch { - return exception switch + CosmosException cosmosEx => cosmosEx.StatusCode switch { - CosmosException cosmosEx => cosmosEx.StatusCode switch - { #if NET9_0_OR_GREATER - HttpStatusCode.TooManyRequests => true, // 429 - Rate limited + HttpStatusCode.TooManyRequests => true, // 429 - Rate limited #endif - HttpStatusCode.InternalServerError => true, // 500 - Server error - HttpStatusCode.BadGateway => true, // 502 - Bad gateway - HttpStatusCode.ServiceUnavailable => true, // 503 - Service unavailable - HttpStatusCode.GatewayTimeout => true, // 504 - Gateway timeout - HttpStatusCode.RequestTimeout => true, // 408 - Request timeout - _ => false - }, - TaskCanceledException or OperationCanceledException or ArgumentException => false, - _ => true // Retry other exceptions (network issues, etc.) - }; - } + HttpStatusCode.InternalServerError => true, // 500 - Server error + HttpStatusCode.BadGateway => true, // 502 - Bad gateway + HttpStatusCode.ServiceUnavailable => true, // 503 - Service unavailable + HttpStatusCode.GatewayTimeout => true, // 504 - Gateway timeout + HttpStatusCode.RequestTimeout => true, // 408 - Request timeout + _ => false + }, + TaskCanceledException or OperationCanceledException or ArgumentException => false, + _ => true // Retry other exceptions (network issues, etc.) + }; -#pragma warning disable CA5394 // Do not use insecure randomness - private double RandomNextDouble() => s_random.NextDouble(); -#pragma warning restore CA5394 // Do not use insecure randomness + private static double RandomNextDouble() => +#if NET + Random.Shared.NextDouble(); +#else + (t_random ??= new()).NextDouble(); +#endif public ValueTask DisposeAsync() { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/ServiceCollectionExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/ServiceCollectionExtensions.cs index 1ca7e6c995..9313697e57 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/ServiceCollectionExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB/ServiceCollectionExtensions.cs @@ -28,7 +28,7 @@ public static class ServiceCollectionExtensions string containerName = "ActorState") { // Register CosmosClient as singleton - services.AddSingleton(serviceProvider => + services.AddSingleton(serviceProvider => { var cosmosClientOptions = new CosmosClientOptions { @@ -46,7 +46,7 @@ public static class ServiceCollectionExtensions }); // Register LazyCosmosContainer as singleton - services.AddSingleton(serviceProvider => + services.AddSingleton(serviceProvider => { var cosmosClient = serviceProvider.GetRequiredService(); return new LazyCosmosContainer(cosmosClient, databaseName, containerName); @@ -75,7 +75,7 @@ public static class ServiceCollectionExtensions string containerName = "ActorState") { // Register LazyCosmosContainer as singleton using existing CosmosClient - services.AddSingleton(serviceProvider => + services.AddSingleton(serviceProvider => { var cosmosClient = serviceProvider.GetRequiredService(); return new LazyCosmosContainer(cosmosClient, databaseName, containerName); diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActivityExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActivityExtensions.cs index 36dc263a43..53796cacb1 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActivityExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActivityExtensions.cs @@ -9,11 +9,11 @@ namespace Microsoft.Extensions.AI.Agents.Runtime; /// internal static class ActivityExtensions { - public const string ActorCreated = ActorRuntimeOpenTelemetryConsts.EventInfo.Names.ActorCreated; - public const string ActorStarted = ActorRuntimeOpenTelemetryConsts.EventInfo.Names.ActorStarted; - public const string MessageSent = ActorRuntimeOpenTelemetryConsts.EventInfo.Names.MessageSent; - public const string MessageReceived = ActorRuntimeOpenTelemetryConsts.EventInfo.Names.MessageReceived; - public const string RequestCompleted = ActorRuntimeOpenTelemetryConsts.EventInfo.Names.RequestCompleted; + public const string ActorCreated = EventInfo.Names.ActorCreated; + public const string ActorStarted = EventInfo.Names.ActorStarted; + public const string MessageSent = EventInfo.Names.MessageSent; + public const string MessageReceived = EventInfo.Names.MessageReceived; + public const string RequestCompleted = EventInfo.Names.RequestCompleted; // Re-export common status values for convenience public const string Started = "started"; @@ -31,18 +31,19 @@ internal static class ActivityExtensions /// Optional operation name. public static void SetActorAttributes(this System.Diagnostics.Activity? activity, ActorId actorId, string? operation = null) { - if (activity == null) + if (activity is null) { return; } - activity.SetTag(ActorRuntimeOpenTelemetryConsts.Actor.Id, actorId.ToString()); - activity.SetTag(ActorRuntimeOpenTelemetryConsts.Actor.Type, actorId.Type.Name); - activity.SetTag(ActorRuntimeOpenTelemetryConsts.Actor.RpcSystem, ActorRuntimeOpenTelemetryConsts.Actor.SystemName); + activity + .SetTag(Actor.Id, actorId.ToString()) + .SetTag(Actor.Type, actorId.Type.Name) + .SetTag(Actor.RpcSystem, Actor.SystemName); if (!string.IsNullOrEmpty(operation)) { - activity.SetTag(ActorRuntimeOpenTelemetryConsts.Actor.Operation, operation); + activity.SetTag(Actor.Operation, operation); } } @@ -55,7 +56,7 @@ internal static class ActivityExtensions /// Optional message method. public static void SetMessageAttributes(this System.Diagnostics.Activity? activity, string messageId, string? messageType = null, string? method = null) { - if (activity == null) + if (activity is null) { return; } @@ -82,7 +83,7 @@ internal static class ActivityExtensions /// Optional timeout value. public static void SetRequestAttributes(this System.Diagnostics.Activity? activity, string requestId, string? method = null, System.TimeSpan? timeout = null) { - if (activity == null) + if (activity is null) { return; } @@ -109,7 +110,7 @@ internal static class ActivityExtensions /// Optional ETag value. public static void SetStateAttributes(this System.Diagnostics.Activity? activity, string operationType, int? operationCount = null, string? etag = null) { - if (activity == null) + if (activity is null) { return; } @@ -135,7 +136,7 @@ internal static class ActivityExtensions /// Optional error message for failures. public static void SetOperationStatus(this System.Diagnostics.Activity? activity, bool success, string? errorMessage = null) { - if (activity == null) + if (activity is null) { return; } @@ -156,25 +157,17 @@ internal static class ActivityExtensions /// The activity to set error attributes on. /// The exception that occurred. /// Optional custom error type. - public static void SetErrorAttributes(this System.Diagnostics.Activity? activity, System.Exception exception, string? errorType = null) - { - if (activity == null) - { - return; - } - - activity.SetTag(ErrorInfo.Type, errorType ?? exception.GetType().Name); - activity.SetTag(ErrorInfo.Message, exception.Message); - activity.SetStatus(System.Diagnostics.ActivityStatusCode.Error, exception.Message); - - // Add exception event - activity.AddEvent(new System.Diagnostics.ActivityEvent("exception", System.DateTimeOffset.UtcNow, new System.Diagnostics.ActivityTagsCollection - { - [ErrorInfo.Type] = errorType ?? exception.GetType().Name, - [ErrorInfo.Message] = exception.Message, - [ErrorInfo.StackTrace] = exception.StackTrace - })); - } + public static void SetErrorAttributes(this System.Diagnostics.Activity? activity, System.Exception exception, string? errorType = null) => + activity? + .SetTag(ErrorInfo.Type, errorType ?? exception.GetType().Name) + .SetTag(ErrorInfo.Message, exception.Message) + .SetStatus(System.Diagnostics.ActivityStatusCode.Error, exception.Message) + .AddEvent(new System.Diagnostics.ActivityEvent("exception", System.DateTimeOffset.UtcNow, new System.Diagnostics.ActivityTagsCollection + { + [ErrorInfo.Type] = errorType ?? exception.GetType().Name, + [ErrorInfo.Message] = exception.Message, + [ErrorInfo.StackTrace] = exception.StackTrace + })); /// /// Sets RPC-style attributes for actor operations. @@ -182,17 +175,11 @@ internal static class ActivityExtensions /// The activity to set attributes on. /// The RPC service name. /// The RPC method name. - public static void SetRpcAttributes(this System.Diagnostics.Activity? activity, string service, string method) - { - if (activity == null) - { - return; - } - - activity.SetTag(Actor.RpcSystem, Actor.SystemName); - activity.SetTag(Actor.RpcService, service); - activity.SetTag(Actor.RpcMethod, method); - } + public static void SetRpcAttributes(this System.Diagnostics.Activity? activity, string service, string method) => + activity? + .SetTag(Actor.RpcSystem, Actor.SystemName) + .SetTag(Actor.RpcService, service) + .SetTag(Actor.RpcMethod, method); /// /// Sets up complete telemetry for actor retrieval/creation operations. @@ -203,7 +190,7 @@ internal static class ActivityExtensions /// Whether the actor was started. public static void SetupActorOperation(this System.Diagnostics.Activity? activity, ActorId actorId, bool? exists = null, bool? started = null) { - if (activity == null) + if (activity is null) { return; } @@ -233,7 +220,7 @@ internal static class ActivityExtensions /// Optional message status. public static void SetupMessageOperation(this System.Diagnostics.Activity? activity, ActorId actorId, string messageId, string? messageType = null, string? method = null, string? status = null) { - if (activity == null) + if (activity is null) { return; } @@ -259,7 +246,7 @@ internal static class ActivityExtensions /// Optional timeout value. public static void SetupRequestOperation(this System.Diagnostics.Activity? activity, ActorId actorId, string requestId, string? method = null, string service = "ActorClient", string rpcMethod = "SendRequest", System.TimeSpan? timeout = null) { - if (activity == null) + if (activity is null) { return; } @@ -279,7 +266,7 @@ internal static class ActivityExtensions /// Optional ETag value. public static void SetupStateOperation(this System.Diagnostics.Activity? activity, ActorId actorId, string operationType, int? operationCount = null, string? etag = null) { - if (activity == null) + if (activity is null) { return; } @@ -295,7 +282,7 @@ internal static class ActivityExtensions /// Optional additional tags to set. public static void RecordSuccess(this System.Diagnostics.Activity? activity, params (string key, object? value)[] additionalTags) { - if (activity == null) + if (activity is null) { return; } @@ -317,7 +304,7 @@ internal static class ActivityExtensions /// Optional additional tags to set. public static void RecordFailure(this System.Diagnostics.Activity? activity, System.Exception exception, string? errorType = null, params (string key, object? value)[] additionalTags) { - if (activity == null) + if (activity is null) { return; } @@ -339,7 +326,7 @@ internal static class ActivityExtensions /// Optional additional event data. public static void AddActorEvent(this System.Diagnostics.Activity? activity, string eventName, ActorId actorId, params (string key, object? value)[] additionalData) { - if (activity == null) + if (activity is null) { return; } @@ -368,7 +355,7 @@ internal static class ActivityExtensions /// Additional event data. public static void CompleteWithEvent(this System.Diagnostics.Activity? activity, string eventName, ActorId actorId, (string key, object? value)[] statusTags, params (string key, object? value)[] eventData) { - if (activity == null) + if (activity is null) { return; } @@ -405,5 +392,5 @@ internal static class ActivityExtensions /// Record failure. /// public static void Fail(this System.Diagnostics.Activity? activity, System.Exception exception, string? status = null) => - RecordFailure(activity, exception, null, status != null ? (Request.Status, status) : default); + RecordFailure(activity, exception, null, status is not null ? (Request.Status, status) : default); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeBuilder.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeBuilder.cs index a26abdb7b2..6edeb6280d 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeBuilder.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeBuilder.cs @@ -14,8 +14,6 @@ namespace Microsoft.Extensions.AI.Agents.Runtime; /// internal sealed class ActorRuntimeBuilder : IActorRuntimeBuilder { - private readonly IHostApplicationBuilder _builder; - /// /// Gets the collection of registered actor types and their corresponding factory methods. /// @@ -23,7 +21,7 @@ internal sealed class ActorRuntimeBuilder : IActorRuntimeBuilder /// A dictionary where keys are instances and values are factory functions /// that create instances given an and . /// - public Dictionary> ActorFactories { get; } = new(); + public Dictionary> ActorFactories { get; } = []; /// /// Gets or creates an instance for the specified host application builder. @@ -37,12 +35,12 @@ internal sealed class ActorRuntimeBuilder : IActorRuntimeBuilder /// Thrown when is null. public static ActorRuntimeBuilder GetOrAdd(IHostApplicationBuilder builder) { - Microsoft.Shared.Diagnostics.Throw.IfNull(builder); + Shared.Diagnostics.Throw.IfNull(builder); var services = builder.Services; var descriptor = services.FirstOrDefault(s => s.ImplementationInstance is ActorRuntimeBuilder); if (descriptor?.ImplementationInstance is not ActorRuntimeBuilder instance) { - instance = new ActorRuntimeBuilder(builder); + instance = new ActorRuntimeBuilder(); services.Add(ServiceDescriptor.Singleton(instance)); instance.ConfigureServices(services); } @@ -53,10 +51,8 @@ internal sealed class ActorRuntimeBuilder : IActorRuntimeBuilder /// /// Initializes a new instance of the class. /// - /// The host application builder to associate with this actor runtime builder. - private ActorRuntimeBuilder(IHostApplicationBuilder builder) + private ActorRuntimeBuilder() { - this._builder = builder; } /// @@ -75,17 +71,15 @@ internal sealed class ActorRuntimeBuilder : IActorRuntimeBuilder /// Each actor type can only be registered once. Attempting to register the same actor type /// multiple times will result in an exception being thrown by the underlying dictionary. /// - public void AddActorType(ActorType type, Func activator) - { + public void AddActorType(ActorType type, Func activator) => this.ActorFactories.Add(type, activator); - } private void ConfigureServices(IServiceCollection services) { services.AddSingleton(this); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(sp => + services.AddSingleton(sp => { var actorStateStorage = sp.GetRequiredService(); return new InProcessActorRuntime(sp, this.ActorFactories, actorStateStorage); diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeHostingExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeHostingExtensions.cs index 88f120c9c2..9ebf282f9e 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeHostingExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/ActorRuntimeHostingExtensions.cs @@ -16,8 +16,6 @@ public static class ActorRuntimeHostingExtensions /// The to configure. /// An that can be used to further configure the actor runtime. /// Thrown when is null. - public static IActorRuntimeBuilder AddActorRuntime(this IHostApplicationBuilder builder) - { - return ActorRuntimeBuilder.GetOrAdd(builder); - } + public static IActorRuntimeBuilder AddActorRuntime(this IHostApplicationBuilder builder) => + ActorRuntimeBuilder.GetOrAdd(builder); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorContext.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorContext.cs index 317e73ef22..9a90072aca 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorContext.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorContext.cs @@ -19,7 +19,7 @@ namespace Microsoft.Extensions.AI.Agents.Runtime; internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDisposable, IDisposable { - private static readonly ActivitySource ActivitySource = new(ActorRuntimeOpenTelemetryConsts.InProcessSourceName); + private static readonly ActivitySource ActivitySource = new(Tel.InProcessSourceName); private readonly CancellationTokenSource _cts = new(); private readonly Channel _pendingMessages = Channel.CreateUnbounded(); @@ -50,7 +50,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos public void Start() { using var activity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatActorOperation(ActorRuntimeOpenTelemetryConsts.Operations.StartActor)); + Tel.SpanNames.FormatActorOperation(Tel.Operations.StartActor)); activity.SetActorAttributes(this.ActorId, "start"); @@ -72,7 +72,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos public void EnqueueMessage(ActorMessage message) { using var activity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatMessageOperation(ActorRuntimeOpenTelemetryConsts.Operations.ReceiveMessage)); + Tel.SpanNames.FormatMessageOperation(Tel.Operations.ReceiveMessage)); var messageId = message switch { @@ -95,7 +95,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos } catch (Exception ex) { - activity.RecordFailure(ex, null, (ActorRuntimeOpenTelemetryConsts.Message.Status, "failed")); + activity.RecordFailure(ex, null, (Tel.Message.Status, "failed")); throw; } } @@ -118,7 +118,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos public ActorResponseHandle SendRequest(ActorRequest request) { using var activity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatRequestOperation(ActorRuntimeOpenTelemetryConsts.Operations.ProcessRequest)); + Tel.SpanNames.FormatRequestOperation(Tel.Operations.ProcessRequest)); activity.SetupRequestOperation(this.ActorId, request.MessageId, request.Method, "ActorContext", "SendRequest"); @@ -148,9 +148,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos requestStatus = "found"; } -#pragma warning disable CA2000 // Dispose objects before losing scope var handle = new InProcessActorResponseHandle(this, entry); -#pragma warning restore CA2000 // Dispose objects before losing scope Log.ResponseHandleCreated(this._logger, this.ActorId.ToString(), request.MessageId); activity.Complete(RequestCompleted, this.ActorId, [(Tel.Request.Status, requestStatus), (Tel.Response.Status, HandleCreated)], @@ -161,7 +159,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos } catch (Exception ex) { - activity.RecordFailure(ex, null, (ActorRuntimeOpenTelemetryConsts.Request.Status, "failed")); + activity.RecordFailure(ex, null, (Tel.Request.Status, "failed")); throw; } } @@ -169,11 +167,11 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos public void OnProgressUpdate(string messageId, int sequenceNumber, JsonElement data) { using var activity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatActorOperation(ActorRuntimeOpenTelemetryConsts.Operations.ProgressUpdate)); + Tel.SpanNames.FormatActorOperation(Tel.Operations.ProgressUpdate)); activity.SetActorAttributes(this.ActorId); activity.SetMessageAttributes(messageId); - activity?.SetTag(ActorRuntimeOpenTelemetryConsts.Message.SequenceNumber, sequenceNumber); + activity?.SetTag(Tel.Message.SequenceNumber, sequenceNumber); try { @@ -181,7 +179,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos var update = new UpdateRequestOperation(messageId, RequestStatus.Pending, data); this.PostRequestUpdate(update); - activity.RecordSuccess((ActorRuntimeOpenTelemetryConsts.Message.Status, "processed")); + activity.RecordSuccess((Tel.Message.Status, "processed")); } catch (Exception ex) { @@ -259,8 +257,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos IReadOnlyCollection writeOps = [.. operations.Operations.OfType()]; - WriteResponse result; - result = await this.Storage.WriteStateAsync( + WriteResponse result = await this.Storage.WriteStateAsync( this.ActorId, writeOps, operations.ETag, @@ -368,10 +365,8 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos private sealed class InProcessActorResponseHandle(InProcessActorContext context, ActorInboxEntry entry) : ActorResponseHandle { #if NET8_0_OR_GREATER - public override async ValueTask CancelAsync(CancellationToken cancellationToken) - { + public override async ValueTask CancelAsync(CancellationToken cancellationToken) => await entry.Cts.CancelAsync().ConfigureAwait(false); - } #else public override ValueTask CancelAsync(CancellationToken cancellationToken) { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorRuntime.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorRuntime.cs index 60fe298c0d..1242e20200 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorRuntime.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/InProcessActorRuntime.cs @@ -17,19 +17,19 @@ internal sealed class InProcessActorRuntime( IReadOnlyDictionary> actorFactories, IActorStateStorage storage) { - private static readonly ActivitySource ActivitySource = new(ActorRuntimeOpenTelemetryConsts.InProcessSourceName); - private static readonly Meter Meter = new(ActorRuntimeOpenTelemetryConsts.InProcessSourceName); + private static readonly ActivitySource ActivitySource = new(Tel.InProcessSourceName); + private static readonly Meter Meter = new(Tel.InProcessSourceName); // Metrics following OpenTelemetry semantic conventions private static readonly Counter ActorCreatedCounter = Meter.CreateCounter( - ActorRuntimeOpenTelemetryConsts.Client.ActorCount.Name, - ActorRuntimeOpenTelemetryConsts.CountUnit, - ActorRuntimeOpenTelemetryConsts.Client.ActorCount.Description); + Tel.Client.ActorCount.Name, + Tel.CountUnit, + Tel.Client.ActorCount.Description); private static readonly Histogram OperationDurationHistogram = Meter.CreateHistogram( - ActorRuntimeOpenTelemetryConsts.Client.OperationDuration.Name, + Tel.Client.OperationDuration.Name, "s", - ActorRuntimeOpenTelemetryConsts.Client.OperationDuration.Description); + Tel.Client.OperationDuration.Description); private readonly object _createActorLock = new(); private readonly IReadOnlyDictionary> _actorFactories = actorFactories; @@ -44,7 +44,7 @@ internal sealed class InProcessActorRuntime( // Create span following OpenTelemetry conventions for RPC operations using var activity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatActorOperation(ActorRuntimeOpenTelemetryConsts.Operations.GetActor)); + Tel.SpanNames.FormatActorOperation(Tel.Operations.GetActor)); try { @@ -61,7 +61,7 @@ internal sealed class InProcessActorRuntime( var exception = new InvalidOperationException(errorMessage); activity.SetupActorOperation(actorId, exists: false); - activity.RecordFailure(exception, ActorRuntimeOpenTelemetryConsts.ErrorInfo.Types.ActorNotFound); + activity.RecordFailure(exception, Tel.ErrorInfo.Types.ActorNotFound); throw exception; } @@ -103,8 +103,8 @@ internal sealed class InProcessActorRuntime( // Record operation duration metric var duration = stopwatch.Elapsed.TotalSeconds; OperationDurationHistogram.Record(duration, - new KeyValuePair(ActorRuntimeOpenTelemetryConsts.Actor.Operation, ActorRuntimeOpenTelemetryConsts.Operations.GetActor), - new KeyValuePair(ActorRuntimeOpenTelemetryConsts.Actor.Type, actorId.Type.Name)); + new KeyValuePair(Tel.Actor.Operation, Tel.Operations.GetActor), + new KeyValuePair(Tel.Actor.Type, actorId.Type.Name)); } } @@ -114,7 +114,7 @@ internal sealed class InProcessActorRuntime( { // Create nested span for actor creation var createActivity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatActorOperation(ActorRuntimeOpenTelemetryConsts.Operations.CreateActor)); + Tel.SpanNames.FormatActorOperation(Tel.Operations.CreateActor)); InProcessActorContext? instance = null; try { @@ -126,7 +126,7 @@ internal sealed class InProcessActorRuntime( createActivity.Complete(ActorCreated, actorId, [(Tel.Actor.Started, true)]); // Record metrics for successful actor creation - ActorCreatedCounter.Add(1, new KeyValuePair(ActorRuntimeOpenTelemetryConsts.Actor.Type, actorId.Type.Name)); + ActorCreatedCounter.Add(1, new KeyValuePair(Tel.Actor.Type, actorId.Type.Name)); return instance; } catch (Exception ex) @@ -141,16 +141,16 @@ internal sealed class InProcessActorRuntime( internal sealed class InProcessActorClient(InProcessActorRuntime runtime) : IActorClient { - private static readonly ActivitySource ActivitySource = new(ActorRuntimeOpenTelemetryConsts.InProcessSourceName); - private static readonly Meter ClientMeter = new(ActorRuntimeOpenTelemetryConsts.InProcessSourceName); + private static readonly ActivitySource ActivitySource = new(Tel.InProcessSourceName); + private static readonly Meter ClientMeter = new(Tel.InProcessSourceName); private static readonly Counter RequestCounter = ClientMeter.CreateCounter( - ActorRuntimeOpenTelemetryConsts.Client.RequestCount.Name, - ActorRuntimeOpenTelemetryConsts.CountUnit, - ActorRuntimeOpenTelemetryConsts.Client.RequestCount.Description); + Tel.Client.RequestCount.Name, + Tel.CountUnit, + Tel.Client.RequestCount.Description); private static readonly Histogram ClientOperationDurationHistogram = ClientMeter.CreateHistogram( - ActorRuntimeOpenTelemetryConsts.Client.OperationDuration.Name, + Tel.Client.OperationDuration.Name, "s", - ActorRuntimeOpenTelemetryConsts.Client.OperationDuration.Description); + Tel.Client.OperationDuration.Description); private readonly InProcessActorRuntime _runtime = runtime; @@ -158,21 +158,17 @@ internal sealed class InProcessActorClient(InProcessActorRuntime runtime) : IAct { // Create span for get response operation using var activity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatRequestOperation(ActorRuntimeOpenTelemetryConsts.Operations.ReceiveResponse)); + Tel.SpanNames.FormatRequestOperation(Tel.Operations.ReceiveResponse)); activity.SetupRequestOperation(actorId, messageId, service: "ActorClient", rpcMethod: "GetResponse"); var actorContext = this._runtime.GetOrCreateActor(actorId); -#pragma warning disable CA2000 // Dispose objects before losing scope if (actorContext.TryGetResponseHandle(messageId, out var handle)) { return new(handle); } -#pragma warning restore CA2000 // Dispose objects before losing scope -#pragma warning disable CA2000 // Dispose objects before losing scope return new(new NotFoundActorResponseHandle(actorId, messageId)); -#pragma warning restore CA2000 // Dispose objects before losing scope } public ValueTask SendRequestAsync(ActorRequest request, CancellationToken cancellationToken) @@ -181,7 +177,7 @@ internal sealed class InProcessActorClient(InProcessActorRuntime runtime) : IAct // Create span for send request operation following RPC client conventions using var activity = ActivitySource.StartActivity( - ActorRuntimeOpenTelemetryConsts.SpanNames.FormatRequestOperation(ActorRuntimeOpenTelemetryConsts.Operations.SendRequest)); + Tel.SpanNames.FormatRequestOperation(Tel.Operations.SendRequest)); try { @@ -190,9 +186,7 @@ internal sealed class InProcessActorClient(InProcessActorRuntime runtime) : IAct // Ensure the message is enqueued on the actor's inbox, getting a response handle for it. var actorId = request.ActorId; var actorContext = this._runtime.GetOrCreateActor(actorId); -#pragma warning disable CA2000 // Dispose objects before losing scope var response = actorContext.SendRequest(request); -#pragma warning restore CA2000 // Dispose objects before losing scope activity.Complete(MessageSent, actorId, Sent, (Tel.Message.Id, request.MessageId)); @@ -205,7 +199,7 @@ internal sealed class InProcessActorClient(InProcessActorRuntime runtime) : IAct } catch (Exception ex) { - activity.RecordFailure(ex, null, (ActorRuntimeOpenTelemetryConsts.Request.Status, "failed")); + activity.RecordFailure(ex, null, (Tel.Request.Status, "failed")); throw; } finally @@ -213,8 +207,8 @@ internal sealed class InProcessActorClient(InProcessActorRuntime runtime) : IAct // Record operation duration var duration = stopwatch.Elapsed.TotalSeconds; ClientOperationDurationHistogram.Record(duration, - new KeyValuePair(ActorRuntimeOpenTelemetryConsts.Actor.Operation, ActorRuntimeOpenTelemetryConsts.Operations.SendRequest), - new KeyValuePair(ActorRuntimeOpenTelemetryConsts.Actor.Type, request.ActorId.Type.Name)); + new KeyValuePair(Tel.Actor.Operation, Tel.Operations.SendRequest), + new KeyValuePair(Tel.Actor.Type, request.ActorId.Type.Name)); } } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/NotFoundActorResponseHandle.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/NotFoundActorResponseHandle.cs index d3a7f0e5f0..ecd90de93a 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/NotFoundActorResponseHandle.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime/NotFoundActorResponseHandle.cs @@ -23,16 +23,12 @@ internal sealed class NotFoundActorResponseHandle : ActorResponseHandle }; } - public override ValueTask CancelAsync(CancellationToken cancellationToken) - { + public override ValueTask CancelAsync(CancellationToken cancellationToken) => throw new InvalidOperationException( $"Failed to cancel request for actor '{this._response.ActorId}' with message ID '{this._response.MessageId}'. The request was not found."); - } - public override ValueTask GetResponseAsync(CancellationToken cancellationToken) - { - return new ValueTask(this._response); - } + public override ValueTask GetResponseAsync(CancellationToken cancellationToken) => + new(this._response); public override bool TryGetResponse([NotNullWhen(true)] out ActorResponse? response) { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/AgentExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/AgentExtensions.cs index 4ceec57304..239d22b57c 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/AgentExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/AgentExtensions.cs @@ -21,13 +21,11 @@ public static class AgentExtensions /// An optional source name that will be used on the telemetry data. /// When indicates whether potentially sensitive information should be included in telemetry. Default is /// An that wraps the original agent with telemetry. - public static OpenTelemetryAgent WithOpenTelemetry(this AIAgent agent, ILoggerFactory? loggerFactory = null, string? sourceName = null, bool? enableSensitiveData = null) - { - return new OpenTelemetryAgent(agent, loggerFactory?.CreateLogger(typeof(OpenTelemetryAgent)), sourceName) + public static OpenTelemetryAgent WithOpenTelemetry(this AIAgent agent, ILoggerFactory? loggerFactory = null, string? sourceName = null, bool? enableSensitiveData = null) => + new(agent, loggerFactory?.CreateLogger(typeof(OpenTelemetryAgent)), sourceName) { EnableSensitiveData = enableSensitiveData ?? false }; - } /// /// Creates a that will invoke the provided Agent. diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/AgentChatClientBuilderExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/AgentChatClientBuilderExtensions.cs index 15433dbd68..9c305b4922 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/AgentChatClientBuilderExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/AgentChatClientBuilderExtensions.cs @@ -21,8 +21,6 @@ public static class AgentChatClientBuilderExtensions _ = Throw.IfNull(builder); return builder.Use((innerClient, services) => - { - return new AgentInvokedChatClient(innerClient); - }); + new AgentInvokedChatClient(innerClient)); } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs index 982cb2799a..35a42bc0b5 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs @@ -95,7 +95,7 @@ public sealed class ChatClientAgent : AIAgent public string? Instructions => this._agentOptions?.Instructions; /// - /// Gets of the default used by the agent. + /// Gets of the default used by the agent. /// internal ChatOptions? ChatOptions => this._agentOptions?.ChatOptions; @@ -229,24 +229,19 @@ public sealed class ChatClientAgent : AIAgent } /// - public override object? GetService(Type serviceType, object? serviceKey = null) - { - return base.GetService(serviceType, serviceKey) + public override object? GetService(Type serviceType, object? serviceKey = null) => + base.GetService(serviceType, serviceKey) ?? (serviceType == typeof(AIAgentMetadata) ? this._agentMetadata : serviceType == typeof(IChatClient) ? this.ChatClient : this.ChatClient.GetService(serviceType, serviceKey)); - } /// - public override AgentThread GetNewThread() - { - var thread = new AgentThread + public override AgentThread GetNewThread() => + new() { MessageStore = this._agentOptions?.ChatMessageStoreFactory?.Invoke(default, null), AIContextProvider = this._agentOptions?.AIContextProviderFactory?.Invoke(default, null) }; - return thread; - } /// public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) @@ -455,7 +450,7 @@ public sealed class ChatClientAgent : AIAgent { throw new InvalidOperationException( $""" - The {nameof(chatOptions.ConversationId)} provided via {nameof(Microsoft.Extensions.AI.ChatOptions)} is different to the id of the provided {nameof(AgentThread)}. + The {nameof(chatOptions.ConversationId)} provided via {nameof(AI.ChatOptions)} is different to the id of the provided {nameof(AgentThread)}. Only one id can be used for a run. """); } @@ -493,12 +488,12 @@ public sealed class ChatClientAgent : AIAgent // so we should update the thread with the new id. thread.ConversationId = responseConversationId; } - else if (thread.MessageStore is null) + else { // If the service doesn't use service side thread storage (i.e. we got no id back from invocation), and // the thread has no MessageStore yet, and we have a custom messages store, we should update the thread // with the custom MessageStore so that it has somewhere to store the chat history. - thread.MessageStore = this._agentOptions?.ChatMessageStoreFactory?.Invoke(default, null); + thread.MessageStore ??= this._agentOptions?.ChatMessageStoreFactory?.Invoke(default, null); } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs index 1abe056741..34aa7369cf 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System; using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -21,7 +20,7 @@ internal static class ChatClientExtensions if (chatClient.GetService() is null) { - _ = chatBuilder.Use((IChatClient innerClient, IServiceProvider services) => + _ = chatBuilder.Use((innerClient, services) => { var loggerFactory = services.GetService(); diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs index 90f547a1c2..d035471707 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs @@ -130,7 +130,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable { var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); - using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, inputMessages, thread); + using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, thread); Stopwatch? stopwatch = this._operationDurationHistogram.Enabled ? Stopwatch.StartNew() : null; this.LogChatMessages(inputMessages); @@ -149,7 +149,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable } finally { - this.TraceResponse(activity, response, error, stopwatch, inputMessages.Count, isStreaming: false); + this.TraceResponse(activity, response, error, stopwatch); } } @@ -162,7 +162,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable { var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); - using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, inputMessages, thread); + using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, thread); Stopwatch? stopwatch = this._operationDurationHistogram.Enabled ? Stopwatch.StartNew() : null; IAsyncEnumerable updates; @@ -172,7 +172,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable } catch (Exception ex) { - this.TraceResponse(activity, response: null, ex, stopwatch, inputMessages.Count, isStreaming: true); + this.TraceResponse(activity, response: null, ex, stopwatch); throw; } @@ -206,7 +206,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable } finally { - this.TraceResponse(activity, trackedUpdates.ToAgentRunResponse(), error, stopwatch, inputMessages.Count, isStreaming: true); + this.TraceResponse(activity, trackedUpdates.ToAgentRunResponse(), error, stopwatch); await responseEnumerator.DisposeAsync().ConfigureAwait(false); } } @@ -214,7 +214,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable /// /// Creates an activity for an agent request, or returns null if not enabled. /// - private Activity? CreateAndConfigureActivity(string operationName, IReadOnlyCollection messages, AgentThread? thread) + private Activity? CreateAndConfigureActivity(string operationName, AgentThread? thread) { // Get the GenAI system name for telemetry var chatClientAgent = this.InnerAgent as ChatClientAgent; @@ -280,9 +280,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable Activity? activity, AgentRunResponse? response, Exception? error, - Stopwatch? stopwatch, - int inputMessageCount, - bool isStreaming) + Stopwatch? stopwatch) { // Record operation duration metric if (this._operationDurationHistogram.Enabled && stopwatch is not null) @@ -413,22 +411,6 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable } } - private void LogChatResponse(ChatResponse response) - { - if (!this._logger.IsEnabled(EventLogLevel)) - { - return; - } - - EventId id = new(1, OpenTelemetryConsts.GenAI.Choice); - this.Log(id, JsonSerializer.Serialize(new ChoiceEvent() - { - FinishReason = response.FinishReason?.Value ?? "error", - Index = 0, - Message = this.CreateAssistantEvent(response.Messages is { Count: 1 } ? response.Messages[0].Contents : response.Messages.SelectMany(m => m.Contents)), - }, OtelContext.Default.ChoiceEvent)); - } - private void LogAgentResponse(AgentRunResponse response) { if (this._openTelemetryChatClient is not null) diff --git a/dotnet/src/Shared/Demos/SampleEnvironment.cs b/dotnet/src/Shared/Demos/SampleEnvironment.cs index 12b326bd69..c2adccb845 100644 --- a/dotnet/src/Shared/Demos/SampleEnvironment.cs +++ b/dotnet/src/Shared/Demos/SampleEnvironment.cs @@ -11,7 +11,7 @@ namespace SampleHelpers; internal static class SampleEnvironment { public static string? GetEnvironmentVariable(string key) - => SampleEnvironment.GetEnvironmentVariable(key, EnvironmentVariableTarget.Process); + => GetEnvironmentVariable(key, EnvironmentVariableTarget.Process); public static string? GetEnvironmentVariable(string key, EnvironmentVariableTarget target) { @@ -70,73 +70,73 @@ internal static class SampleEnvironment // Methods that directly call System.Environment public static IDictionary GetEnvironmentVariables() - => System.Environment.GetEnvironmentVariables(); + => SystemEnvironment.GetEnvironmentVariables(); public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target) - => System.Environment.GetEnvironmentVariables(target); + => SystemEnvironment.GetEnvironmentVariables(target); public static void SetEnvironmentVariable(string variable, string? value) - => System.Environment.SetEnvironmentVariable(variable, value); + => SystemEnvironment.SetEnvironmentVariable(variable, value); public static void SetEnvironmentVariable(string variable, string? value, EnvironmentVariableTarget target) - => System.Environment.SetEnvironmentVariable(variable, value, target); + => SystemEnvironment.SetEnvironmentVariable(variable, value, target); public static string[] GetCommandLineArgs() - => System.Environment.GetCommandLineArgs(); + => SystemEnvironment.GetCommandLineArgs(); public static string CommandLine - => System.Environment.CommandLine; + => SystemEnvironment.CommandLine; public static string CurrentDirectory { - get => System.Environment.CurrentDirectory; - set => System.Environment.CurrentDirectory = value; + get => SystemEnvironment.CurrentDirectory; + set => SystemEnvironment.CurrentDirectory = value; } public static string ExpandEnvironmentVariables(string name) - => System.Environment.ExpandEnvironmentVariables(name); + => SystemEnvironment.ExpandEnvironmentVariables(name); - public static string GetFolderPath(System.Environment.SpecialFolder folder) - => System.Environment.GetFolderPath(folder); + public static string GetFolderPath(SystemEnvironment.SpecialFolder folder) + => SystemEnvironment.GetFolderPath(folder); - public static string GetFolderPath(System.Environment.SpecialFolder folder, System.Environment.SpecialFolderOption option) - => System.Environment.GetFolderPath(folder, option); + public static string GetFolderPath(SystemEnvironment.SpecialFolder folder, SystemEnvironment.SpecialFolderOption option) + => SystemEnvironment.GetFolderPath(folder, option); public static int ProcessorCount - => System.Environment.ProcessorCount; + => SystemEnvironment.ProcessorCount; public static bool Is64BitProcess - => System.Environment.Is64BitProcess; + => SystemEnvironment.Is64BitProcess; public static bool Is64BitOperatingSystem - => System.Environment.Is64BitOperatingSystem; + => SystemEnvironment.Is64BitOperatingSystem; public static string MachineName - => System.Environment.MachineName; + => SystemEnvironment.MachineName; public static string NewLine - => System.Environment.NewLine; + => SystemEnvironment.NewLine; public static OperatingSystem OSVersion - => System.Environment.OSVersion; + => SystemEnvironment.OSVersion; public static string StackTrace - => System.Environment.StackTrace; + => SystemEnvironment.StackTrace; public static int SystemPageSize - => System.Environment.SystemPageSize; + => SystemEnvironment.SystemPageSize; public static bool HasShutdownStarted - => System.Environment.HasShutdownStarted; + => SystemEnvironment.HasShutdownStarted; #if NET9_0_OR_GREATER public static int ProcessId - => System.Environment.ProcessId; + => SystemEnvironment.ProcessId; public static string? ProcessPath - => System.Environment.ProcessPath; + => SystemEnvironment.ProcessPath; public static bool IsPrivilegedProcess - => System.Environment.IsPrivilegedProcess; + => SystemEnvironment.IsPrivilegedProcess; #endif } diff --git a/dotnet/src/Shared/Samples/BaseSample.cs b/dotnet/src/Shared/Samples/BaseSample.cs index 020e98070c..420ba1920b 100644 --- a/dotnet/src/Shared/Samples/BaseSample.cs +++ b/dotnet/src/Shared/Samples/BaseSample.cs @@ -40,7 +40,7 @@ public abstract class BaseSample : TextWriter public BaseSample Console => this; /// - public override Encoding Encoding => System.Text.Encoding.UTF8; + public override Encoding Encoding => Encoding.UTF8; /// /// Initializes a new instance of the class, setting up logging, configuration, and @@ -79,10 +79,8 @@ public abstract class BaseSample : TextWriter /// Writes a user message to the console. /// /// The text of the message to be sent. Cannot be null or empty. - protected void WriteUserMessage(string message) - { + protected void WriteUserMessage(string message) => this.WriteMessageOutput(new ChatMessage(ChatRole.User, message)); - } /// /// Processes and writes the latest agent chat response to the console, including metadata and content details. @@ -124,9 +122,9 @@ public abstract class BaseSample : TextWriter { string authorExpression = message.Role == ChatRole.User ? string.Empty : FormatAuthor(); string contentExpression = message.Text.Trim(); - bool isCode = false; //message.AdditionalProperties?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false; - string codeMarker = isCode ? "\n [CODE]\n" : " "; - Console.WriteLine($"\n# {message.Role}{authorExpression}:{codeMarker}{contentExpression}"); + const bool IsCode = false; //message.AdditionalProperties?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false; + const string CodeMarker = IsCode ? "\n [CODE]\n" : " "; + Console.WriteLine($"\n# {message.Role}{authorExpression}:{CodeMarker}{contentExpression}"); // Provide visibility for inner content (that isn't TextContent). foreach (AIContent item in message.Contents) @@ -166,9 +164,9 @@ public abstract class BaseSample : TextWriter string authorExpression = update.Role == ChatRole.User ? string.Empty : FormatAuthor(); string contentExpression = string.IsNullOrWhiteSpace(update.Text) ? string.Empty : update.Text; - bool isCode = false; //message.AdditionalProperties?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false; - string codeMarker = isCode ? "\n [CODE]\n" : " "; - Console.WriteLine($"\n# {update.Role}{authorExpression}:{codeMarker}{contentExpression}"); + const bool IsCode = false; //message.AdditionalProperties?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false; + const string CodeMarker = IsCode ? "\n [CODE]\n" : " "; + Console.WriteLine($"\n# {update.Role}{authorExpression}:{CodeMarker}{contentExpression}"); // Provide visibility for inner content (that isn't TextContent). foreach (AIContent item in update.Contents) diff --git a/dotnet/src/Shared/Samples/OrchestrationSample.cs b/dotnet/src/Shared/Samples/OrchestrationSample.cs index 3a4a908cc4..b789629d87 100644 --- a/dotnet/src/Shared/Samples/OrchestrationSample.cs +++ b/dotnet/src/Shared/Samples/OrchestrationSample.cs @@ -24,36 +24,25 @@ public abstract class OrchestrationSample : BaseSample /// An optional name for the agent. /// A set of instances to be used as tools by the agent. /// A new instance configured with the provided parameters. - protected ChatClientAgent CreateAgent(string instructions, string? description = null, string? name = null, params AIFunction[] functions) - { - // Get the chat client to use for the agent. - using IChatClient chatClient = CreateChatClient(); - - ChatClientAgentOptions options = - new() - { - Name = name, - Description = description, - Instructions = instructions, - ChatOptions = new() { Tools = functions, ToolMode = ChatToolMode.Auto } - }; - - return new ChatClientAgent(chatClient, options); - } + protected static ChatClientAgent CreateAgent(string instructions, string? description = null, string? name = null, params AIFunction[] functions) => + new(CreateChatClient(), new ChatClientAgentOptions() + { + Name = name, + Description = description, + Instructions = instructions, + ChatOptions = new() { Tools = functions, ToolMode = ChatToolMode.Auto } + }); /// /// Creates and configures a new instance using the OpenAI client and test configuration. /// /// A configured instance ready for use with agents. - protected IChatClient CreateChatClient() - { - return new OpenAIClient(TestConfiguration.OpenAI.ApiKey) - .GetChatClient(TestConfiguration.OpenAI.ChatModelId) - .AsIChatClient() - .AsBuilder() - .UseFunctionInvocation() - .Build(); - } + protected static IChatClient CreateChatClient() => new OpenAIClient(TestConfiguration.OpenAI.ApiKey) + .GetChatClient(TestConfiguration.OpenAI.ChatModelId) + .AsIChatClient() + .AsBuilder() + .UseFunctionInvocation() + .Build(); /// /// Display the provided history. @@ -129,7 +118,7 @@ public abstract class OrchestrationSample : BaseSample /// /// The collection of objects to process. /// A representing the asynchronous operation. - public ValueTask ResponseCallback(IEnumerable response) + public ValueTask ResponseCallbackAsync(IEnumerable response) { WriteStreamedResponse(this.StreamedResponses); this.StreamedResponses.Clear(); @@ -144,7 +133,7 @@ public abstract class OrchestrationSample : BaseSample /// /// The to process. /// A representing the asynchronous operation. - public ValueTask StreamingResultCallback(AgentRunResponseUpdate streamedResponse) + public ValueTask StreamingResultCallbackAsync(AgentRunResponseUpdate streamedResponse) { this.StreamedResponses.Add(streamedResponse); return default; @@ -153,16 +142,16 @@ public abstract class OrchestrationSample : BaseSample /// /// Initializes a new instance of the class, setting up logging, configuration, and - /// optionally redirecting output to the test output. + /// optionally redirecting output to the test output. /// /// This constructor initializes logging using an and sets up /// configuration from multiple sources, including a JSON file, environment variables, and user secrets. - /// If is , calls to + /// If is , calls to /// will be redirected to the test output provided by . /// /// The instance used to write test output. /// - /// A value indicating whether output should be redirected to the test output. to redirect; otherwise, . + /// A value indicating whether output should be redirected to the test output. to redirect; otherwise, . /// protected OrchestrationSample(ITestOutputHelper output, bool redirectSystemConsoleOutput = true) : base(output, redirectSystemConsoleOutput) diff --git a/dotnet/src/Shared/Samples/TestConfiguration.cs b/dotnet/src/Shared/Samples/TestConfiguration.cs index cb072649aa..ec85a97a1e 100644 --- a/dotnet/src/Shared/Samples/TestConfiguration.cs +++ b/dotnet/src/Shared/Samples/TestConfiguration.cs @@ -60,10 +60,8 @@ public sealed class TestConfiguration /// Initializes the configuration system with the specified configuration root. /// /// The root of the configuration hierarchy used to initialize the system. Must not be . - public static void Initialize(IConfigurationRoot configRoot) - { + public static void Initialize(IConfigurationRoot configRoot) => s_instance = new TestConfiguration(configRoot); - } #region Private Members diff --git a/dotnet/src/Shared/Samples/TextOutputHelperExtensions.cs b/dotnet/src/Shared/Samples/TextOutputHelperExtensions.cs index 6c8cbb7a5e..c1535af0dc 100644 --- a/dotnet/src/Shared/Samples/TextOutputHelperExtensions.cs +++ b/dotnet/src/Shared/Samples/TextOutputHelperExtensions.cs @@ -12,36 +12,28 @@ public static class TextOutputHelperExtensions /// /// Target /// Target object to write - public static void WriteLine(this ITestOutputHelper testOutputHelper, object target) - { + public static void WriteLine(this ITestOutputHelper testOutputHelper, object target) => testOutputHelper.WriteLine(target.ToString()); - } /// /// Current interface ITestOutputHelper does not have a WriteLine method that takes no parameters. This extension method adds it to make it analogous to Console.WriteLine when used in Console apps. /// /// Target - public static void WriteLine(this ITestOutputHelper testOutputHelper) - { + public static void WriteLine(this ITestOutputHelper testOutputHelper) => testOutputHelper.WriteLine(string.Empty); - } /// /// Current interface ITestOutputHelper does not have a Write method that takes no parameters. This extension method adds it to make it analogous to Console.Write when used in Console apps. /// /// Target - public static void Write(this ITestOutputHelper testOutputHelper) - { + public static void Write(this ITestOutputHelper testOutputHelper) => testOutputHelper.WriteLine(string.Empty); - } /// /// Current interface ITestOutputHelper does not have a Write method. This extension method adds it to make it analogous to Console.Write when used in Console apps. /// /// Target /// Target object to write - public static void Write(this ITestOutputHelper testOutputHelper, object target) - { + public static void Write(this ITestOutputHelper testOutputHelper, object target) => testOutputHelper.WriteLine(target.ToString()); - } } diff --git a/dotnet/src/Shared/Throw/Throw.cs b/dotnet/src/Shared/Throw/Throw.cs index 5566034aba..42feb36740 100644 --- a/dotnet/src/Shared/Throw/Throw.cs +++ b/dotnet/src/Shared/Throw/Throw.cs @@ -126,7 +126,7 @@ internal static partial class Throw public static string IfNullOrWhitespace([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") { #if !NETCOREAPP3_1_OR_GREATER - if (argument == null) + if (argument is null) { ArgumentNullException(paramName); } @@ -134,7 +134,7 @@ internal static partial class Throw if (string.IsNullOrWhiteSpace(argument)) { - if (argument == null) + if (argument is null) { ArgumentNullException(paramName); } @@ -159,7 +159,7 @@ internal static partial class Throw public static string IfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") { #if !NETCOREAPP3_1_OR_GREATER - if (argument == null) + if (argument is null) { ArgumentNullException(paramName); } @@ -167,7 +167,7 @@ internal static partial class Throw if (string.IsNullOrEmpty(argument)) { - if (argument == null) + if (argument is null) { ArgumentNullException(paramName); } @@ -215,7 +215,7 @@ internal static partial class Throw where T : struct, Enum { #if NET5_0_OR_GREATER - if (!Enum.IsDefined(argument)) + if (!Enum.IsDefined(argument)) #else if (!Enum.IsDefined(typeof(T), argument)) #endif @@ -248,7 +248,7 @@ internal static partial class Throw [ExcludeFromCodeCoverage] public static IEnumerable IfNullOrEmpty([NotNull] IEnumerable? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") { - if (argument == null) + if (argument is null) { ArgumentNullException(paramName); } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs index f6ceb9819c..5ffe731b44 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs @@ -23,8 +23,5 @@ public abstract class AgentTests(Func createAgentF return this.Fixture.InitializeAsync(); } - public Task DisposeAsync() - { - return this.Fixture.DisposeAsync(); - } + public Task DisposeAsync() => this.Fixture.DisposeAsync(); } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/MenuPlugin.cs b/dotnet/tests/AgentConformance.IntegrationTests/MenuPlugin.cs index ad78c770a1..1b9016e35b 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/MenuPlugin.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/MenuPlugin.cs @@ -12,21 +12,14 @@ namespace AgentConformance.IntegrationTests; internal static class MenuPlugin { [Description("Provides a list of specials from the menu.")] - public static string GetSpecials() - { - return - """ - Special Soup: Clam Chowder - Special Salad: Cobb Salad - Special Drink: Chai Tea - """; - } + public static string GetSpecials() => """ + Special Soup: Clam Chowder + Special Salad: Cobb Salad + Special Drink: Chai Tea + """; [Description("Provides the price of the requested menu item.")] public static string GetItemPrice( [Description("The name of the menu item.")] - string menuItem) - { - return "$9.99"; - } + string menuItem) => "$9.99"; } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs index 4e3bc65dcd..984356affb 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs @@ -85,15 +85,15 @@ public abstract class RunStreamingTests(Func creat public virtual async Task ThreadMaintainsHistoryAsync() { // Arrange - var q1 = "What is the capital of France."; - var q2 = "And Austria?"; + const string Q1 = "What is the capital of France."; + const string Q2 = "And Austria?"; var agent = this.Fixture.Agent; var thread = agent.GetNewThread(); await using var cleanup = new ThreadCleanup(thread, this.Fixture); // Act - var responseUpdates1 = await agent.RunStreamingAsync(q1, thread).ToListAsync(); - var responseUpdates2 = await agent.RunStreamingAsync(q2, thread).ToListAsync(); + var responseUpdates1 = await agent.RunStreamingAsync(Q1, thread).ToListAsync(); + var responseUpdates2 = await agent.RunStreamingAsync(Q2, thread).ToListAsync(); // Assert var response1Text = string.Concat(responseUpdates1.Select(x => x.Text)); @@ -105,8 +105,8 @@ public abstract class RunStreamingTests(Func creat 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)); - Assert.Equal(q1, chatHistory[0].Text); - Assert.Equal(q2, chatHistory[2].Text); + Assert.Equal(Q1, chatHistory[0].Text); + Assert.Equal(Q2, chatHistory[2].Text); Assert.Contains("Paris", chatHistory[1].Text); Assert.Contains("Vienna", chatHistory[3].Text); } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs index d2b34eb419..f89c821455 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs @@ -92,15 +92,15 @@ public abstract class RunTests(Func createAgentFix public virtual async Task ThreadMaintainsHistoryAsync() { // Arrange - var q1 = "What is the capital of France."; - var q2 = "And Austria?"; + const string Q1 = "What is the capital of France."; + const string Q2 = "And Austria?"; var agent = this.Fixture.Agent; var thread = agent.GetNewThread(); await using var cleanup = new ThreadCleanup(thread, this.Fixture); // Act - var result1 = await agent.RunAsync(q1, thread); - var result2 = await agent.RunAsync(q2, thread); + var result1 = await agent.RunAsync(Q1, thread); + var result2 = await agent.RunAsync(Q2, thread); // Assert Assert.Contains("Paris", result1.Text); @@ -110,8 +110,8 @@ public abstract class RunTests(Func createAgentFix 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)); - Assert.Equal(q1, chatHistory[0].Text); - Assert.Equal(q2, chatHistory[2].Text); + Assert.Equal(Q1, chatHistory[0].Text); + Assert.Equal(Q2, chatHistory[2].Text); Assert.Contains("Paris", chatHistory[1].Text); Assert.Contains("Vienna", chatHistory[3].Text); } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/Support/AgentCleanup.cs b/dotnet/tests/AgentConformance.IntegrationTests/Support/AgentCleanup.cs index c46ccb4b4d..adb5dafc5f 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/Support/AgentCleanup.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/Support/AgentCleanup.cs @@ -13,8 +13,6 @@ namespace AgentConformance.IntegrationTests.Support; /// The fixture that provides agent specific capabilities. internal sealed class AgentCleanup(ChatClientAgent agent, IChatClientAgentFixture fixture) : IAsyncDisposable { - public async ValueTask DisposeAsync() - { + public async ValueTask DisposeAsync() => await fixture.DeleteAgentAsync(agent); - } } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs b/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs index 2284566c96..e56eeff3cb 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs @@ -28,10 +28,10 @@ public sealed class TestConfiguration var configType = typeof(T); var configTypeName = configType.Name; - var trimText = "Configuration"; - if (configTypeName.EndsWith(trimText, StringComparison.OrdinalIgnoreCase)) + const string TrimText = "Configuration"; + if (configTypeName.EndsWith(TrimText, StringComparison.OrdinalIgnoreCase)) { - configTypeName = configTypeName.Substring(0, configTypeName.Length - trimText.Length); + configTypeName = configTypeName.Substring(0, configTypeName.Length - TrimText.Length); } return s_configuration.GetRequiredSection(configTypeName).Get() ?? diff --git a/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs b/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs index 690c4e0fed..3458053456 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs @@ -13,8 +13,6 @@ namespace AgentConformance.IntegrationTests.Support; /// The fixture that provides agent specific capabilities. internal sealed class ThreadCleanup(AgentThread thread, IAgentFixture fixture) : IAsyncDisposable { - public async ValueTask DisposeAsync() - { + public async ValueTask DisposeAsync() => await fixture.DeleteThreadAsync(thread); - } } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs index 00519e40d4..0d24df4d44 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs @@ -30,9 +30,8 @@ public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture { List messages = []; - AsyncPageable threadMessages = this._persistentAgentsClient.Messages.GetMessagesAsync(threadId: thread.ConversationId, order: ListSortOrder.Ascending); - - await foreach (var threadMessage in threadMessages) + await foreach (var threadMessage in (AsyncPageable)this._persistentAgentsClient.Messages.GetMessagesAsync( + threadId: thread.ConversationId, order: ListSortOrder.Ascending)) { var message = new ChatMessage { @@ -74,10 +73,8 @@ public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture }); } - public Task DeleteAgentAsync(ChatClientAgent agent) - { - return this._persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id); - } + public Task DeleteAgentAsync(ChatClientAgent agent) => + this._persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id); public Task DeleteThreadAsync(AgentThread thread) { diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs index 0f575e141d..6fde585c3e 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs @@ -19,21 +19,16 @@ namespace CopilotStudio.IntegrationTests; public class CopilotStudioFixture : IAgentFixture { #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - private AIAgent _agent; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - public AIAgent Agent => this._agent; + public AIAgent Agent { get; private set; } = null!; - public Task> GetChatHistoryAsync(AgentThread thread) - { + public Task> GetChatHistoryAsync(AgentThread thread) => throw new NotSupportedException("CopilotStudio doesn't allow retrieval of chat history."); - } - public Task DeleteThreadAsync(AgentThread thread) - { + public Task DeleteThreadAsync(AgentThread thread) => // Chat Completion does not require/support deleting threads, so this is a no-op. - return Task.CompletedTask; - } + Task.CompletedTask; public Task InitializeAsync() { @@ -60,13 +55,10 @@ public class CopilotStudioFixture : IAgentFixture CopilotClient client = new(settings, httpClientFactory, NullLogger.Instance, CopilotStudioHttpClientName); - this._agent = new CopilotStudioAgent(client); + this.Agent = new CopilotStudioAgent(client); return Task.CompletedTask; } - public Task DisposeAsync() - { - return Task.CompletedTask; - } + public Task DisposeAsync() => Task.CompletedTask; } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs index 09412f6733..4f4a670f4e 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs @@ -11,32 +11,22 @@ public class CopilotStudioRunStreamingTests() : RunStreamingTests + Task.CompletedTask; [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessageReturnsExpectedResultAsync() - { - return base.RunWithChatMessageReturnsExpectedResultAsync(); - } + public override Task RunWithChatMessageReturnsExpectedResultAsync() => + base.RunWithChatMessageReturnsExpectedResultAsync(); [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessagesReturnsExpectedResultAsync() - { - return base.RunWithChatMessagesReturnsExpectedResultAsync(); - } + public override Task RunWithChatMessagesReturnsExpectedResultAsync() => + base.RunWithChatMessagesReturnsExpectedResultAsync(); [Fact(Skip = ManualVerification)] - public override Task RunWithNoMessageDoesNotFailAsync() - { - return base.RunWithNoMessageDoesNotFailAsync(); - } + public override Task RunWithNoMessageDoesNotFailAsync() => + base.RunWithNoMessageDoesNotFailAsync(); [Fact(Skip = ManualVerification)] - public override Task RunWithStringReturnsExpectedResultAsync() - { - return base.RunWithStringReturnsExpectedResultAsync(); - } + public override Task RunWithStringReturnsExpectedResultAsync() => + base.RunWithStringReturnsExpectedResultAsync(); } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs index e598686bec..9a89db2326 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs @@ -11,32 +11,22 @@ public class CopilotStudioRunTests() : RunTests(() => new( 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() - { - return Task.CompletedTask; - } + public override Task ThreadMaintainsHistoryAsync() => + Task.CompletedTask; [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessageReturnsExpectedResultAsync() - { - return base.RunWithChatMessageReturnsExpectedResultAsync(); - } + public override Task RunWithChatMessageReturnsExpectedResultAsync() => base.RunWithChatMessageReturnsExpectedResultAsync(); [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessagesReturnsExpectedResultAsync() - { - return base.RunWithChatMessagesReturnsExpectedResultAsync(); - } + public override Task RunWithChatMessagesReturnsExpectedResultAsync() => + + base.RunWithChatMessagesReturnsExpectedResultAsync(); [Fact(Skip = ManualVerification)] - public override Task RunWithNoMessageDoesNotFailAsync() - { - return base.RunWithNoMessageDoesNotFailAsync(); - } + public override Task RunWithNoMessageDoesNotFailAsync() => + base.RunWithNoMessageDoesNotFailAsync(); [Fact(Skip = ManualVerification)] - public override Task RunWithStringReturnsExpectedResultAsync() - { - return base.RunWithStringReturnsExpectedResultAsync(); - } + public override Task RunWithStringReturnsExpectedResultAsync() => + base.RunWithStringReturnsExpectedResultAsync(); } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioTokenHandler.cs b/dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioTokenHandler.cs index c4e4539524..e1700d50b3 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioTokenHandler.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioTokenHandler.cs @@ -135,8 +135,6 @@ internal sealed class CopilotStudioTokenHandler : HttpClientHandler storageProperties.WithMacKeyChain(KeyChainServiceName, KeyChainAccountName); } - MsalCacheHelper tokenCacheHelper = await MsalCacheHelper.CreateAsync(storageProperties.Build()).ConfigureAwait(false); - - return tokenCacheHelper; + return await MsalCacheHelper.CreateAsync(storageProperties.Build()).ConfigureAwait(false); } } diff --git a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageConcurrencyTests.cs b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageConcurrencyTests.cs index 03edf21f97..4ccdda837f 100644 --- a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageConcurrencyTests.cs +++ b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageConcurrencyTests.cs @@ -4,8 +4,6 @@ using System.Text.Json; namespace Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests; -#pragma warning disable CA5394 // Insecure randomness is okay for test purposes - /// /// Integration tests for CosmosActorStateStorage focusing on concurrency control and ETag progression. /// @@ -34,20 +32,20 @@ public class CosmosActorStateStorageConcurrencyTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key = "testKey"; + const string Key = "testKey"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); // Act - First write - var operations1 = new List { new SetValueOperation(key, value1) }; + var operations1 = new List { new SetValueOperation(Key, value1) }; var result1 = await storage.WriteStateAsync(testActorId, operations1, "0", cancellationToken); // Act - Second write - var operations2 = new List { new SetValueOperation(key, value2) }; + var operations2 = new List { new SetValueOperation(Key, value2) }; var result2 = await storage.WriteStateAsync(testActorId, operations2, result1.ETag, cancellationToken); // Act - Third write - var operations3 = new List { new RemoveKeyOperation(key) }; + var operations3 = new List { new RemoveKeyOperation(Key) }; var result3 = await storage.WriteStateAsync(testActorId, operations3, result2.ETag, cancellationToken); // Assert @@ -188,11 +186,11 @@ public class CosmosActorStateStorageConcurrencyTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // Act & Assert - Test null eTag (should create new document) @@ -230,7 +228,7 @@ public class CosmosActorStateStorageConcurrencyTests // Act & Assert - Test writing with correct eTag should succeed var updateOperations = new List { - new SetValueOperation(key, JsonSerializer.SerializeToElement("updatedValue")) + new SetValueOperation(Key, JsonSerializer.SerializeToElement("updatedValue")) }; var resultWithCorrectETag = await storage.WriteStateAsync(uniqueActorId2, updateOperations, resultWithInitialETag.ETag, cancellationToken); Assert.True(resultWithCorrectETag.Success); @@ -240,7 +238,7 @@ public class CosmosActorStateStorageConcurrencyTests // Verify the value was actually updated var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var readResult = await storage.ReadStateAsync(uniqueActorId2, readOperations, cancellationToken); var getValue = readResult.Results[0] as GetValueResult; @@ -261,13 +259,13 @@ public class CosmosActorStateStorageConcurrencyTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); // Fresh actor - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); // Act - Read state from non-existent actor (this calls GetActorETagAsync internally) var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var readResult = await storage.ReadStateAsync(testActorId, readOperations, cancellationToken); @@ -281,7 +279,7 @@ public class CosmosActorStateStorageConcurrencyTests // Act - Write using the ETag from the read operation var writeOperations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; var writeResult = await storage.WriteStateAsync(testActorId, writeOperations, readResult.ETag, cancellationToken); @@ -311,16 +309,16 @@ public class CosmosActorStateStorageConcurrencyTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); // Non-existent actor - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // Act - Try to write with a completely fabricated/invalid ETag (no document exists) - var fabricatedETag = "\"fabricated-etag-12345\""; // Made-up ETag for non-existent document - var resultWithFabricatedETag = await storage.WriteStateAsync(testActorId, operations, fabricatedETag, cancellationToken); + const string FabricatedETag = "\"fabricated-etag-12345\""; // Made-up ETag for non-existent document + var resultWithFabricatedETag = await storage.WriteStateAsync(testActorId, operations, FabricatedETag, cancellationToken); // Assert - The write should fail due to ETag mismatch (document doesn't exist) Assert.False(resultWithFabricatedETag.Success); @@ -329,7 +327,7 @@ public class CosmosActorStateStorageConcurrencyTests // Verify no document was created var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var readResult = await storage.ReadStateAsync(testActorId, readOperations, cancellationToken); var getValue = readResult.Results[0] as GetValueResult; diff --git a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageListKeysTests.cs b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageListKeysTests.cs index 22f0dcd7c6..ef26b79bab 100644 --- a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageListKeysTests.cs +++ b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageListKeysTests.cs @@ -29,18 +29,18 @@ public class CosmosActorStateStorageListKeysTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var prefixKey1 = "prefix_key1"; - var prefixKey2 = "prefix_key2"; - var otherKey = "other_key"; + const string PrefixKey1 = "prefix_key1"; + const string PrefixKey2 = "prefix_key2"; + const string OtherKey = "other_key"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var value3 = JsonSerializer.SerializeToElement("value3"); var writeOperations = new List { - new SetValueOperation(prefixKey1, value1), - new SetValueOperation(prefixKey2, value2), - new SetValueOperation(otherKey, value3) + new SetValueOperation(PrefixKey1, value1), + new SetValueOperation(PrefixKey2, value2), + new SetValueOperation(OtherKey, value3) }; await storage.WriteStateAsync(testActorId, writeOperations, "0", cancellationToken); @@ -57,9 +57,9 @@ public class CosmosActorStateStorageListKeysTests var listKeys = result.Results[0] as ListKeysResult; Assert.NotNull(listKeys); Assert.Equal(2, listKeys.Keys.Count); - Assert.Contains(prefixKey1, listKeys.Keys); - Assert.Contains(prefixKey2, listKeys.Keys); - Assert.DoesNotContain(otherKey, listKeys.Keys); + Assert.Contains(PrefixKey1, listKeys.Keys); + Assert.Contains(PrefixKey2, listKeys.Keys); + Assert.DoesNotContain(OtherKey, listKeys.Keys); } [Fact] @@ -72,15 +72,15 @@ public class CosmosActorStateStorageListKeysTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key1 = "key1"; - var key2 = "key2"; + const string Key1 = "key1"; + const string Key2 = "key2"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var writeOperations = new List { - new SetValueOperation(key1, value1), - new SetValueOperation(key2, value2) + new SetValueOperation(Key1, value1), + new SetValueOperation(Key2, value2) }; await storage.WriteStateAsync(testActorId, writeOperations, "0", cancellationToken); @@ -135,15 +135,15 @@ public class CosmosActorStateStorageListKeysTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key1 = "key1"; - var key2 = "key2"; + const string Key1 = "key1"; + const string Key2 = "key2"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var writeOperations = new List { - new SetValueOperation(key1, value1), - new SetValueOperation(key2, value2) + new SetValueOperation(Key1, value1), + new SetValueOperation(Key2, value2) }; // First write some data @@ -162,8 +162,8 @@ public class CosmosActorStateStorageListKeysTests var listKeys = readResult.Results[0] as ListKeysResult; Assert.NotNull(listKeys); Assert.Equal(2, listKeys.Keys.Count); - Assert.Contains(key1, listKeys.Keys); - Assert.Contains(key2, listKeys.Keys); + Assert.Contains(Key1, listKeys.Keys); + Assert.Contains(Key2, listKeys.Keys); } [Fact] @@ -176,9 +176,9 @@ public class CosmosActorStateStorageListKeysTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key1 = "key1"; - var key2 = "key2"; - var key3 = "key3"; + const string Key1 = "key1"; + const string Key2 = "key2"; + const string Key3 = "key3"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var value3 = JsonSerializer.SerializeToElement("value3"); @@ -186,9 +186,9 @@ public class CosmosActorStateStorageListKeysTests // Setup initial state with 3 keys var writeOperations = new List { - new SetValueOperation(key1, value1), - new SetValueOperation(key2, value2), - new SetValueOperation(key3, value3) + new SetValueOperation(Key1, value1), + new SetValueOperation(Key2, value2), + new SetValueOperation(Key3, value3) }; var writeResult = await storage.WriteStateAsync(testActorId, writeOperations, "0", cancellationToken); Assert.True(writeResult.Success); @@ -196,7 +196,7 @@ public class CosmosActorStateStorageListKeysTests // Remove one key var removeOperations = new List { - new RemoveKeyOperation(key2) + new RemoveKeyOperation(Key2) }; var removeResult = await storage.WriteStateAsync(testActorId, removeOperations, writeResult.ETag, cancellationToken); Assert.True(removeResult.Success); @@ -213,9 +213,9 @@ public class CosmosActorStateStorageListKeysTests var listKeys = readResult.Results[0] as ListKeysResult; Assert.NotNull(listKeys); Assert.Equal(2, listKeys.Keys.Count); - Assert.Contains(key1, listKeys.Keys); - Assert.Contains(key3, listKeys.Keys); - Assert.DoesNotContain(key2, listKeys.Keys); + Assert.Contains(Key1, listKeys.Keys); + Assert.Contains(Key3, listKeys.Keys); + Assert.DoesNotContain(Key2, listKeys.Keys); } [Fact] @@ -235,9 +235,7 @@ public class CosmosActorStateStorageListKeysTests string[] miscKeys = ["config", "metadata"]; var writeOperations = new List(); - var allKeys = userKeys.Concat(sessionKeys).Concat(cacheKeys).Concat(miscKeys); - - foreach (var key in allKeys) + foreach (var key in userKeys.Concat(sessionKeys).Concat(cacheKeys).Concat(miscKeys)) { writeOperations.Add(new SetValueOperation(key, JsonSerializer.SerializeToElement($"value_for_{key}"))); } diff --git a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageTests.cs b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageTests.cs index 97ff06c026..2027eb350e 100644 --- a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageTests.cs +++ b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosActorStateStorageTests.cs @@ -29,12 +29,12 @@ public class CosmosActorStateStorageTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // Act @@ -55,15 +55,15 @@ public class CosmosActorStateStorageTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key1 = "key1"; - var key2 = "key2"; + const string Key1 = "key1"; + const string Key2 = "key2"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement(42); var writeOperations = new List { - new SetValueOperation(key1, value1), - new SetValueOperation(key2, value2) + new SetValueOperation(Key1, value1), + new SetValueOperation(Key2, value2) }; // Act - Write state @@ -77,8 +77,8 @@ public class CosmosActorStateStorageTests // Act - Read individual values var readOperations = new List { - new GetValueOperation(key1), - new GetValueOperation(key2) + new GetValueOperation(Key1), + new GetValueOperation(Key2) }; var readResult = await storage.ReadStateAsync(testActorId, readOperations, cancellationToken); @@ -105,14 +105,14 @@ public class CosmosActorStateStorageTests var listKeys = listResult.Results[0] as ListKeysResult; Assert.NotNull(listKeys); Assert.Equal(2, listKeys.Keys.Count); - Assert.Contains(key1, listKeys.Keys); - Assert.Contains(key2, listKeys.Keys); + Assert.Contains(Key1, listKeys.Keys); + Assert.Contains(Key2, listKeys.Keys); // Act - Update with correct ETag var updateOperations = new List { - new SetValueOperation(key1, JsonSerializer.SerializeToElement("updated_value1")), - new RemoveKeyOperation(key2) + new SetValueOperation(Key1, JsonSerializer.SerializeToElement("updated_value1")), + new RemoveKeyOperation(Key2) }; var updateResult = await storage.WriteStateAsync(testActorId, updateOperations, writeResult.ETag, cancellationToken); @@ -123,8 +123,8 @@ public class CosmosActorStateStorageTests // Act - Verify final state var finalReadOperations = new List { - new GetValueOperation(key1), - new GetValueOperation(key2), + new GetValueOperation(Key1), + new GetValueOperation(Key2), new ListKeysOperation(continuationToken: null) }; var finalResult = await storage.ReadStateAsync(testActorId, finalReadOperations, cancellationToken); @@ -143,8 +143,8 @@ public class CosmosActorStateStorageTests Assert.Equal("updated_value1", finalValue1.Value?.GetString()); Assert.Null(finalValue2.Value); // key2 was removed Assert.Single(finalKeys.Keys); - Assert.Contains(key1, finalKeys.Keys); - Assert.DoesNotContain(key2, finalKeys.Keys); + Assert.Contains(Key1, finalKeys.Keys); + Assert.DoesNotContain(Key2, finalKeys.Keys); } [Fact] @@ -157,11 +157,11 @@ public class CosmosActorStateStorageTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // First write to establish state @@ -171,7 +171,7 @@ public class CosmosActorStateStorageTests // Act - Try to write with incorrect ETag var incorrectOperations = new List { - new SetValueOperation(key, JsonSerializer.SerializeToElement("newValue")) + new SetValueOperation(Key, JsonSerializer.SerializeToElement("newValue")) }; var result = await storage.WriteStateAsync(testActorId, incorrectOperations, "incorrect-etag", cancellationToken); @@ -182,7 +182,7 @@ public class CosmosActorStateStorageTests // Verify original value is unchanged var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var readResult = await storage.ReadStateAsync(testActorId, readOperations, cancellationToken); var getValue = readResult.Results[0] as GetValueResult; @@ -200,17 +200,17 @@ public class CosmosActorStateStorageTests var testActorId1 = new ActorId("TestActor1", Guid.NewGuid().ToString()); var testActorId2 = new ActorId("TestActor2", Guid.NewGuid().ToString()); - var key = "sharedKey"; + const string Key = "sharedKey"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var operations1 = new List { - new SetValueOperation(key, value1) + new SetValueOperation(Key, value1) }; var operations2 = new List { - new SetValueOperation(key, value2) + new SetValueOperation(Key, value2) }; // Act - Write to both actors @@ -220,7 +220,7 @@ public class CosmosActorStateStorageTests // Assert - Verify values are different var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var result1 = await storage.ReadStateAsync(testActorId1, readOperations, cancellationToken); @@ -246,10 +246,7 @@ public class CosmosActorStateStorageTests var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); var emptyOperations = new List(); // Act & Assert - await Assert.ThrowsAsync(async () => - { - await storage.WriteStateAsync(testActorId, emptyOperations, "0", cancellationToken); - }); + await Assert.ThrowsAsync(async () => await storage.WriteStateAsync(testActorId, emptyOperations, "0", cancellationToken)); } [Fact] @@ -310,12 +307,12 @@ public class CosmosActorStateStorageTests }; #pragma warning restore CA1861 // Avoid constant arrays as arguments - var key = "complexObject"; + const string Key = "complexObject"; var value = JsonSerializer.SerializeToElement(complexObject); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // Act - Write complex object @@ -325,7 +322,7 @@ public class CosmosActorStateStorageTests // Act - Read back complex object var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var readResult = await storage.ReadStateAsync(testActorId, readOperations, cancellationToken); @@ -360,9 +357,9 @@ public class CosmosActorStateStorageTests await using var storage = new CosmosActorStateStorage(this._fixture.Container); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key1 = "key1"; - var key2 = "key2"; - var key3 = "key3"; + const string Key1 = "key1"; + const string Key2 = "key2"; + const string Key3 = "key3"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var value3 = JsonSerializer.SerializeToElement("value3"); @@ -370,11 +367,11 @@ public class CosmosActorStateStorageTests // Act - Perform multiple operations in a single batch var operations = new List { - new SetValueOperation(key1, value1), // Set key1 - new SetValueOperation(key2, value2), // Set key2 - new SetValueOperation(key3, value3), // Set key3 - new RemoveKeyOperation(key1), // Remove key1 - new SetValueOperation(key1, JsonSerializer.SerializeToElement("new_value1")) // Re-add key1 with new value + new SetValueOperation(Key1, value1), // Set key1 + new SetValueOperation(Key2, value2), // Set key2 + new SetValueOperation(Key3, value3), // Set key3 + new RemoveKeyOperation(Key1), // Remove key1 + new SetValueOperation(Key1, JsonSerializer.SerializeToElement("new_value1")) // Re-add key1 with new value }; var result = await storage.WriteStateAsync(testActorId, operations, "0", cancellationToken); @@ -385,9 +382,9 @@ public class CosmosActorStateStorageTests // Act - Verify final state var readOperations = new List { - new GetValueOperation(key1), - new GetValueOperation(key2), - new GetValueOperation(key3), + new GetValueOperation(Key1), + new GetValueOperation(Key2), + new GetValueOperation(Key3), new ListKeysOperation(continuationToken: null) }; var readResult = await storage.ReadStateAsync(testActorId, readOperations, cancellationToken); @@ -412,9 +409,9 @@ public class CosmosActorStateStorageTests // All three keys should be present Assert.Equal(3, listKeys.Keys.Count); - Assert.Contains(key1, listKeys.Keys); - Assert.Contains(key2, listKeys.Keys); - Assert.Contains(key3, listKeys.Keys); + Assert.Contains(Key1, listKeys.Keys); + Assert.Contains(Key2, listKeys.Keys); + Assert.Contains(Key3, listKeys.Keys); } [SkipOnEmulatorFact] diff --git a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosIdSanitizerTests.cs b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosIdSanitizerTests.cs index 18966a9d0a..bdb3de0b7a 100644 --- a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosIdSanitizerTests.cs +++ b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosIdSanitizerTests.cs @@ -219,11 +219,9 @@ public class CosmosIdSanitizerTests } [Fact] - public void SeparatorChar_HasCorrectValue() - { + public void SeparatorChar_HasCorrectValue() => // Assert Assert.Equal('_', CosmosIdSanitizer.SeparatorChar); - } [Fact] public void Sanitize_WithOnlySeparatorChar_EscapesCorrectly() diff --git a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosTestFixture.cs b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosTestFixture.cs index e9a6941cf5..f589686c33 100644 --- a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosTestFixture.cs +++ b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/CosmosTestFixture.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests; [CollectionDefinition("Cosmos Test Collection")] -public class CosmosTests : ICollectionFixture { } +public class CosmosTests : ICollectionFixture; /// /// Shared test fixture for CosmosDB integration tests. @@ -41,9 +41,7 @@ public class CosmosTestFixture : IAsyncLifetime }); appHost.Services.ConfigureHttpClientDefaults(clientBuilder => - { - clientBuilder.AddStandardResilienceHandler(); - }); + clientBuilder.AddStandardResilienceHandler()); this.App = await appHost.BuildAsync(cancellationToken).WaitAsync(cancellationToken); await this.App.StartAsync(cancellationToken).WaitAsync(cancellationToken); diff --git a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/LazyCosmosContainerTests.cs b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/LazyCosmosContainerTests.cs index 2df7f01e3d..99e365c367 100644 --- a/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/LazyCosmosContainerTests.cs +++ b/dotnet/tests/CosmosDB.IntegrationTests/Microsoft.Extensions.AI.Agents.Runtime.Storage.CosmosDB.Tests/LazyCosmosContainerTests.cs @@ -77,11 +77,11 @@ public class LazyCosmosContainerTests var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); await using var storage = new CosmosActorStateStorage(lazyContainer); - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // This should work if the container was properly initialized @@ -185,32 +185,24 @@ public class LazyCosmosContainerTests } [Fact] - public void Constructor_WithNullContainer_ShouldThrowArgumentNullException() - { + public void Constructor_WithNullContainer_ShouldThrowArgumentNullException() => // Act & Assert - Assert.Throws(() => new LazyCosmosContainer((Container)null!)); - } + Assert.Throws(() => new LazyCosmosContainer(null!)); [Fact] - public void Constructor_WithNullCosmosClient_ShouldThrowArgumentNullException() - { + public void Constructor_WithNullCosmosClient_ShouldThrowArgumentNullException() => // Act & Assert Assert.Throws(() => new LazyCosmosContainer(null!, "test-db", "test-container")); - } [Fact] - public void Constructor_WithNullDatabaseName_ShouldThrowArgumentNullException() - { + public void Constructor_WithNullDatabaseName_ShouldThrowArgumentNullException() => // Act & Assert Assert.Throws(() => new LazyCosmosContainer(this._fixture.CosmosClient, null!, "test-container")); - } [Fact] - public void Constructor_WithNullContainerName_ShouldThrowArgumentNullException() - { + public void Constructor_WithNullContainerName_ShouldThrowArgumentNullException() => // Act & Assert Assert.Throws(() => new LazyCosmosContainer(this._fixture.CosmosClient, "test-db", null!)); - } [SkipOnEmulatorFact] public async Task LazyCosmosContainer_WithInternalConstructor_ShouldWorkWithCosmosActorStateStorageAsync() @@ -229,11 +221,11 @@ public class LazyCosmosContainerTests await using var storage = new CosmosActorStateStorage(lazyContainer); var testActorId = new ActorId("TestActor", Guid.NewGuid().ToString()); - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // This should work - container should be initialized on first storage operation @@ -246,7 +238,7 @@ public class LazyCosmosContainerTests // Verify we can read back the value var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var readResult = await storage.ReadStateAsync(testActorId, readOperations, cancellationToken); @@ -281,6 +273,6 @@ public class LazyCosmosContainerTests await using var lazyContainer = new LazyCosmosContainer(this._fixture.CosmosClient, invalidDatabaseName, "test-container"); // Act & Assert - await Assert.ThrowsAsync(async () => await lazyContainer.GetContainerAsync()); + await Assert.ThrowsAsync(lazyContainer.GetContainerAsync); } } diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffOrchestrationTests.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffOrchestrationTests.cs index e916837dc6..cae22832af 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffOrchestrationTests.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffOrchestrationTests.cs @@ -136,9 +136,7 @@ public sealed class HandoffOrchestrationTests : IDisposable .Build(); ChatClientAgentOptions agentOptions = new() { Name = name, Description = description }; - ChatClientAgent mockAgent = new(chatClient, agentOptions); - - return mockAgent; + return new(chatClient, agentOptions); } private static class Responses diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs index 2068183383..8243736520 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs @@ -29,11 +29,9 @@ public class HandoffsTests } [Fact] - public void StartWith_NullAgent_ThrowsArgumentNullException() - { + public void StartWith_NullAgent_ThrowsArgumentNullException() => // Act & Assert Assert.Throws("initialAgent", () => Handoffs.StartWith(null!)); - } [Fact] public void Add_ValidSourceAndTargets_AddsHandoffRelationships() @@ -80,7 +78,7 @@ public class HandoffsTests var handoffs = Handoffs.StartWith(sourceAgent); // Act & Assert - Assert.Throws("targets", () => handoffs.Add(sourceAgent, (AIAgent[])null!)); + Assert.Throws("targets", () => handoffs.Add(sourceAgent, null!)); } [Fact] @@ -90,17 +88,17 @@ public class HandoffsTests var sourceAgent = CreateAgent("source", "Source agent"); var targetAgent = CreateAgent("target", "Target agent"); var handoffs = Handoffs.StartWith(sourceAgent); - var customReason = "Custom handoff reason"; + const string CustomReason = "Custom handoff reason"; // Act - var result = handoffs.Add(sourceAgent, targetAgent, customReason); + var result = handoffs.Add(sourceAgent, targetAgent, CustomReason); // Assert Assert.Same(handoffs, result); Assert.True(handoffs.Targets.ContainsKey(sourceAgent)); var target = handoffs.Targets[sourceAgent].Single(); Assert.Equal(targetAgent, target.Target); - Assert.Equal(customReason, target.Reason); + Assert.Equal(CustomReason, target.Reason); } [Fact] @@ -122,7 +120,7 @@ public class HandoffsTests var handoffs = Handoffs.StartWith(sourceAgent); // Act & Assert - Assert.Throws("target", () => handoffs.Add(sourceAgent, (AIAgent)null!, "reason")); + Assert.Throws("target", () => handoffs.Add(sourceAgent, null!, "reason")); } [Fact] @@ -159,15 +157,15 @@ public class HandoffsTests // Arrange var agent = CreateAgent("agent1", "Test agent"); var handoffs = Handoffs.StartWith(agent); - var orchestrationName = "Test Orchestration"; + const string OrchestrationName = "Test Orchestration"; // Act - var orchestration = handoffs.Build(orchestrationName); + var orchestration = handoffs.Build(OrchestrationName); // Assert Assert.NotNull(orchestration); Assert.IsType(orchestration); - Assert.Equal(orchestrationName, orchestration.Name); + Assert.Equal(OrchestrationName, orchestration.Name); } [Fact] @@ -196,9 +194,9 @@ public class HandoffsTests var sourceAgent1 = CreateAgent("source1", "Source agent 1"); var sourceAgent2 = CreateAgent("source2", "Source agent 2"); var targetAgent = CreateAgent("target", "Target agent"); - var handoffs = Handoffs.StartWith(sourceAgent1); - handoffs.Add(sourceAgent1, targetAgent); - handoffs.Add(sourceAgent2, targetAgent); + var handoffs = Handoffs.StartWith(sourceAgent1) + .Add(sourceAgent1, targetAgent) + .Add(sourceAgent2, targetAgent); var readOnlyDict = (IReadOnlyDictionary>)handoffs; // Act @@ -236,9 +234,9 @@ public class HandoffsTests var sourceAgent1 = CreateAgent("source1", "Source agent 1"); var sourceAgent2 = CreateAgent("source2", "Source agent 2"); var targetAgent = CreateAgent("target", "Target agent"); - var handoffs = Handoffs.StartWith(sourceAgent1); - handoffs.Add(sourceAgent1, targetAgent); - handoffs.Add(sourceAgent2, targetAgent); + var handoffs = Handoffs.StartWith(sourceAgent1) + .Add(sourceAgent1, targetAgent) + .Add(sourceAgent2, targetAgent); var readOnlyCollection = (IReadOnlyCollection>>)handoffs; // Act @@ -383,22 +381,20 @@ public class HandoffsTests { // Arrange var agent = CreateAgent("agent1", "Test agent"); - var reason = "Custom reason"; + const string Reason = "Custom reason"; // Act - var target = new Handoffs.HandoffTarget(agent, reason); + var target = new Handoffs.HandoffTarget(agent, Reason); // Assert Assert.Equal(agent, target.Target); - Assert.Equal(reason, target.Reason); + Assert.Equal(Reason, target.Reason); } [Fact] - public void HandoffTarget_Constructor_WithNullTarget_ThrowsArgumentNullException() - { + public void HandoffTarget_Constructor_WithNullTarget_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => new Handoffs.HandoffTarget(null!)); - } [Fact] public void HandoffTarget_Constructor_WithAgentWithoutDescriptionOrName_ThrowsInvalidOperationException() @@ -604,7 +600,6 @@ public class HandoffsTests Name = name, Description = description, }; - ChatClientAgent mockAgent = new(mockClient.Object, options); - return mockAgent; + return new(mockClient.Object, options); } } diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HttpMessageHandlerStub.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HttpMessageHandlerStub.cs index 3d199d36ea..ff090a4501 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HttpMessageHandlerStub.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HttpMessageHandlerStub.cs @@ -11,8 +11,6 @@ internal sealed class HttpMessageHandlerStub : HttpMessageHandler { public Queue ResponseQueue { get; } = new(); - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - return Task.FromResult(this.ResponseQueue.Dequeue()); - } + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + Task.FromResult(this.ResponseQueue.Dequeue()); } diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs index 09ddd5c0cb..514a420104 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.AI; using Microsoft.Extensions.AI.Agents; -using Moq; namespace Microsoft.Agents.Orchestration.UnitTest; @@ -15,13 +14,10 @@ namespace Microsoft.Agents.Orchestration.UnitTest; /// internal sealed class MockAgent(int index) : AIAgent { - public static MockAgent CreateWithResponse(int index, string response) + public static MockAgent CreateWithResponse(int index, string response) => new(index) { - return new(index) - { - Response = [new(ChatRole.Assistant, response)] - }; - } + Response = [new(ChatRole.Assistant, response)] + }; public int InvokeCount { get; private set; } @@ -31,19 +27,11 @@ internal sealed class MockAgent(int index) : AIAgent public override string? Description => $"test {index}"; - public override AgentThread GetNewThread() - { - return new AgentThread() { ConversationId = Guid.NewGuid().ToString() }; - } + public override AgentThread GetNewThread() => new() { ConversationId = Guid.NewGuid().ToString() }; public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { this.InvokeCount++; - if (thread == null) - { - Mock mockThread = new(MockBehavior.Strict); - thread = mockThread.Object; - } return Task.FromResult(new AgentRunResponse(messages: [.. this.Response])); } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs index b908779e7e..83087e96b9 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs @@ -25,10 +25,10 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output, AgentFixtu [Theory] [InlineData("SendActivity.yaml", "SendActivity.json")] [InlineData("InvokeAgent.yaml", "InvokeAgent.json")] - public Task Validate(string workflowFileName, string testcaseFileName) => - this.RunWorkflow(workflowFileName, testcaseFileName); + public Task ValidateAsync(string workflowFileName, string testcaseFileName) => + this.RunWorkflowAsync(workflowFileName, testcaseFileName); - private Task RunWorkflow(string workflowFileName, string testcaseFileName) + private Task RunWorkflowAsync(string workflowFileName, string testcaseFileName) { this.Output.WriteLine($"WORKFLOW: {workflowFileName}"); this.Output.WriteLine($"TESTCASE: {testcaseFileName}"); @@ -42,13 +42,13 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output, AgentFixtu return testcase.Setup.Input.Type switch { - nameof(ChatMessage) => this.RunWorkflow(testcase, workflowPath, configuration), - nameof(String) => this.RunWorkflow(testcase, workflowPath, configuration), + nameof(ChatMessage) => this.RunWorkflowAsync(testcase, workflowPath, configuration), + nameof(String) => this.RunWorkflowAsync(testcase, workflowPath, configuration), _ => throw new NotSupportedException($"Input type '{testcase.Setup.Input.Type}' is not supported."), }; } - private async Task RunWorkflow( + private async Task RunWorkflowAsync( Testcase testcase, string workflowPath, IConfiguration configuration) where TInput : notnull @@ -58,7 +58,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output, AgentFixtu AzureAIConfiguration? foundryConfig = configuration.GetSection("AzureAI").Get(); Assert.NotNull(foundryConfig); - IDictionary agentMap = await agentFixture.GetAgentsAsync(foundryConfig); + IReadOnlyDictionary agentMap = await agentFixture.GetAgentsAsync(foundryConfig); IConfiguration workflowConfig = new ConfigurationBuilder() diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFactory.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFactory.cs index f88d4c1f35..d30ddd0341 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFactory.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFactory.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Threading; @@ -20,7 +19,7 @@ namespace Microsoft.Agents.Workflows.Declarative.IntegrationTests.Framework; internal static class AgentFactory { - public static async Task> CreateAsync(string agentsDirectory, AzureAIConfiguration config, CancellationToken cancellationToken) + public static async Task> CreateAsync(string agentsDirectory, AzureAIConfiguration config, CancellationToken cancellationToken) { PersistentAgentsClient clientAgents = new(config.Endpoint, new AzureCliCredential()); @@ -45,6 +44,6 @@ internal static class AgentFactory agentMap[agent.Name] = agent.Id; } - return agentMap.ToImmutableDictionary(); + return agentMap; } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFixture.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFixture.cs index fb38bd1279..cea1f310ad 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFixture.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/AgentFixture.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Collections.Immutable; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Shared.IntegrationTests; @@ -10,9 +10,9 @@ namespace Microsoft.Agents.Workflows.Declarative.IntegrationTests.Framework; public sealed class AgentFixture : IDisposable { - private static ImmutableDictionary? s_agentMap; + private static IReadOnlyDictionary? s_agentMap; - internal async Task> GetAgentsAsync(AzureAIConfiguration config, CancellationToken cancellationToken = default) + internal async Task> GetAgentsAsync(AzureAIConfiguration config, CancellationToken cancellationToken = default) { s_agentMap ??= await AgentFactory.CreateAsync("Agents", config, cancellationToken); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowEvents.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowEvents.cs index 5d2deb418a..42c4681ae1 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowEvents.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowEvents.cs @@ -1,23 +1,23 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Collections.Immutable; +using System.Collections.Generic; using System.Linq; namespace Microsoft.Agents.Workflows.Declarative.IntegrationTests.Framework; internal sealed class WorkflowEvents { - public WorkflowEvents(ImmutableList workflowEvents) + public WorkflowEvents(IReadOnlyList workflowEvents) { this.Events = workflowEvents; - this.EventCounts = workflowEvents.GroupBy(e => e.GetType()).ToImmutableDictionary(e => e.Key, e => e.Count()); - this.ActionInvokeEvents = workflowEvents.OfType().ToImmutableList(); - this.ActionCompleteEvents = workflowEvents.OfType().ToImmutableList(); + this.EventCounts = workflowEvents.GroupBy(e => e.GetType()).ToDictionary(e => e.Key, e => e.Count()); + this.ActionInvokeEvents = workflowEvents.OfType().ToList(); + this.ActionCompleteEvents = workflowEvents.OfType().ToList(); } - public ImmutableList Events { get; } - public IImmutableDictionary EventCounts { get; } - public ImmutableList ActionInvokeEvents { get; } - public ImmutableList ActionCompleteEvents { get; private set; } + public IReadOnlyList Events { get; } + public IReadOnlyDictionary EventCounts { get; } + public IReadOnlyList ActionInvokeEvents { get; } + public IReadOnlyList ActionCompleteEvents { get; } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowHarness.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowHarness.cs index c1b43a76e9..3f2b6fc327 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowHarness.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.IntegrationTests/Framework/WorkflowHarness.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -using System.Collections.Immutable; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -11,7 +11,7 @@ internal static class WorkflowHarness public static async Task RunAsync(Workflow workflow, TInput input) where TInput : notnull { StreamingRun run = await InProcessExecution.StreamAsync(workflow, input); - ImmutableList workflowEvents = run.WatchStreamAsync().ToEnumerable().ToImmutableList(); + IReadOnlyList workflowEvents = run.WatchStreamAsync().ToEnumerable().ToList(); return new WorkflowEvents(workflowEvents); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowContextTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowContextTest.cs index 24ee30deee..83bbbfee22 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowContextTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowContextTest.cs @@ -29,23 +29,23 @@ public class DeclarativeWorkflowContextTests { // Arrange TokenCredential credentials = new DefaultAzureCredential(); - int maxCallDepth = 10; - int maxExpressionLength = 100; + const int MaxCallDepth = 10; + const int MaxExpressionLength = 100; ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { }); // Act Mock mockProvider = new(MockBehavior.Strict); DeclarativeWorkflowOptions context = new(mockProvider.Object) { - MaximumCallDepth = maxCallDepth, - MaximumExpressionLength = maxExpressionLength, + MaximumCallDepth = MaxCallDepth, + MaximumExpressionLength = MaxExpressionLength, LoggerFactory = loggerFactory }; // Assert Assert.Equal(mockProvider.Object, context.AgentProvider); - Assert.Equal(maxCallDepth, context.MaximumCallDepth); - Assert.Equal(maxExpressionLength, context.MaximumExpressionLength); + Assert.Equal(MaxCallDepth, context.MaximumCallDepth); + Assert.Equal(MaxExpressionLength, context.MaximumExpressionLength); Assert.Same(loggerFactory, context.LoggerFactory); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs index cd9f0c28d8..bf41f1fc33 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs @@ -28,25 +28,25 @@ public sealed class DeclarativeWorkflowExceptionTest(ITestOutputHelper output) : private static void AssertDefault(Action throwAction) where TException : Exception { - TException exception = Assert.Throws(() => throwAction.Invoke()); + TException exception = Assert.Throws(throwAction.Invoke); Assert.NotEmpty(exception.Message); Assert.Null(exception.InnerException); } private static void AssertMessage(Action throwAction) where TException : Exception { - const string message = "Test exception message"; - TException exception = Assert.Throws(() => throwAction.Invoke(message)); - Assert.Equal(message, exception.Message); + const string Message = "Test exception message"; + TException exception = Assert.Throws(() => throwAction.Invoke(Message)); + Assert.Equal(Message, exception.Message); Assert.Null(exception.InnerException); } private static void AssertInner(Action throwAction) where TException : Exception { - const string message = "Test exception message"; + const string Message = "Test exception message"; NotSupportedException innerException = new("Inner exception message"); - TException exception = Assert.Throws(() => throwAction.Invoke(message, innerException)); - Assert.Equal(message, exception.Message); + TException exception = Assert.Throws(() => throwAction.Invoke(Message, innerException)); + Assert.Equal(Message, exception.Message); Assert.Equal(innerException, exception.InnerException); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs index d33139ece1..d65b33319b 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Collections.Immutable; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -19,33 +19,33 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests; /// public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : WorkflowTest(output) { - private ImmutableList WorkflowEvents { get; set; } = ImmutableList.Empty; + private List WorkflowEvents { get; set; } = []; - private ImmutableDictionary WorkflowEventCounts { get; set; } = ImmutableDictionary.Empty; + private Dictionary WorkflowEventCounts { get; set; } = []; [Theory] [InlineData("BadEmpty.yaml")] [InlineData("BadId.yaml")] [InlineData("BadKind.yaml")] - public async Task InvalidWorkflow(string workflowFile) + public async Task InvalidWorkflowAsync(string workflowFile) { - await Assert.ThrowsAsync(() => this.RunWorkflow(workflowFile)); + await Assert.ThrowsAsync(() => this.RunWorkflowAsync(workflowFile)); this.AssertNotExecuted("end_all"); } [Fact] - public async Task LoopEachAction() + public async Task LoopEachActionAsync() { - await this.RunWorkflow("LoopEach.yaml"); + await this.RunWorkflowAsync("LoopEach.yaml"); this.AssertExecutionCount(expectedCount: 35); this.AssertExecuted("foreach_loop"); this.AssertExecuted("end_all"); } [Fact] - public async Task LoopBreakAction() + public async Task LoopBreakActionAsync() { - await this.RunWorkflow("LoopBreak.yaml"); + await this.RunWorkflowAsync("LoopBreak.yaml"); this.AssertExecutionCount(expectedCount: 7); this.AssertExecuted("foreach_loop"); this.AssertExecuted("breakLoop_now"); @@ -55,9 +55,9 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow } [Fact] - public async Task LoopContinueAction() + public async Task LoopContinueActionAsync() { - await this.RunWorkflow("LoopContinue.yaml"); + await this.RunWorkflowAsync("LoopContinue.yaml"); this.AssertExecutionCount(expectedCount: 7); this.AssertExecuted("foreach_loop"); this.AssertExecuted("continueLoop_now"); @@ -67,18 +67,18 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow } [Fact] - public async Task EndConversationAction() + public async Task EndConversationActionAsync() { - await this.RunWorkflow("EndConversation.yaml"); + await this.RunWorkflowAsync("EndConversation.yaml"); this.AssertExecutionCount(expectedCount: 1); this.AssertExecuted("end_all"); this.AssertNotExecuted("sendActivity_1"); } [Fact] - public async Task GotoAction() + public async Task GotoActionAsync() { - await this.RunWorkflow("Goto.yaml"); + await this.RunWorkflowAsync("Goto.yaml"); this.AssertExecutionCount(expectedCount: 2); this.AssertExecuted("goto_end"); this.AssertExecuted("end_all"); @@ -90,9 +90,9 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow [Theory] [InlineData(12)] [InlineData(37)] - public async Task ConditionAction(int input) + public async Task ConditionActionAsync(int input) { - await this.RunWorkflow("Condition.yaml", input); + await this.RunWorkflowAsync("Condition.yaml", input); this.AssertExecutionCount(expectedCount: 9); this.AssertExecuted("setVariable_test"); this.AssertExecuted("conditionGroup_test"); @@ -118,9 +118,9 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow [Theory] [InlineData(12, 7)] [InlineData(37, 9)] - public async Task ConditionActionWithElse(int input, int expectedActions) + public async Task ConditionActionWithElseAsync(int input, int expectedActions) { - await this.RunWorkflow("ConditionElse.yaml", input); + await this.RunWorkflowAsync("ConditionElse.yaml", input); this.AssertExecutionCount(expectedActions); this.AssertExecuted("setVariable_test"); this.AssertExecuted("conditionGroup_test"); @@ -149,9 +149,9 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow [InlineData("SetTextVariable.yaml", 1, "set_text")] [InlineData("ClearAllVariables.yaml", 1, "clear_all")] [InlineData("ResetVariable.yaml", 2, "clear_var")] - public async Task ExecuteAction(string workflowFile, int expectedCount, string expectedId) + public async Task ExecuteActionAsync(string workflowFile, int expectedCount, string expectedId) { - await this.RunWorkflow(workflowFile); + await this.RunWorkflowAsync(workflowFile); this.AssertExecutionCount(expectedCount); this.AssertExecuted(expectedId); } @@ -240,14 +240,13 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow } } - private void AssertMessage(string message) - { + private void AssertMessage(string message) => Assert.Contains(this.WorkflowEvents.OfType(), e => string.Equals(e.Message.Trim(), message, StringComparison.Ordinal)); - } - private Task RunWorkflow(string workflowPath) => this.RunWorkflow(workflowPath, string.Empty); + private Task RunWorkflowAsync(string workflowPath) => + this.RunWorkflowAsync(workflowPath, string.Empty); - private async Task RunWorkflow(string workflowPath, TInput workflowInput) where TInput : notnull + private async Task RunWorkflowAsync(string workflowPath, TInput workflowInput) where TInput : notnull { using StreamReader yamlReader = File.OpenText(Path.Combine("Workflows", workflowPath)); Mock mockAgentProvider = new(MockBehavior.Strict); @@ -257,7 +256,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow StreamingRun run = await InProcessExecution.StreamAsync(workflow, workflowInput); - this.WorkflowEvents = run.WatchStreamAsync().ToEnumerable().ToImmutableList(); + this.WorkflowEvents = run.WatchStreamAsync().ToEnumerable().ToList(); foreach (WorkflowEvent workflowEvent in this.WorkflowEvents) { if (workflowEvent is ExecutorInvokedEvent invokeEvent) @@ -278,16 +277,14 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow this.Output.WriteLine($"MESSAGE: {messageEvent.Response.Messages[0].Text.Trim()}"); } } - this.WorkflowEventCounts = this.WorkflowEvents.GroupBy(e => e.GetType()).ToImmutableDictionary(e => e.Key, e => e.Count()); + this.WorkflowEventCounts = this.WorkflowEvents.GroupBy(e => e.GetType()).ToDictionary(e => e.Key, e => e.Count()); } private sealed class RootExecutor() : ReflectingExecutor(WorkflowActionVisitor.Steps.Root("anything")), IMessageHandler { - public async ValueTask HandleAsync(string message, IWorkflowContext context) - { - await context.SendMessageAsync($"{this.Id}: {DateTime.UtcNow.ToShortTimeString()}").ConfigureAwait(false); - } + public async ValueTask HandleAsync(string message, IWorkflowContext context) => + await context.SendMessageAsync($"{this.Id}: {DateTime.UtcNow:t}").ConfigureAwait(false); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Extensions/FormulaValueExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Extensions/FormulaValueExtensionsTests.cs index a2ac6295dc..7d73714d34 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Extensions/FormulaValueExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Extensions/FormulaValueExtensionsTests.cs @@ -78,8 +78,7 @@ public class FormulaValueExtensionsTests { BlankValue formulaValue = FormulaValue.NewBlank(); Assert.Equal(DataType.Blank, formulaValue.GetDataType()); - - BlankDataValue dataCopy = Assert.IsType(formulaValue.ToDataValue()); + Assert.IsType(formulaValue.ToDataValue()); Assert.Equal(string.Empty, formulaValue.Format()); } @@ -89,7 +88,7 @@ public class FormulaValueExtensionsTests { VoidValue formulaValue = FormulaValue.NewVoid(); Assert.Equal(DataType.Unspecified, formulaValue.GetDataType()); - BlankDataValue dataCopy = Assert.IsType(formulaValue.ToDataValue()); + Assert.IsType(formulaValue.ToDataValue()); } [Fact] @@ -193,7 +192,7 @@ public class FormulaValueExtensionsTests new NamedValue("FieldA", FormulaValue.New("Value1")), new NamedValue("FieldB", FormulaValue.New("Value2")), new NamedValue("FieldC", FormulaValue.New("Value3"))); - TableValue formulaValue = TableValue.NewTable(recordValue.Type, [recordValue]); + TableValue formulaValue = FormulaValue.NewTable(recordValue.Type, [recordValue]); TableDataValue dataValue = formulaValue.ToTable(); Assert.Equal(formulaValue.Rows.Count(), dataValue.Values.Length); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs index 75e8056e70..35c5553454 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs @@ -14,23 +14,23 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests.Interpreter; public sealed class DeclarativeWorkflowModelTest(ITestOutputHelper output) : WorkflowTest(output) { [Fact] - public async Task GetDepthForDefault() + public async Task GetDepthForDefaultAsync() { - DeclarativeWorkflowModel model = new(this.CreateExecutor("root")); + DeclarativeWorkflowModel model = new(CreateExecutor("root")); Assert.Equal(0, model.GetDepth(null)); } [Fact] - public async Task GetDepthForMissingNode() + public async Task GetDepthForMissingNodeAsync() { - DeclarativeWorkflowModel model = new(this.CreateExecutor("root")); + DeclarativeWorkflowModel model = new(CreateExecutor("root")); Assert.Throws(() => model.GetDepth("missing")); } [Fact] - public async Task ConnectMissingNode() + public async Task ConnectMissingNodeAsync() { - TestExecutor rootExecutor = this.CreateExecutor("root"); + TestExecutor rootExecutor = CreateExecutor("root"); DeclarativeWorkflowModel model = new(rootExecutor); model.AddLink("root", "missing"); WorkflowBuilder workflowBuilder = new(rootExecutor); @@ -38,36 +38,34 @@ public sealed class DeclarativeWorkflowModelTest(ITestOutputHelper output) : Wor } [Fact] - public async Task AddToMissingParent() + public async Task AddToMissingParentAsync() { - DeclarativeWorkflowModel model = new(this.CreateExecutor("root")); - Assert.Throws(() => model.AddNode(this.CreateExecutor("next"), "missing")); + DeclarativeWorkflowModel model = new(CreateExecutor("root")); + Assert.Throws(() => model.AddNode(CreateExecutor("next"), "missing")); } [Fact] - public async Task LinkFromMissingSource() + public async Task LinkFromMissingSourceAsync() { - DeclarativeWorkflowModel model = new(this.CreateExecutor("root")); + DeclarativeWorkflowModel model = new(CreateExecutor("root")); Assert.Throws(() => model.AddLink("missing", "anything")); } [Fact] - public async Task LocateMissingParent() + public async Task LocateMissingParentAsync() { - DeclarativeWorkflowModel model = new(this.CreateExecutor("root")); + DeclarativeWorkflowModel model = new(CreateExecutor("root")); Assert.Null(model.LocateParent(null)); Assert.Throws(() => model.LocateParent("missing")); } - private TestExecutor CreateExecutor(string id) => new(id); + private static TestExecutor CreateExecutor(string id) => new(id); internal sealed class TestExecutor(string actionId) : ReflectingExecutor(actionId), IMessageHandler { - public async ValueTask HandleAsync(string message, IWorkflowContext context) - { - await context.SendMessageAsync($"{this.Id}: {DateTime.UtcNow.ToShortTimeString()}").ConfigureAwait(false); - } + public async ValueTask HandleAsync(string message, IWorkflowContext context) => + await context.SendMessageAsync($"{this.Id}: {DateTime.UtcNow:t}").ConfigureAwait(false); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs index 7da024f4ce..9a48de3621 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs @@ -14,40 +14,40 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests.ObjectModel; public sealed class ClearAllVariablesExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) { [Fact] - public async Task ClearWorkflowScope() + public async Task ClearWorkflowScopeAsync() { // Arrange this.State.Set("NoVar", FormulaValue.New("Old value")); ClearAllVariables model = this.CreateModel( - this.FormatDisplayName(nameof(ClearWorkflowScope)), + this.FormatDisplayName(nameof(ClearWorkflowScopeAsync)), VariablesToClear.ConversationScopedVariables); // Act ClearAllVariablesExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyUndefined("NoVar"); } [Fact] - public async Task ClearUndefinedScope() + public async Task ClearUndefinedScopeAsync() { // Arrange ClearAllVariables model = this.CreateModel( - this.FormatDisplayName(nameof(ClearUndefinedScope)), + this.FormatDisplayName(nameof(ClearUndefinedScopeAsync)), VariablesToClear.UserScopedVariables); // Act ClearAllVariablesExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyUndefined("NoVar"); } @@ -61,8 +61,6 @@ public sealed class ClearAllVariablesExecutorTest(ITestOutputHelper output) : Wo Variables = EnumExpression.Literal(VariablesToClearWrapper.Get(variableTarget)), }; - ClearAllVariables model = this.AssignParent(actionBuilder); - - return model; + return AssignParent(actionBuilder); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs index 032f02cd05..7b7cb352dc 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs @@ -14,7 +14,7 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests.ObjectModel; public sealed class ParseValueExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) { [Fact] - public async Task ParseTable() + public async Task ParseTableAsync() { // Arrange RecordDataType.Builder recordBuilder = @@ -27,73 +27,73 @@ public sealed class ParseValueExecutorTest(ITestOutputHelper output) : WorkflowA }; ParseValue model = this.CreateModel( - this.FormatDisplayName(nameof(ParseTable)), + this.FormatDisplayName(nameof(ParseTableAsync)), recordBuilder, @"{ ""key1"": ""val1"" }"); // Act ParseValueExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyState("Target", FormulaValue.NewRecordFromFields(new NamedValue("key1", FormulaValue.New("val1")))); } [Fact] - public async Task ParseBoolean() + public async Task ParseBooleanAsync() { // Arrange ParseValue model = this.CreateModel( - this.FormatDisplayName(nameof(ParseTable)), + this.FormatDisplayName(nameof(ParseTableAsync)), new BooleanDataType.Builder(), "True"); // Act ParseValueExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyState("Target", FormulaValue.New(true)); } [Fact] - public async Task ParseNumber() + public async Task ParseNumberAsync() { // Arrange ParseValue model = this.CreateModel( - this.FormatDisplayName(nameof(ParseNumber)), + this.FormatDisplayName(nameof(ParseNumberAsync)), new NumberDataType.Builder(), "42"); // Act ParseValueExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyState("Target", FormulaValue.New(42)); } [Fact] - public async Task ParseString() + public async Task ParseStringAsync() { // Arrange ParseValue model = this.CreateModel( - this.FormatDisplayName(nameof(ParseString)), + this.FormatDisplayName(nameof(ParseStringAsync)), new StringDataType.Builder(), "Hello, World!"); // Act ParseValueExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyState("Target", FormulaValue.New("Hello, World!")); } @@ -109,8 +109,6 @@ public sealed class ParseValueExecutorTest(ITestOutputHelper output) : WorkflowA Value = new ValueExpression.Builder(ValueExpression.Literal(StringDataValue.Create(sourceText))), }; - ParseValue model = this.AssignParent(actionBuilder); - - return model; + return AssignParent(actionBuilder); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs index b3e8d29b1b..7440f367ca 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs @@ -14,7 +14,7 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests.ObjectModel; public sealed class ResetVariableExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) { [Fact] - public async Task ResetDefinedValue() + public async Task ResetDefinedValueAsync() { // Arrange this.State.Set("MyVar1", FormulaValue.New("Value #1")); @@ -22,36 +22,36 @@ public sealed class ResetVariableExecutorTest(ITestOutputHelper output) : Workfl ResetVariable model = this.CreateModel( - this.FormatDisplayName(nameof(ResetDefinedValue)), + this.FormatDisplayName(nameof(ResetDefinedValueAsync)), FormatVariablePath("MyVar1")); // Act ResetVariableExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyUndefined("MyVar1"); this.VerifyState("MyVar2", FormulaValue.New("Value #2")); } [Fact] - public async Task ResetUndefinedValue() + public async Task ResetUndefinedValueAsync() { // Arrange this.State.Set("MyVar1", FormulaValue.New("Value #1")); ResetVariable model = this.CreateModel( - this.FormatDisplayName(nameof(ResetUndefinedValue)), + this.FormatDisplayName(nameof(ResetUndefinedValueAsync)), FormatVariablePath("NoVar")); // Act ResetVariableExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyUndefined("NoVar"); this.VerifyState("MyVar1", FormulaValue.New("Value #1")); } @@ -66,8 +66,6 @@ public sealed class ResetVariableExecutorTest(ITestOutputHelper output) : Workfl Variable = InitializablePropertyPath.Create(variablePath), }; - ResetVariable model = this.AssignParent(actionBuilder); - - return model; + return AssignParent(actionBuilder); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs index 8704e80257..6f1f45a52b 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs @@ -13,20 +13,20 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests.ObjectModel; public sealed class SendActivityExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) { [Fact] - public async Task CaptureActivity() + public async Task CaptureActivityAsync() { // Arrange SendActivity model = this.CreateModel( - this.FormatDisplayName(nameof(CaptureActivity)), + this.FormatDisplayName(nameof(CaptureActivityAsync)), "Test activity message"); // Act SendActivityExecutor action = new(model, this.State); - WorkflowEvent[] events = await this.Execute(action); + WorkflowEvent[] events = await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); Assert.Contains(events, e => e is MessageActivityEvent); } @@ -46,8 +46,6 @@ public sealed class SendActivityExecutorTest(ITestOutputHelper output) : Workflo Activity = activityBuilder.Build(), }; - SendActivity model = this.AssignParent(actionBuilder); - - return model; + return AssignParent(actionBuilder); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs index 50c547fda5..beea04eee8 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs @@ -14,42 +14,42 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests.ObjectModel; public sealed class SetTextVariableExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) { [Fact] - public async Task SetLiteralValue() + public async Task SetLiteralValueAsync() { // Arrange SetTextVariable model = this.CreateModel( - this.FormatDisplayName(nameof(SetLiteralValue)), + this.FormatDisplayName(nameof(SetLiteralValueAsync)), FormatVariablePath("TextVar"), "Text variable value"); // Act SetTextVariableExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyState("TextVar", FormulaValue.New("Text variable value")); } [Fact] - public async Task UpdateExistingValue() + public async Task UpdateExistingValueAsync() { // Arrange this.State.Set("TextVar", FormulaValue.New("Old value")); SetTextVariable model = this.CreateModel( - this.FormatDisplayName(nameof(UpdateExistingValue)), + this.FormatDisplayName(nameof(UpdateExistingValueAsync)), FormatVariablePath("TextVar"), "New value"); // Act SetTextVariableExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyState("TextVar", FormulaValue.New("New value")); } @@ -64,8 +64,6 @@ public sealed class SetTextVariableExecutorTest(ITestOutputHelper output) : Work Value = TemplateLine.Parse(textValue), }; - SetTextVariable model = this.AssignParent(actionBuilder); - - return model; + return AssignParent(actionBuilder); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs index fe9e195e08..12cbe68fb3 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs @@ -14,147 +14,139 @@ namespace Microsoft.Agents.Workflows.Declarative.UnitTests.ObjectModel; public sealed class SetVariableExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) { [Fact] - public void InvalidModel() - { + public void InvalidModel() => // Arrange, Act, Assert Assert.Throws(() => new SetVariableExecutor(new SetVariable(), this.State)); - } [Fact] - public async Task SetNumericValue() - { + public async Task SetNumericValueAsync() => // Arrange, Act, Assert - await this.ExecuteTest( - displayName: nameof(SetNumericValue), + await this.ExecuteTestAsync( + displayName: nameof(SetNumericValueAsync), variableName: "TestVariable", variableValue: new NumberDataValue(42), expectedValue: FormulaValue.New(42)); - } [Fact] - public async Task SetStringValue() - { + public async Task SetStringValueAsync() => // Arrange, Act, Assert - await this.ExecuteTest( - displayName: nameof(SetStringValue), + await this.ExecuteTestAsync( + displayName: nameof(SetStringValueAsync), variableName: "TestVariable", variableValue: new StringDataValue("Text"), expectedValue: FormulaValue.New("Text")); - } [Fact] - public async Task SetBooleanValue() - { + public async Task SetBooleanValueAsync() => // Arrange, Act, Assert - await this.ExecuteTest( - displayName: nameof(SetBooleanValue), + await this.ExecuteTestAsync( + displayName: nameof(SetBooleanValueAsync), variableName: "TestVariable", variableValue: new BooleanDataValue(true), expectedValue: FormulaValue.New(true)); - } [Fact] - public async Task SetBooleanExpression() + public async Task SetBooleanExpressionAsync() { // Arrange ValueExpression.Builder expressionBuilder = new(ValueExpression.Expression("true || false")); // Act, Assert - await this.ExecuteTest( - displayName: nameof(SetBooleanExpression), + await this.ExecuteTestAsync( + displayName: nameof(SetBooleanExpressionAsync), variableName: "TestVariable", valueExpression: expressionBuilder, expectedValue: FormulaValue.New(true)); } [Fact] - public async Task SetNumberExpression() + public async Task SetNumberExpressionAsync() { // Arrange ValueExpression.Builder expressionBuilder = new(ValueExpression.Expression("9 - 3")); // Act, Assert - await this.ExecuteTest( - displayName: nameof(SetBooleanExpression), + await this.ExecuteTestAsync( + displayName: nameof(SetBooleanExpressionAsync), variableName: "TestVariable", valueExpression: expressionBuilder, expectedValue: FormulaValue.New(6)); } [Fact] - public async Task SetStringExpression() + public async Task SetStringExpressionAsync() { // Arrange ValueExpression.Builder expressionBuilder = new(ValueExpression.Expression(@"Concatenate(""A"", ""B"", ""C"")")); // Act, Assert - await this.ExecuteTest( - displayName: nameof(SetBooleanExpression), + await this.ExecuteTestAsync( + displayName: nameof(SetBooleanExpressionAsync), variableName: "TestVariable", valueExpression: expressionBuilder, expectedValue: FormulaValue.New("ABC")); } [Fact] - public async Task SetBooleanVariable() + public async Task SetBooleanVariableAsync() { // Arrange this.State.Set("Source", FormulaValue.New(true)); ValueExpression.Builder expressionBuilder = new(ValueExpression.Variable(PropertyPath.TopicVariable("Source"))); // Act, Assert - await this.ExecuteTest( - displayName: nameof(SetBooleanExpression), + await this.ExecuteTestAsync( + displayName: nameof(SetBooleanExpressionAsync), variableName: "TestVariable", valueExpression: expressionBuilder, expectedValue: FormulaValue.New(true)); } [Fact] - public async Task SetNumberVariable() + public async Task SetNumberVariableAsync() { // Arrange this.State.Set("Source", FormulaValue.New(321)); ValueExpression.Builder expressionBuilder = new(ValueExpression.Variable(PropertyPath.TopicVariable("Source"))); // Act, Assert - await this.ExecuteTest( - displayName: nameof(SetBooleanExpression), + await this.ExecuteTestAsync( + displayName: nameof(SetBooleanExpressionAsync), variableName: "TestVariable", valueExpression: expressionBuilder, expectedValue: FormulaValue.New(321)); } [Fact] - public async Task SetStringVariable() + public async Task SetStringVariableAsync() { // Arrange this.State.Set("Source", FormulaValue.New("Test")); ValueExpression.Builder expressionBuilder = new(ValueExpression.Variable(PropertyPath.TopicVariable("Source"))); // Act, Assert - await this.ExecuteTest( - displayName: nameof(SetBooleanExpression), + await this.ExecuteTestAsync( + displayName: nameof(SetBooleanExpressionAsync), variableName: "TestVariable", valueExpression: expressionBuilder, expectedValue: FormulaValue.New("Test")); } [Fact] - public async Task UpdateExistingValue() + public async Task UpdateExistingValueAsync() { // Arrange this.State.Set("VarA", FormulaValue.New(33)); // Act, Assert - await this.ExecuteTest( - displayName: nameof(UpdateExistingValue), + await this.ExecuteTestAsync( + displayName: nameof(UpdateExistingValueAsync), variableName: "VarA", variableValue: new NumberDataValue(42), expectedValue: FormulaValue.New(42)); } - private Task ExecuteTest( + private Task ExecuteTestAsync( string displayName, string variableName, DataValue variableValue, @@ -164,10 +156,10 @@ public sealed class SetVariableExecutorTest(ITestOutputHelper output) : Workflow ValueExpression.Builder expressionBuilder = new(ValueExpression.Literal(variableValue)); // Act & Assert - return this.ExecuteTest(displayName, variableName, expressionBuilder, expectedValue); + return this.ExecuteTestAsync(displayName, variableName, expressionBuilder, expectedValue); } - private async Task ExecuteTest( + private async Task ExecuteTestAsync( string displayName, string variableName, ValueExpression.Builder valueExpression, @@ -184,10 +176,10 @@ public sealed class SetVariableExecutorTest(ITestOutputHelper output) : Workflow // Act SetVariableExecutor action = new(model, this.State); - await this.Execute(action); + await this.ExecuteAsync(action); // Assert - this.VerifyModel(model, action); + VerifyModel(model, action); this.VerifyState(variableName, expectedValue); } @@ -202,8 +194,6 @@ public sealed class SetVariableExecutorTest(ITestOutputHelper output) : Workflow Value = valueExpression, }; - SetVariable model = this.AssignParent(actionBuilder); - - return model; + return AssignParent(actionBuilder); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs index a9af40c682..54b3b6fdc9 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs @@ -24,7 +24,7 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor protected string FormatDisplayName(string name) => $"{this.GetType().Name}_{name}"; - internal async Task Execute(DeclarativeActionExecutor executor) + internal async Task ExecuteAsync(DeclarativeActionExecutor executor) { TestWorkflowExecutor workflowExecutor = new(); WorkflowBuilder workflowBuilder = new(workflowExecutor); @@ -36,7 +36,7 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor return events; } - internal void VerifyModel(DialogAction model, DeclarativeActionExecutor action) + internal static void VerifyModel(DialogAction model, DeclarativeActionExecutor action) { Assert.Equal(model.Id, action.Id); Assert.Equal(model, action.Model); @@ -52,12 +52,10 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor protected void VerifyUndefined(string variableName) => this.VerifyUndefined(variableName, VariableScopeNames.Topic); - internal void VerifyUndefined(string variableName, string scopeName) - { + internal void VerifyUndefined(string variableName, string scopeName) => Assert.IsType(this.State.Get(variableName, scopeName)); - } - protected TAction AssignParent(DialogAction.Builder actionBuilder) where TAction : DialogAction + protected static TAction AssignParent(DialogAction.Builder actionBuilder) where TAction : DialogAction { OnActivity.Builder activityBuilder = new() @@ -76,9 +74,7 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor ReflectingExecutor(nameof(TestWorkflowExecutor)), IMessageHandler { - public async ValueTask HandleAsync(WorkflowFormulaState message, IWorkflowContext context) - { + public async ValueTask HandleAsync(WorkflowFormulaState message, IWorkflowContext context) => await context.SendMessageAsync(new ExecutorResultMessage(this.Id)).ConfigureAwait(false); - } } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs index 84136e9fab..ca3a8905d9 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs @@ -71,7 +71,7 @@ public class TemplateExtensionsTests(ITestOutputHelper output) : RecalcEngineTes public void FormatTextSegment() { // Arrange - TemplateSegment textSegment = TextSegment.FromText("Hello World"); + TemplateSegment textSegment = TemplateSegment.FromText("Hello World"); TemplateLine line = new([textSegment]); // Act @@ -125,7 +125,7 @@ public class TemplateExtensionsTests(ITestOutputHelper output) : RecalcEngineTes public void FormatMultipleSegments() { // Arrange - TemplateSegment textSegment = TextSegment.FromText("Hello "); + TemplateSegment textSegment = TemplateSegment.FromText("Hello "); ExpressionSegment expressionSegment = new(ValueExpression.Expression(@"""World""")); TemplateLine line = new([textSegment, expressionSegment]); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs index c12396b395..9fcf045a1e 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs @@ -47,36 +47,28 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest #region BoolExpression Tests [Fact] - public void BoolExpressionGetValueForNull() - { + public void BoolExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((BoolExpression)null!); - } [Fact] - public void BoolExpressionGetValueForInvalid() - { + public void BoolExpressionGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(BoolExpression.Variable(PropertyPath.TopicVariable(Variables.StringValue))); - } [Fact] - public void BoolExpressionGetValueForLiteral() - { + public void BoolExpressionGetValueForLiteral() => // Arrange, Act & Assert this.EvaluateExpression( BoolExpression.Literal(true), expectedValue: true); - } [Fact] - public void BoolExpressionGetValueForBlank() - { + public void BoolExpressionGetValueForBlank() => // Arrange, Act & Assert this.EvaluateExpression( BoolExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: false); - } [Fact] public void BoolExpressionGetValueForVariable() @@ -88,49 +80,39 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest } [Fact] - public void BoolExpressionGetValueForFormula() - { + public void BoolExpressionGetValueForFormula() => // Arrange, Act & Assert this.EvaluateExpression( BoolExpression.Expression("true || false"), expectedValue: true); - } #endregion #region StringExpression Tests [Fact] - public void StringExpressionGetValueForNull() - { + public void StringExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((StringExpression)null!); - } [Fact] - public void StringExpressionGetValueForInvalid() - { + public void StringExpressionGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(StringExpression.Variable(PropertyPath.TopicVariable(Variables.BoolValue))); - } [Fact] - public void StringExpressionGetValueForStringExpressionBlank() - { + public void StringExpressionGetValueForStringExpressionBlank() => // Arrange, Act & Assert this.EvaluateExpression( StringExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: string.Empty); - } [Fact] - public void StringExpressionGetValueForLiteral() - { + public void StringExpressionGetValueForLiteral() => // Arrange, Act & Assert this.EvaluateExpression( StringExpression.Literal("test"), expectedValue: "test"); - } [Fact] public void StringExpressionGetValueForVariable() @@ -142,13 +124,11 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest } [Fact] - public void StringExpressionGetValueForFormula() - { + public void StringExpressionGetValueForFormula() => // Arrange, Act & Assert this.EvaluateExpression( StringExpression.Expression(@"""A"" & ""B"""), expectedValue: "AB"); - } [Fact] public void StringExpressionGetValueForRecord() @@ -173,36 +153,28 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest #region IntExpression Tests [Fact] - public void IntExpressionGetValueForNull() - { + public void IntExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((IntExpression)null!); - } [Fact] - public void IntExpressionGetValueForInvalid() - { + public void IntExpressionGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(IntExpression.Variable(PropertyPath.TopicVariable(Variables.StringValue))); - } [Fact] - public void IntExpressionGetValueForIntExpressionBlank() - { + public void IntExpressionGetValueForIntExpressionBlank() => // Arrange, Act & Assert this.EvaluateExpression( IntExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: 0); - } [Fact] - public void IntExpressionGetValueForLiteral() - { + public void IntExpressionGetValueForLiteral() => // Arrange, Act & Assert this.EvaluateExpression( IntExpression.Literal(7), expectedValue: 7); - } [Fact] public void IntExpressionGetValueForVariable() @@ -214,96 +186,76 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest } [Fact] - public void IntExpressionGetValueForFormula() - { + public void IntExpressionGetValueForFormula() => // Arrange, Act & Assert this.EvaluateExpression( IntExpression.Expression("1 + 6"), expectedValue: 7); - } #endregion #region NumberExpression Tests [Fact] - public void NumberExpressionGetValueForNull() - { + public void NumberExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((NumberExpression)null!); - } [Fact] - public void NumberExpressionGetValueForInvalid() - { + public void NumberExpressionGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(NumberExpression.Variable(PropertyPath.TopicVariable(Variables.StringValue))); - } [Fact] - public void NumberExpressionGetValueForBlank() - { + public void NumberExpressionGetValueForBlank() => // Arrange, Act & Assert this.EvaluateExpression( NumberExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: 0); - } [Fact] - public void NumberExpressionGetValueForLiteral() - { + public void NumberExpressionGetValueForLiteral() => // Arrange, Act & Assert this.EvaluateExpression( NumberExpression.Literal(3.14), expectedValue: 3.14); - } [Fact] - public void NumberExpressionGetValueForVariable() - { + public void NumberExpressionGetValueForVariable() => // Arrange, Act & Assert this.EvaluateExpression( NumberExpression.Variable(PropertyPath.TopicVariable(Variables.NumberValue)), expectedValue: 33.3); - } [Fact] - public void NumberExpressionGetValueForFormula() - { + public void NumberExpressionGetValueForFormula() => // Arrange, Act & Assert this.EvaluateExpression( NumberExpression.Expression("31.1 + 2.2"), expectedValue: 33.3); - } #endregion #region DataValueExpression Tests [Fact] - public void DataValueExpressionGetValueForNull() - { + public void DataValueExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((ValueExpression)null!); - } [Fact] - public void DataValueExpressionGetValueForDataValueExpressionBlank() - { + public void DataValueExpressionGetValueForDataValueExpressionBlank() => // Arrange, Act & Assert this.EvaluateExpression( ValueExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: DataValue.Blank()); - } [Fact] - public void DataValueExpressionGetValueForLiteral() - { + public void DataValueExpressionGetValueForLiteral() => // Arrange, Act & Assert this.EvaluateExpression( ValueExpression.Literal(DataValue.Create("test")), expectedValue: DataValue.Create("test")); - } [Fact] public void DataValueExpressionGetValueForVariable() @@ -315,85 +267,69 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest } [Fact] - public void DataValueExpressionGetValueForFormula() - { + public void DataValueExpressionGetValueForFormula() => // Arrange, Act & Assert this.EvaluateExpression( ValueExpression.Expression(@"""A"" & ""B"""), expectedValue: DataValue.Create("AB")); - } #endregion #region EnumExpression Tests [Fact] - public void EnumExpressionGetValueForNull() - { + public void EnumExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((EnumExpression)null!); - } [Fact] - public void EnumExpressionGetValueForInvalid() - { + public void EnumExpressionGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(EnumExpression.Variable(PropertyPath.TopicVariable(Variables.BoolValue))); - } [Fact] - public void EnumExpressionGetValueForLiteral() - { + public void EnumExpressionGetValueForLiteral() => // Arrange, Act & Assert - this.EvaluateExpression( + this.EvaluateExpression( EnumExpression.Literal(VariablesToClearWrapper.Get(VariablesToClear.ConversationScopedVariables)), expectedValue: VariablesToClear.ConversationScopedVariables); - } [Fact] - public void EnumExpressionGetValueForBlank() - { + public void EnumExpressionGetValueForBlank() => // Arrange, Act & Assert - this.EvaluateExpression( + this.EvaluateExpression( EnumExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: VariablesToClear.ConversationScopedVariables); - } [Fact] public void EnumExpressionGetValueForVariable() { // Arrange, Act & Assert - this.EvaluateExpression( + this.EvaluateExpression( EnumExpression.Variable(PropertyPath.TopicVariable(Variables.EnumValue)), expectedValue: VariablesToClear.ConversationScopedVariables); } [Fact] - public void EnumExpressionGetValueForFormula() - { + public void EnumExpressionGetValueForFormula() => // Arrange, Act & Assert - this.EvaluateExpression( + this.EvaluateExpression( EnumExpression.Expression(@"""ConversationScoped"" & ""Variables"""), expectedValue: VariablesToClear.ConversationScopedVariables); - } #endregion #region ObjectExpression Tests [Fact] - public void ObjectExpressionGetValueForNull() - { + public void ObjectExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((ObjectExpression)null!); - } [Fact] - public void ObjectExpressionGetValueForInvalid() - { + public void ObjectExpressionGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(ObjectExpression.Variable(PropertyPath.TopicVariable(Variables.BoolValue))); - } [Fact] public void ObjectExpressionGetValueForLiteral() @@ -402,20 +338,18 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest RecordDataValue.Builder recordBuilder = new(); recordBuilder.Properties.Add(nameof(EnvironmentVariableReference.SchemaName), new StringDataValue("test")); RecordDataValue objectRecord = recordBuilder.Build(); - EnvironmentVariableReference element = new EnvironmentVariableReference.Builder() { SchemaName = "test" }.Build(); + _ = new EnvironmentVariableReference.Builder() { SchemaName = "test" }.Build(); this.EvaluateExpression( ObjectExpression.Literal(objectRecord), expectedValue: objectRecord); } [Fact] - public void ObjectExpressionGetValueForBlank() - { + public void ObjectExpressionGetValueForBlank() => // Arrange, Act & Assert this.EvaluateExpression( ObjectExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: null); - } [Fact] public void ObjectExpressionGetValueForVariable() @@ -431,18 +365,14 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest #region ArrayExpression Tests [Fact] - public void ArrayExpressionGetValueForNull() - { + public void ArrayExpressionGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((ArrayExpression)null!); - } [Fact] - public void ArrayExpressionGetValueForInvalid() - { + public void ArrayExpressionGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(ArrayExpression.Variable(PropertyPath.TopicVariable(Variables.BoolValue))); - } [Fact] public void ArrayExpressionGetValueForLiteral() @@ -455,13 +385,11 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest } [Fact] - public void ArrayExpressionGetValueForBlank() - { + public void ArrayExpressionGetValueForBlank() => // Arrange, Act & Assert this.EvaluateExpression( ArrayExpression.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: []); - } [Fact] public void ArrayExpressionGetValueForVariable() @@ -473,40 +401,32 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest } [Fact] - public void ArrayExpressionGetValueForFormula() - { + public void ArrayExpressionGetValueForFormula() => // Arrange, Act & Assert this.EvaluateExpression( ArrayExpression.Expression(@"[""a"", ""b""]"), expectedValue: ["a", "b"]); - } #endregion #region ArrayExpressionOnly Tests [Fact] - public void ArrayExpressionOnlyGetValueForNull() - { + public void ArrayExpressionOnlyGetValueForNull() => // Arrange, Act & Assert this.EvaluateInvalidExpression((ArrayExpressionOnly)null!); - } [Fact] - public void ArrayExpressionOnlyGetValueForInvalid() - { + public void ArrayExpressionOnlyGetValueForInvalid() => // Arrange, Act & Assert this.EvaluateInvalidExpression(ArrayExpressionOnly.Variable(PropertyPath.TopicVariable(Variables.BoolValue))); - } [Fact] - public void ArrayExpressionOnlyGetValueForBlank() - { + public void ArrayExpressionOnlyGetValueForBlank() => // Arrange, Act & Assert this.EvaluateExpression( ArrayExpressionOnly.Variable(PropertyPath.TopicVariable(Variables.BlankValue)), expectedValue: []); - } [Fact] public void ArrayExpressionOnlyGetValueForVariable() @@ -518,13 +438,11 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest } [Fact] - public void ArrayExpressionOnlyGetValueForFormula() - { + public void ArrayExpressionOnlyGetValueForFormula() => // Arrange, Act & Assert this.EvaluateExpression( ArrayExpressionOnly.Expression(@"[""a"", ""b""]"), expectedValue: ["a", "b"]); - } #endregion @@ -565,35 +483,35 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest private EvaluationResult EvaluateExpression(EnumExpression expression, TEnum expectedValue, SensitivityLevel expectedSensitivity = SensitivityLevel.None) where TEnum : EnumWrapper - => this.EvaluateExpression((evaluator) => evaluator.GetValue(expression), expectedValue, expectedSensitivity); + => this.EvaluateExpression((evaluator) => evaluator.GetValue(expression), expectedValue, expectedSensitivity); private void EvaluateInvalidExpression(EnumExpression expression) - where TException : Exception where TEnum : EnumWrapper - => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); + where TException : Exception + => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); private EvaluationResult EvaluateExpression(ObjectExpression expression, TValue? expectedValue, SensitivityLevel expectedSensitivity = SensitivityLevel.None) where TValue : BotElement - => this.EvaluateExpression((evaluator) => evaluator.GetValue(expression), expectedValue, expectedSensitivity); + => this.EvaluateExpression((evaluator) => evaluator.GetValue(expression), expectedValue, expectedSensitivity); private void EvaluateInvalidExpression(ObjectExpression expression) where TException : Exception where TValue : BotElement - => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); + => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); private ImmutableArray EvaluateExpression(ArrayExpression expression, TValue[] expectedValue) - => this.EvaluateArrayExpression((evaluator) => evaluator.GetValue(expression), expectedValue); + => this.EvaluateArrayExpression((evaluator) => evaluator.GetValue(expression), expectedValue); private void EvaluateInvalidExpression(ArrayExpression expression) where TException : Exception - => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); + => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); private ImmutableArray EvaluateExpression(ArrayExpressionOnly expression, TValue[] expectedValue) - => this.EvaluateArrayExpression((evaluator) => evaluator.GetValue(expression), expectedValue); + => this.EvaluateArrayExpression((evaluator) => evaluator.GetValue(expression), expectedValue); private void EvaluateInvalidExpression(ArrayExpressionOnly expression) where TException : Exception - => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); + => this.EvaluateInvalidExpression((evaluator) => evaluator.GetValue(expression)); private EvaluationResult EvaluateExpression( Func> evaluator, diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/WorkflowTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/WorkflowTest.cs index 7c0ad46d0e..f82107e08f 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/WorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/WorkflowTest.cs @@ -16,7 +16,7 @@ public abstract class WorkflowTest : IDisposable protected WorkflowTest(ITestOutputHelper output) { this.Output = new TestOutputAdapter(output); - System.Console.SetOut(this.Output); + Console.SetOut(this.Output); } public void Dispose() diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs index 54495efbd4..1a1183f240 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs @@ -20,15 +20,14 @@ internal static class TextMessageStreamingExtensions string[] splits = message.Split(' '); for (int i = 0; i < splits.Length - 1; i++) { - splits[i] = splits[i] + ' '; + splits[i] += " "; } return splits.Select(text => (AIContent)new TextContent(text) { RawRepresentation = text }); } - public static AgentRunResponseUpdate ToResponseUpdate(this AIContent content, string? messageId = null, DateTimeOffset? createdAt = null, string? responseId = null, string? agentId = null, string? authorName = null) - { - return new AgentRunResponseUpdate() + public static AgentRunResponseUpdate ToResponseUpdate(this AIContent content, string? messageId = null, DateTimeOffset? createdAt = null, string? responseId = null, string? agentId = null, string? authorName = null) => + new() { Role = ChatRole.Assistant, CreatedAt = createdAt ?? DateTimeOffset.Now, @@ -38,7 +37,6 @@ internal static class TextMessageStreamingExtensions AuthorName = authorName, Contents = [content], }; - } public static IEnumerable ToAgentRunStream(this string message, DateTimeOffset? createdAt = null, string? messageId = null, string? responseId = null, string? agentId = null, string? authorName = null) { @@ -48,16 +46,14 @@ internal static class TextMessageStreamingExtensions return contents.Select(content => content.ToResponseUpdate(messageId, createdAt, responseId, agentId, authorName)); } - public static ChatMessage ToChatMessage(this IEnumerable contents, string? messageId = null, DateTimeOffset? createdAt = null, string? responseId = null, string? agentId = null, string? authorName = null, string? rawRepresentation = null) - { - return new ChatMessage(ChatRole.Assistant, contents is List contentsList ? contentsList : contents.ToList()) + public static ChatMessage ToChatMessage(this IEnumerable contents, string? messageId = null, DateTimeOffset? createdAt = null, string? responseId = null, string? agentId = null, string? authorName = null, string? rawRepresentation = null) => + new(ChatRole.Assistant, contents is List contentsList ? contentsList : contents.ToList()) { AuthorName = authorName, CreatedAt = createdAt ?? DateTimeOffset.Now, MessageId = messageId ?? Guid.NewGuid().ToString("N"), RawRepresentation = rawRepresentation, }; - } public static IEnumerable StreamMessage(this ChatMessage message, string? responseId = null, string? agentId = null) { @@ -67,10 +63,8 @@ internal static class TextMessageStreamingExtensions return message.Contents.Select(content => content.ToResponseUpdate(messageId, message.CreatedAt, responseId: responseId, agentId: agentId, authorName: message.AuthorName)); } - public static IEnumerable StreamMessages(this List messages, string? agentId = null) - { - return messages.SelectMany(message => message.StreamMessage(agentId)); - } + public static IEnumerable StreamMessages(this List messages, string? agentId = null) => + messages.SelectMany(message => message.StreamMessage(agentId)); public static List ToChatMessages(this IEnumerable messages, string? authorName = null) { diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeMapSmokeTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeMapSmokeTests.cs index 590fcce60d..1f6fbcb9d8 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeMapSmokeTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeMapSmokeTests.cs @@ -17,7 +17,7 @@ public class EdgeMapSmokeTests runContext.Executors["executor2"] = new ForwardMessageExecutor("executor2"); runContext.Executors["executor3"] = new ForwardMessageExecutor("executor3"); - Dictionary> workflowEdges = new(); + Dictionary> workflowEdges = []; FanInEdgeData edgeData = new(["executor1", "executor2"], "executor3", new EdgeId(0)); Edge fanInEdge = new(edgeData); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeRunnerTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeRunnerTests.cs index 57f77c371e..053758472b 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeRunnerTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/EdgeRunnerTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.Agents.Workflows.UnitTests; public class EdgeRunnerTests { - private async Task CreateAndRunDirectedEdgeTestAsync(bool? conditionMatch = null, bool? targetMatch = null) + private static async Task CreateAndRunDirectedEdgeTestAsync(bool? conditionMatch = null, bool? targetMatch = null) { const string MessageVariant1 = "test"; const string MessageVariant2 = "something else"; @@ -58,21 +58,21 @@ public class EdgeRunnerTests // NoCondition vs Condition(=> true) vs Condition(=> false) // Untargeted vs Targeted(matching) vs Targeted(not matching) - await this.CreateAndRunDirectedEdgeTestAsync(); // NoCondition, Untargeted + await CreateAndRunDirectedEdgeTestAsync(); // NoCondition, Untargeted - await this.CreateAndRunDirectedEdgeTestAsync(targetMatch: true); // NoCondition, Targeted - await this.CreateAndRunDirectedEdgeTestAsync(targetMatch: false); // NoCondition, Targeted(not matching) + await CreateAndRunDirectedEdgeTestAsync(targetMatch: true); // NoCondition, Targeted + await CreateAndRunDirectedEdgeTestAsync(targetMatch: false); // NoCondition, Targeted(not matching) - await this.CreateAndRunDirectedEdgeTestAsync(conditionMatch: true); // Condition(=> true), Untargeted - await this.CreateAndRunDirectedEdgeTestAsync(conditionMatch: false); // Condition(=> false), Untargeted + await CreateAndRunDirectedEdgeTestAsync(conditionMatch: true); // Condition(=> true), Untargeted + await CreateAndRunDirectedEdgeTestAsync(conditionMatch: false); // Condition(=> false), Untargeted - await this.CreateAndRunDirectedEdgeTestAsync(conditionMatch: true, targetMatch: true); // Condition(=> true), Targeted(matching) - await this.CreateAndRunDirectedEdgeTestAsync(conditionMatch: true, targetMatch: false); // Condition(=> true), Targeted(not matching) - await this.CreateAndRunDirectedEdgeTestAsync(conditionMatch: false, targetMatch: true); // Condition(=> false), Targeted(matching) - await this.CreateAndRunDirectedEdgeTestAsync(conditionMatch: false, targetMatch: false); // Condition(=> false), Targeted(not matching) + await CreateAndRunDirectedEdgeTestAsync(conditionMatch: true, targetMatch: true); // Condition(=> true), Targeted(matching) + await CreateAndRunDirectedEdgeTestAsync(conditionMatch: true, targetMatch: false); // Condition(=> true), Targeted(not matching) + await CreateAndRunDirectedEdgeTestAsync(conditionMatch: false, targetMatch: true); // Condition(=> false), Targeted(matching) + await CreateAndRunDirectedEdgeTestAsync(conditionMatch: false, targetMatch: false); // Condition(=> false), Targeted(not matching) } - private async Task CreateAndRunFanOutEdgeTestAsync(bool? assignerSelectsEmpty = null, bool? targetMatch = null) + private static async Task CreateAndRunFanOutEdgeTestAsync(bool? assignerSelectsEmpty = null, bool? targetMatch = null) { TestRunContext runContext = new(); @@ -122,18 +122,18 @@ public class EdgeRunnerTests // NoAssigned vs Assigner(includes output) vs Assigner(does not include output) // Untargeted vs Targeted(matching) vs Targeted(not matching) - await this.CreateAndRunFanOutEdgeTestAsync(); // NoAssigner, Untargeted + await CreateAndRunFanOutEdgeTestAsync(); // NoAssigner, Untargeted - await this.CreateAndRunFanOutEdgeTestAsync(targetMatch: true); // NoAssigner, Targeted(matching) - await this.CreateAndRunFanOutEdgeTestAsync(targetMatch: false); // NoAssigner, Targeted(not matching) + await CreateAndRunFanOutEdgeTestAsync(targetMatch: true); // NoAssigner, Targeted(matching) + await CreateAndRunFanOutEdgeTestAsync(targetMatch: false); // NoAssigner, Targeted(not matching) - await this.CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: false); // Assigner(includes output), Untargeted - await this.CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: true); // Assigner(does not include output), Untargeted + await CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: false); // Assigner(includes output), Untargeted + await CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: true); // Assigner(does not include output), Untargeted - await this.CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: false, targetMatch: true); // Assigner(includes output), Targeted(matching) - await this.CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: false, targetMatch: false); // Assigner(includes output), Targeted(not matching) - await this.CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: true, targetMatch: true); // Assigner(does not include output), Targeted(matching) - await this.CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: true, targetMatch: false); // Assigner(does not include output), Targeted(not matching) + await CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: false, targetMatch: true); // Assigner(includes output), Targeted(matching) + await CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: false, targetMatch: false); // Assigner(includes output), Targeted(not matching) + await CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: true, targetMatch: true); // Assigner(does not include output), Targeted(matching) + await CreateAndRunFanOutEdgeTestAsync(assignerSelectsEmpty: true, targetMatch: false); // Assigner(does not include output), Targeted(not matching) } [Fact] @@ -154,13 +154,13 @@ public class EdgeRunnerTests // Step 4: Send message from executor2, should forward now. FanInEdgeState state = runner.CreateState(); - await RunIteration(); + await RunIterationAsync(); // Repeat the same sequence, to ensure state is properly reset inside of FanInEdgeState. runContext.QueuedMessages.Clear(); - await RunIteration(); + await RunIterationAsync(); - async ValueTask RunIteration() + async ValueTask RunIterationAsync() { await runner.ChaseAsync("executor1", new("part1"), state, tracer: null); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ForwardMessageExecutor.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ForwardMessageExecutor.cs index 256660d0ba..00f509d8df 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ForwardMessageExecutor.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ForwardMessageExecutor.cs @@ -4,8 +4,6 @@ namespace Microsoft.Agents.Workflows.UnitTests; internal sealed class ForwardMessageExecutor(string? id = null) : Executor(id) where TMessage : notnull { - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler((message, ctx) => ctx.SendMessageAsync(message)); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler((message, ctx) => ctx.SendMessageAsync(message)); } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InMemoryJsonStore.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InMemoryJsonStore.cs index 162e791397..803487526b 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InMemoryJsonStore.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InMemoryJsonStore.cs @@ -9,7 +9,7 @@ namespace Microsoft.Agents.Workflows.UnitTests; internal sealed class InMemoryJsonStore : JsonCheckpointStore { - private readonly Dictionary> _store = new(); + private readonly Dictionary> _store = []; private RunCheckpointCache EnsureRunStore(string runId) { diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InProcessStateTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InProcessStateTests.cs index 1fbbe76799..397f0dddb7 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InProcessStateTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/InProcessStateTests.cs @@ -33,12 +33,12 @@ public class InProcessStateTests for (int i = 0; i < stateActions.Length; i++) { - result[i] = CreateWrapperAsync(stateActions[i]); + result[i] = CreateWrapper(stateActions[i]); } return result; - Func> CreateWrapperAsync(Func action) + Func> CreateWrapper(Func action) { return async (turn, context, cancellation) => @@ -68,7 +68,7 @@ public class InProcessStateTests => currState => currState.HasValue ? currState + 1 : defaultValue; private static Func ValidateState(int expectedValue, string? because = null, params object[] becauseArgs) - => (int? currState) => + => currState => { currState.Should().Be(expectedValue, because, becauseArgs); @@ -76,7 +76,7 @@ public class InProcessStateTests }; private static Func MaxTurns(int maxTurns) - => (object? maybeTurn) => maybeTurn is not TurnToken turn || turn.Count < maxTurns; + => maybeTurn => maybeTurn is not TurnToken turn || turn.Count < maxTurns; [Fact] public async Task InProcessRun_StateShouldPersist_NotCheckpointedAsync() diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/JsonSerializationTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/JsonSerializationTests.cs index 6a0a40e3c2..2eaefee2b6 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/JsonSerializationTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/JsonSerializationTests.cs @@ -28,7 +28,7 @@ public class JsonSerializationTests } } - private static int s_nextEdgeId = 0; + private static int s_nextEdgeId; private static EdgeId TakeEdgeId() => new(Interlocked.Increment(ref s_nextEdgeId)); @@ -36,14 +36,14 @@ public class JsonSerializationTests { JsonMarshaller marshaller = new(externalOptions); - JsonElement element = marshaller.Marshal(value); + JsonElement element = marshaller.Marshal(value); T deserialized = marshaller.Marshal(element); - if (deserialized != null) + if (deserialized is not null) { - if (predicate != null) + if (predicate is not null) { - deserialized.Should().Match(predicate); + deserialized.Should().Match(predicate); } return deserialized; @@ -56,7 +56,7 @@ public class JsonSerializationTests [Fact] public void Test_EdgeConnection_JsonRoundtrip() { - EdgeConnection connection = new(new List { "Source1", "Source2" }, new List { "Sink1", "Sink2" }); + EdgeConnection connection = new(["Source1", "Source2"], ["Sink1", "Sink2"]); RunJsonRoundtrip(connection, predicate: connection.CreateValidator()); } @@ -167,11 +167,9 @@ public class JsonSerializationTests .AddEdge(stringToInt, forwardInt) .AddEdge(forwardInt, intToString); - Workflow workflow = builder.BuildWithOutput( + return builder.BuildWithOutput( intToString, StreamingAggregators.Last(), (int _, int __) => true); - - return workflow; } private static WorkflowInfo TestWorkflowInfo => CreateTestWorkflow().ToWorkflowInfo(); @@ -181,10 +179,10 @@ public class JsonSerializationTests ValidateExecutorDictionary(prototype.Executors, prototype.Edges, actual.Executors, actual.Edges); ValidateInputPorts(prototype.InputPorts, actual.InputPorts); - actual.InputType.Should().Match(prototype.InputType.CreateValidator()); + actual.InputType.Should().Match(prototype.InputType.CreateValidator()); actual.StartExecutorId.Should().Be(prototype.StartExecutorId); - actual.OutputType.Should().NotBeNull().And.Match(prototype.OutputType!.CreateValidator()); + actual.OutputType.Should().NotBeNull().And.Match(prototype.OutputType!.CreateValidator()); actual.OutputCollectorId.Should().NotBeNull().And.Be(prototype.OutputCollectorId); void ValidateExecutorDictionary(Dictionary expected, @@ -202,7 +200,7 @@ public class JsonSerializationTests ExecutorInfo actualValue = actual[key]; ExecutorInfo expectedValue = expected[key]; - actualValue.Should().Match(expectedValue.CreateValidator()); + actualValue.Should().Match(expectedValue.CreateValidator()); if (expectedEdges.TryGetValue(key, out List? expectedEdgeList)) { @@ -374,9 +372,9 @@ public class JsonSerializationTests [Fact] public void Test_PortableMessageEnvelope_JsonRoundtrip_BuiltInType() { - string message = "TestMessage"; + const string Message = "TestMessage"; - MessageEnvelope envelope = new(message, new TypeId(typeof(object)), targetId: "Target1"); + MessageEnvelope envelope = new(Message, new TypeId(typeof(object)), targetId: "Target1"); PortableMessageEnvelope value = new(envelope); PortableMessageEnvelope result = RunJsonRoundtrip(value); @@ -460,9 +458,9 @@ public class JsonSerializationTests outstandingRequests: [TestExternalRequest] ); - Dictionary> CreateQueuedMessages() + static Dictionary> CreateQueuedMessages() { - Dictionary> result = new(); + Dictionary> result = []; MessageEnvelope externalEnvelope = new(TestExternalResponse); result.Add(ExecutorIdentity.None, [new(externalEnvelope)]); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageDeliveryValidation.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageDeliveryValidation.cs index 47cf00172e..7ad386e328 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageDeliveryValidation.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageDeliveryValidation.cs @@ -20,7 +20,7 @@ internal static class MessageDeliveryValidation (string expectedSender, List expectedMessages) = forward; return (Action)( - (string senderId) => + senderId => { senderId.Should().Be(expectedSender); queuedMessages[senderId].Should().HaveCount(expectedMessages.Count); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessagingTestHelpers.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessagingTestHelpers.cs deleted file mode 100644 index 07660a7506..0000000000 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessagingTestHelpers.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Linq; -using FluentAssertions; -using Microsoft.Agents.Workflows.Execution; -// Copyright (c) Microsoft. All rights reserved. - -namespace Microsoft.Agents.Workflows.UnitTests; - -internal static class MessagingTestHelpers -{ - private static void CheckForwarded(Dictionary> queuedMessages, params (string expectedSender, List expectedMessages)[] expectedForwards) - { - queuedMessages.Should().HaveCount(expectedForwards.Length); - - IEnumerable> perSenderValidations = expectedForwards.Select( - (forward) => - { - (string expectedSender, List expectedMessages) = forward; - - return (Action)( - (string senderId) => - { - senderId.Should().Be(expectedSender); - queuedMessages[senderId].Should().HaveCount(expectedMessages.Count); - - Action[] validations - = expectedMessages.Select(message => (Action)(envelope => envelope!.Message.Should().Be(message))) - .ToArray(); - - Assert.Collection(queuedMessages[senderId], validations); - }); - } - ); - - Assert.Collection(queuedMessages.Keys, perSenderValidations.ToArray()); - } -} diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ReflectionSmokeTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ReflectionSmokeTest.cs index 22bc81386c..2bf32f71b3 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ReflectionSmokeTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ReflectionSmokeTest.cs @@ -10,16 +10,13 @@ namespace Microsoft.Agents.Workflows.UnitTests; public class BaseTestExecutor : ReflectingExecutor where TActual : ReflectingExecutor { - protected void OnInvokedHandler() - { - this.InvokedHandler = true; - } + protected void OnInvokedHandler() => this.InvokedHandler = true; public bool InvokedHandler { get; private set; - } = false; + } } public class DefaultHandler : BaseTestExecutor, IMessageHandler @@ -68,7 +65,7 @@ public class TypedHandlerWithOutput : BaseTestExecutor RunTestReflectAndRouteMessageAsync(BaseTestExecutor executor, TInput? input = default) where TInput : new() where TE : ReflectingExecutor + private static async ValueTask RunTestReflectAndRouteMessageAsync(BaseTestExecutor executor, TInput? input = default) where TInput : new() where TE : ReflectingExecutor { MessageRouter router = executor.Router; @@ -89,7 +86,7 @@ public class RoutingReflectionTests { DefaultHandler executor = new(); - CallResult? result = await this.RunTestReflectAndRouteMessageAsync(executor); + CallResult? result = await RunTestReflectAndRouteMessageAsync(executor); Assert.NotNull(result); Assert.True(result.IsSuccess); @@ -101,7 +98,7 @@ public class RoutingReflectionTests { TypedHandler executor = new(); - CallResult? result = await this.RunTestReflectAndRouteMessageAsync>(executor, 3); + CallResult? result = await RunTestReflectAndRouteMessageAsync>(executor, 3); Assert.NotNull(result); Assert.True(result.IsSuccess); @@ -113,14 +110,11 @@ public class RoutingReflectionTests { TypedHandlerWithOutput executor = new() { - Handler = (message, context) => - { - return new ValueTask($"{message}"); - } + Handler = (message, context) => new ValueTask($"{message}") }; const string Expected = "3"; - CallResult? result = await this.RunTestReflectAndRouteMessageAsync>(executor, int.Parse(Expected)); + CallResult? result = await RunTestReflectAndRouteMessageAsync>(executor, int.Parse(Expected)); Assert.NotNull(result); Assert.True(result.IsSuccess); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs index 81b7d0a3e5..71b91d6e12 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs @@ -22,31 +22,16 @@ public class RepresentationTests private sealed class TestAgent : AIAgent { - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - { + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - } - public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - { + public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - } } private static InputPort TestInputPort => InputPort.Create("ExternalFunction"); - private static List ListAggregator(List? current, T incoming) - { - if (current is null) - { - return [incoming]; - } - - current.Add(incoming); - return current; - } - private static async ValueTask RunExecutorishInfoMatchTestAsync(ExecutorIsh target) { ExecutorRegistration registration = target.Registration; @@ -59,19 +44,19 @@ public class RepresentationTests public async Task Test_Executorish_InfosAsync() { int testsRun = 0; - await RunExecutorishTest(new TestExecutor()); - await RunExecutorishTest(TestInputPort); - await RunExecutorishTest(new TestAgent()); + await RunExecutorishTestAsync(new TestExecutor()); + await RunExecutorishTestAsync(TestInputPort); + await RunExecutorishTestAsync(new TestAgent()); Func function = MessageHandlerAsync; - await RunExecutorishTest(function.AsExecutor("FunctionExecutor")); + await RunExecutorishTestAsync(function.AsExecutor("FunctionExecutor")); if (Enum.GetValues(typeof(ExecutorIsh.Type)).Length > testsRun + 1) { Assert.Fail("Not all ExecutorIsh types were tested."); } - async ValueTask RunExecutorishTest(ExecutorIsh executorish) + async ValueTask RunExecutorishTestAsync(ExecutorIsh executorish) { await RunExecutorishInfoMatchTestAsync(executorish); testsRun++; @@ -92,9 +77,7 @@ public class RepresentationTests await RunExecutorishInfoMatchTestAsync(outputCollector); } - private static string Source(string id) => $"Source/{id}"; private static string Source(int id) => $"Source/{id}"; - private static string Sink(string id) => $"Sink/{id}"; private static string Sink(int id) => $"Sink/{id}"; private static Func Condition() => Condition(); @@ -156,7 +139,7 @@ public class RepresentationTests RunEdgeInfoMatchTest(fanInEdge, fanInEdge4, expect: false); // Identity matters RunEdgeInfoMatchTest(fanInEdge, fanInEdge5, expect: false); - void RunEdgeInfoMatchTest(Edge edge, Edge? comparatorEdge = null, bool expect = true) + static void RunEdgeInfoMatchTest(Edge edge, Edge? comparatorEdge = null, bool expect = true) { comparatorEdge ??= edge; @@ -180,7 +163,7 @@ public class RepresentationTests RunWorkflowInfoMatchTest(Step1EntryPoint.WorkflowInstance, Step2EntryPoint.WorkflowInstance, expect: false); - void RunWorkflowInfoMatchTest(Workflow workflow, Workflow? comparator = null, bool expect = true) + static void RunWorkflowInfoMatchTest(Workflow workflow, Workflow? comparator = null, bool expect = true) { comparator ??= workflow; diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs index 169c805348..2399571aac 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System.IO; +using System.Linq; using System.Threading.Tasks; using Microsoft.Agents.Workflows.Reflection; @@ -38,20 +39,15 @@ internal static class Step1EntryPoint internal sealed class UppercaseExecutor() : ReflectingExecutor("UppercaseExecutor"), IMessageHandler { - public async ValueTask HandleAsync(string message, IWorkflowContext context) - { - string result = message.ToUpperInvariant(); - return result; - } + public async ValueTask HandleAsync(string message, IWorkflowContext context) => + message.ToUpperInvariant(); } internal sealed class ReverseTextExecutor() : ReflectingExecutor("ReverseTextExecutor"), IMessageHandler { public async ValueTask HandleAsync(string message, IWorkflowContext context) { - char[] charArray = message.ToCharArray(); - System.Array.Reverse(charArray); - string result = new(charArray); + string result = string.Concat(message.Reverse()); await context.AddEventAsync(new WorkflowCompletedEvent(result)).ConfigureAwait(false); return result; diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs index a51d34aa5f..3d46355407 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs @@ -14,7 +14,7 @@ internal static class Step2EntryPoint { get { - string[] spamKeywords = { "spam", "advertisement", "offer" }; + string[] spamKeywords = ["spam", "advertisement", "offer"]; DetectSpamExecutor detectSpam = new(spamKeywords); RespondToMessageExecutor respondToMessage = new(); @@ -84,7 +84,7 @@ internal sealed class RespondToMessageExecutor : ReflectingExecutor checkpoints = new(); + List checkpoints = []; CancellationTokenSource cancellationSource = new(); StreamingRun handle = checkpointed.Run; @@ -66,7 +66,7 @@ internal static class Step5EntryPoint { case SuperStepCompletedEvent stepCompletedEvt: CheckpointInfo? checkpoint = stepCompletedEvt.CompletionInfo!.Checkpoint; - if (checkpoint != null) + if (checkpoint is not null) { checkpoints.Add(checkpoint); } @@ -113,29 +113,4 @@ internal static class Step5EntryPoint return request.CreateResponse(result); } - - /// - /// This converts the incoming from the judge to a status text that can be displayed - /// to the user. - /// - /// - /// This works correctly timing-wise because both the and the - /// are one edge from the (see the workflow definition in the - /// method). That means they will get the at the same time (one - /// SuperStep after the Judge has generated it.) - /// - /// - /// - /// - private static string ComputeStreamingOutput(NumberSignal signal, string? runningResult) - { - return signal switch - { - NumberSignal.Matched => "You guessed correctly! You Win!", - NumberSignal.Above => "Your guess was too high. Try again.", - NumberSignal.Below => "Your guess was too low. Try again.", - - _ => runningResult ?? string.Empty - }; - } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs index 18a855c803..0a0b32fd3e 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs @@ -55,13 +55,13 @@ internal static class Step6EntryPoint private sealed class RoundRobinGroupChatManagerOptions : GroupChatManagerOptions { - public int? MaxTurns { get; set; } = null; + public int? MaxTurns { get; set; } } private sealed class RoundRobinGroupChatManager() : GroupChatManager { - public int TurnCount { get; private set; } = 0; - public int? MaxTurns { get; private set; } = null; + public int TurnCount { get; private set; } + public int? MaxTurns { get; private set; } protected internal override void Configure(RoundRobinGroupChatManagerOptions options) { @@ -107,14 +107,12 @@ internal sealed class HelloAgent(string id = nameof(HelloAgent)) : AIAgent public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - AgentRunResponseUpdate response = new(ChatRole.Assistant, "Hello World!") + yield return new(ChatRole.Assistant, "Hello World!") { AgentId = this.Id, AuthorName = this.Name, MessageId = Guid.NewGuid().ToString("N"), }; - - yield return response; } } @@ -152,44 +150,34 @@ internal sealed class EchoAgent(string id = nameof(EchoAgent)) : AIAgent collectedText.AppendLine(messageText); } - AgentRunResponseUpdate result = new(ChatRole.Assistant, collectedText.ToString()) + yield return new(ChatRole.Assistant, collectedText.ToString()) { AgentId = this.Id, AuthorName = this.Name, MessageId = Guid.NewGuid().ToString("N"), }; - - yield return result; } } internal sealed class GroupChatHistory { - private readonly List _messages = new(); - private int _bookmark = 0; + private readonly List _messages = []; + private int _bookmark; - public void AddMessage(ChatMessage message) - { + public void AddMessage(ChatMessage message) => this._messages.Add(message); - } - public void AddMessages(IEnumerable messages) - { + public void AddMessages(IEnumerable messages) => this._messages.AddRange(messages); - } - public void UpdateBookmark() - { + public void UpdateBookmark() => this._bookmark = this._messages.Count; - } public IReadOnlyList FullHistory => this._messages.AsReadOnly(); public IEnumerable NewMessagesThisTurn => this._messages.Skip(this._bookmark); } -internal class GroupChatManagerOptions -{ -} +internal class GroupChatManagerOptions; internal abstract class GroupChatManager { @@ -205,8 +193,8 @@ internal abstract class GroupChatManager : GroupChatManager where TOpt internal sealed class GroupChatBuilder { - private readonly List _participants = new(); - private readonly List _shouldEmitEvents = new(); + private readonly List _participants = []; + private readonly List _shouldEmitEvents = []; private readonly Func _managerFactory; private GroupChatBuilder(Func managerFactory) @@ -214,10 +202,8 @@ internal sealed class GroupChatBuilder this._managerFactory = managerFactory; } - public static GroupChatBuilder Create() where TManager : GroupChatManager, new() - { - return new GroupChatBuilder(participantIds => new TManager() { ParticipantIds = participantIds }); - } + public static GroupChatBuilder Create() where TManager : GroupChatManager, new() => + new(participantIds => new TManager() { ParticipantIds = participantIds }); public static GroupChatBuilder Create(Action configure) where TManager : GroupChatManager, new() @@ -283,12 +269,10 @@ internal sealed class GroupChatBuilder this._autoStartConversation = autoStartConversation; } - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler>(this.HandleChatMessagesAsync) - .AddHandler(this.HandleChatMessageAsync) - .AddHandler(this.AssignNextTurnAsync); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler>(this.HandleChatMessagesAsync) + .AddHandler(this.HandleChatMessageAsync) + .AddHandler(this.AssignNextTurnAsync); private async Task TryAutoStartConversationAsync(IWorkflowContext context) { @@ -315,28 +299,26 @@ internal sealed class GroupChatBuilder await this.TryAutoStartConversationAsync(context).ConfigureAwait(false); } - private int _inConversationFlag = 0; + private int _inConversationFlag; /// /// Atomically switches to "in conversation" state if not already in that state. /// /// if the state was changed, otherwise. - private bool TryEnterConversation() - { - return Interlocked.CompareExchange(ref this._inConversationFlag, 1, 0) == 0; - } + private bool TryEnterConversation() => + Interlocked.CompareExchange(ref this._inConversationFlag, 1, 0) == 0; - private bool _shouldHostEmitEvents = false; + private bool _shouldHostEmitEvents; private async ValueTask AssignNextTurnAsync(TurnToken token, IWorkflowContext context) { if (this.TryEnterConversation()) { // Capture the initial turn token's EmitEvents setting - this._shouldHostEmitEvents = token.EmitEvents.HasValue ? token.EmitEvents.Value : false; + this._shouldHostEmitEvents = token.EmitEvents ?? false; } int? nextSpeakerIndex = this._manager.GetNextTurnExecutor(this._history); - if (nextSpeakerIndex == null) + if (nextSpeakerIndex is null) { await context.AddEventAsync(new WorkflowCompletedEvent()) .ConfigureAwait(false); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs index 7facfabc2b..2bb9420f98 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs @@ -182,7 +182,7 @@ public class SampleSmokeTest internal sealed class VerifyingPlaybackResponder { public (TInput input, TResponse response)[] Responses { get; } - private int _position = 0; + private int _position; public VerifyingPlaybackResponder(params (TInput input, TResponse response)[] responses) { diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs index 5e67966711..5705c47ce9 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs @@ -24,7 +24,7 @@ public class SpecializedExecutorSmokeTests { List result = messages.Select(ToMessage).ToList(); - ChatMessage ToMessage(string text) + static ChatMessage ToMessage(string text) { if (string.IsNullOrEmpty(text)) { @@ -34,7 +34,7 @@ public class SpecializedExecutorSmokeTests string[] splits = text.Split(' '); for (int i = 0; i < splits.Length - 1; i++) { - splits[i] = splits[i] + ' '; + splits[i] += ' '; } List contents = splits.Select(text => new TextContent(text) { RawRepresentation = text }).ToList(); @@ -49,21 +49,17 @@ public class SpecializedExecutorSmokeTests return result; } - public static TestAIAgent FromStrings(params string[] messages) - { - return new TestAIAgent(ToChatMessages(messages)); - } + public static TestAIAgent FromStrings(params string[] messages) => + new(ToChatMessages(messages)); public List Messages { get; } = Validate(messages) ?? []; - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - { - return Task.FromResult(new AgentRunResponse(this.Messages) + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + Task.FromResult(new AgentRunResponse(this.Messages) { AgentId = this.Id, ResponseId = Guid.NewGuid().ToString("N") }); - } public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -88,11 +84,11 @@ public class SpecializedExecutorSmokeTests { string? currentMessageId = null; - if (candidateMessages != null) + if (candidateMessages is not null) { foreach (ChatMessage message in candidateMessages) { - if (currentMessageId == null) + if (currentMessageId is null) { currentMessageId = message.MessageId; } @@ -109,32 +105,22 @@ public class SpecializedExecutorSmokeTests internal sealed class TestWorkflowContext : IWorkflowContext { - public List> Updates { get; } = new(); + public List> Updates { get; } = []; - public ValueTask AddEventAsync(WorkflowEvent workflowEvent) - { - return default; - } + public ValueTask AddEventAsync(WorkflowEvent workflowEvent) => + default; - public ValueTask QueueClearScopeAsync(string? scopeName = null) - { - return default; - } + public ValueTask QueueClearScopeAsync(string? scopeName = null) => + default; - public ValueTask QueueStateUpdateAsync(string key, T? value, string? scopeName = null) - { - return default; - } + public ValueTask QueueStateUpdateAsync(string key, T? value, string? scopeName = null) => + default; - public ValueTask ReadStateAsync(string key, string? scopeName = null) - { + public ValueTask ReadStateAsync(string key, string? scopeName = null) => throw new NotImplementedException(); - } - public ValueTask> ReadStateKeysAsync(string? scopeName = null) - { + public ValueTask> ReadStateKeysAsync(string? scopeName = null) => throw new NotImplementedException(); - } public ValueTask SendMessageAsync(object message, string? targetId = null) { @@ -166,7 +152,7 @@ public class SpecializedExecutorSmokeTests { for (int i = 0; i < messageSplits.Length - 1; i++) { - messageSplits[i] = messageSplits[i] + ' '; + messageSplits[i] += ' '; } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/StateKeyObjectTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/StateKeyObjectTests.cs index 9ab2d4891b..d71983197a 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/StateKeyObjectTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/StateKeyObjectTests.cs @@ -88,7 +88,7 @@ public class StateKeyObjectTests ValidateMatch(sharedScope2Key, privateScope1, expectedStrict: false, expectedLoose: false); ValidateMatch(sharedScope2Key, privateScope2, expectedStrict: false, expectedLoose: false); - void ValidateMatch(UpdateKey key, ScopeId scope, bool expectedStrict, bool expectedLoose) + static void ValidateMatch(UpdateKey key, ScopeId scope, bool expectedStrict, bool expectedLoose) { key.IsMatchingScope(scope, strict: true).Should().Be(expectedStrict); key.IsMatchingScope(scope, strict: false).Should().Be(expectedLoose); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestJsonSerializable.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestJsonSerializable.cs index 98d1aa9618..62334e4a30 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestJsonSerializable.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestJsonSerializable.cs @@ -17,7 +17,7 @@ internal sealed class TestJsonSerializable public override bool Equals(object? obj) { - if (obj == null) + if (obj is null) { return false; } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestRunContext.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestRunContext.cs index 4e19bf38b1..33160aa1d9 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestRunContext.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestRunContext.cs @@ -30,7 +30,7 @@ public class TestRunContext : IRunnerContext => runnerContext.SendMessageAsync(executorId, message, targetId); } - public List Events { get; } = new(); + public List Events { get; } = []; public ValueTask AddEventAsync(WorkflowEvent workflowEvent) { @@ -38,39 +38,32 @@ public class TestRunContext : IRunnerContext return default; } - public IWorkflowContext Bind(string executorId) - { - return new BoundContext(executorId, this); - } + public IWorkflowContext Bind(string executorId) => new BoundContext(executorId, this); - public List ExternalRequests { get; } = new(); + public List ExternalRequests { get; } = []; public ValueTask PostAsync(ExternalRequest request) { this.ExternalRequests.Add(request); return default; } - internal Dictionary> QueuedMessages { get; } = new(); + internal Dictionary> QueuedMessages { get; } = []; public ValueTask SendMessageAsync(string sourceId, object message, string? targetId = null) { if (!this.QueuedMessages.TryGetValue(sourceId, out List? deliveryQueue)) { - this.QueuedMessages[sourceId] = deliveryQueue = new(); + this.QueuedMessages[sourceId] = deliveryQueue = []; } deliveryQueue.Add(new(message, targetId: targetId)); return default; } - StepContext IRunnerContext.Advance() - { + StepContext IRunnerContext.Advance() => throw new NotImplementedException(); - } - public Dictionary Executors { get; } = new(); + public Dictionary Executors { get; } = []; - ValueTask IRunnerContext.EnsureExecutorAsync(string executorId, IStepTracer? tracer) - { - return new(this.Executors[executorId]); - } + ValueTask IRunnerContext.EnsureExecutorAsync(string executorId, IStepTracer? tracer) => + new(this.Executors[executorId]); } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestingExecutor.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestingExecutor.cs index c90e88416c..20873a4008 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestingExecutor.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/TestingExecutor.cs @@ -8,23 +8,21 @@ using System.Threading.Tasks; namespace Microsoft.Agents.Workflows.UnitTests; -internal class TestingExecutor : Executor, IDisposable +internal abstract class TestingExecutor : Executor, IDisposable { private readonly bool _loop; private readonly Func>[] _actions; - private readonly HashSet _linkedTokens = new(); + private readonly HashSet _linkedTokens = []; private CancellationTokenSource _internalCts = new(); - public TestingExecutor(string? id = null, bool loop = false, params Func>[] actions) : base(id) + protected TestingExecutor(string? id = null, bool loop = false, params Func>[] actions) : base(id) { this._loop = loop; this._actions = actions; } - public void UnlinkCancellation(CancellationToken token) - { + public void UnlinkCancellation(CancellationToken token) => this._linkedTokens.Remove(token); - } public void LinkCancellation(CancellationToken token) { @@ -34,18 +32,14 @@ internal class TestingExecutor : Executor, IDisposable tokenSource.Dispose(); } - public void SetCancel() - { + public void SetCancel() => Volatile.Read(ref this._internalCts).Cancel(); - } - protected sealed override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler(this.RouteToActions); - } + protected sealed override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler(this.RouteToActionsAsync); - private int _nextActionIndex = 0; - private ValueTask RouteToActions(TIn message, IWorkflowContext context) + private int _nextActionIndex; + private ValueTask RouteToActionsAsync(TIn message, IWorkflowContext context) { if (this._nextActionIndex >= this._actions.Length) { @@ -75,10 +69,8 @@ internal class TestingExecutor : Executor, IDisposable this.Dispose(false); } - protected virtual void Dispose(bool disposing) - { + protected virtual void Dispose(bool disposing) => this._internalCts.Dispose(); - } public void Dispose() { diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ValidationExtensions.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ValidationExtensions.cs index ac77efcf8f..7a39afedcd 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ValidationExtensions.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ValidationExtensions.cs @@ -116,12 +116,9 @@ internal static partial class ValidationExtensions innerValidatorExpr ); - Expression> validatorExpr = Expression.Lambda>( + return Expression.Lambda>( bodyExpression, - outerParam - ); - - return validatorExpr; + outerParam); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/WorkflowBuilderSmokeTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/WorkflowBuilderSmokeTests.cs index 3bb33d4411..7e5fdcfb9f 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/WorkflowBuilderSmokeTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/WorkflowBuilderSmokeTests.cs @@ -9,26 +9,16 @@ public partial class WorkflowBuilderSmokeTests { private sealed class NoOpExecutor(string? id = null) : Executor(id) { - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler( - (msg, ctx) => - { - return ctx.SendMessageAsync(msg); - }); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler( + (msg, ctx) => ctx.SendMessageAsync(msg)); } private sealed class SomeOtherNoOpExecutor(string? id = null) : Executor(id) { - protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) - { - return routeBuilder.AddHandler( - (msg, ctx) => - { - return ctx.SendMessageAsync(msg); - }); - } + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) => + routeBuilder.AddHandler( + (msg, ctx) => ctx.SendMessageAsync(msg)); } [Fact] @@ -42,7 +32,7 @@ public partial class WorkflowBuilderSmokeTests workflow.Registrations.Should().HaveCount(1); workflow.Registrations.Should().ContainKey("start"); - workflow.Registrations["start"].ExecutorType.Should().Be(typeof(NoOpExecutor)); + workflow.Registrations["start"].ExecutorType.Should().Be(); } [Fact] @@ -57,7 +47,7 @@ public partial class WorkflowBuilderSmokeTests workflow.Registrations.Should().HaveCount(1); workflow.Registrations.Should().ContainKey("start"); - workflow.Registrations["start"].ExecutorType.Should().Be(typeof(NoOpExecutor)); + workflow.Registrations["start"].ExecutorType.Should().Be(); } [Fact] @@ -89,6 +79,6 @@ public partial class WorkflowBuilderSmokeTests workflow.Registrations.Should().HaveCount(1); workflow.Registrations.Should().ContainKey("start"); - workflow.Registrations["start"].ExecutorType.Should().Be(typeof(NoOpExecutor)); + workflow.Registrations["start"].ExecutorType.Should().Be(); } } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/A2AAgentTests.cs index bc8f1a3938..998c295799 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/A2AAgentTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/A2AAgentTests.cs @@ -54,11 +54,9 @@ public sealed class A2AAgentTests : IDisposable } [Fact] - public void Constructor_WithNullA2AClient_ThrowsArgumentNullException() - { + public void Constructor_WithNullA2AClient_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => new A2AAgent(null!)); - } [Fact] public void Constructor_WithDefaultParameters_UsesBaseProperties() @@ -96,10 +94,10 @@ public sealed class A2AAgentTests : IDisposable { MessageId = "response-123", Role = MessageRole.Agent, - Parts = new List - { + Parts = + [ new TextPart { Text = "Hello! How can I help you today?" } - } + ] }; var inputMessages = new List @@ -139,10 +137,10 @@ public sealed class A2AAgentTests : IDisposable { MessageId = "response-123", Role = MessageRole.Agent, - Parts = new List - { + Parts = + [ new TextPart { Text = "Response" } - }, + ], ContextId = "new-context-id" }; @@ -194,10 +192,10 @@ public sealed class A2AAgentTests : IDisposable { MessageId = "response-123", Role = MessageRole.Agent, - Parts = new List - { + Parts = + [ new TextPart { Text = "Response" } - }, + ], ContextId = "different-context" }; @@ -221,7 +219,7 @@ public sealed class A2AAgentTests : IDisposable { MessageId = "stream-1", Role = MessageRole.Agent, - Parts = new List { new TextPart { Text = "Hello" } }, + Parts = [new TextPart { Text = "Hello" }], ContextId = "stream-context" }; @@ -267,14 +265,14 @@ public sealed class A2AAgentTests : IDisposable { MessageId = "stream-1", Role = MessageRole.Agent, - Parts = new List { new TextPart { Text = "Response" } }, + Parts = [new TextPart { Text = "Response" }], ContextId = "new-stream-context" }; var thread = this._agent.GetNewThread(); // Act - await foreach (var update in this._agent.RunStreamingAsync(inputMessages, thread)) + await foreach (var _ in this._agent.RunStreamingAsync(inputMessages, thread)) { // Just iterate through to trigger the logic } @@ -298,7 +296,7 @@ public sealed class A2AAgentTests : IDisposable thread.ConversationId = "existing-context-id"; // Act - await foreach (var update in this._agent.RunStreamingAsync(inputMessages, thread)) + await foreach (var _ in this._agent.RunStreamingAsync(inputMessages, thread)) { // Just iterate through to trigger the logic } @@ -325,7 +323,7 @@ public sealed class A2AAgentTests : IDisposable { MessageId = "stream-1", Role = MessageRole.Agent, - Parts = new List { new TextPart { Text = "Response" } }, + Parts = [new TextPart { Text = "Response" }], ContextId = "different-context" }; @@ -362,11 +360,11 @@ public sealed class A2AAgentTests : IDisposable // Arrange var inputMessages = new List { - new(ChatRole.User, new List - { + new(ChatRole.User, + [ new TextContent("Check this file:"), new HostedFileContent("https://example.com/file.pdf") - }) + ]) }; // Act @@ -409,7 +407,7 @@ public sealed class A2AAgentTests : IDisposable // Return the pre-configured non-streaming response if (this.ResponseToReturn is not null) { - var jsonRpcResponse = JsonRpcResponse.CreateJsonRpcResponse("response-id", this.ResponseToReturn); + var jsonRpcResponse = JsonRpcResponse.CreateJsonRpcResponse("response-id", this.ResponseToReturn); return new HttpResponseMessage(HttpStatusCode.OK) { @@ -424,7 +422,7 @@ public sealed class A2AAgentTests : IDisposable await SseFormatter.WriteAsync( new SseItem[] { - new(JsonRpcResponse.CreateJsonRpcResponse("response-id", this.StreamingResponseToReturn!)) + new(JsonRpcResponse.CreateJsonRpcResponse("response-id", this.StreamingResponseToReturn!)) }.ToAsyncEnumerable(), stream, (item, writer) => diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs index 11189a53a8..9005d6cc11 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs @@ -65,7 +65,7 @@ public sealed class A2ACardResolverExtensionsTests : IDisposable this._handler.ResponsesToReturn.Enqueue(new Message { Role = MessageRole.Agent, - Parts = new List { new TextPart { Text = "Response" } }, + Parts = [new TextPart { Text = "Response" }], }); var agent = await this._resolver.GetAIAgentAsync(this._httpClient); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2AMessageExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2AMessageExtensionsTests.cs index 7c5c7a9852..c91c6ccbcf 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2AMessageExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2AMessageExtensionsTests.cs @@ -15,7 +15,7 @@ public sealed class A2AMessageExtensionsTests public void ToChatMessage_WithMixedParts_ReturnsChatMessageWithMixedContents() { // Arrange - var uri = "https://example.com/image.jpg"; + const string Uri = "https://example.com/image.jpg"; var metadata = new Dictionary { @@ -26,12 +26,12 @@ public sealed class A2AMessageExtensionsTests { MessageId = "mixed-parts-id", Role = MessageRole.Agent, - Parts = new List - { + Parts = + [ new TextPart { Text = "Here's an image:" }, - new FilePart { File = new FileWithUri { Uri = uri } }, + new FilePart { File = new FileWithUri { Uri = Uri } }, new TextPart { Text = "What do you think?" } - }, + ], Metadata = metadata }; @@ -50,7 +50,7 @@ public sealed class A2AMessageExtensionsTests Assert.Equal("Here's an image:", firstContent.Text); var fileContent = Assert.IsType(result.Contents[1]); - Assert.Equal(uri, fileContent.FileId); + Assert.Equal(Uri, fileContent.FileId); var lastContent = Assert.IsType(result.Contents[2]); Assert.Equal("What do you think?", lastContent.Text); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2APartExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2APartExtensionsTests.cs index 88b8da93db..5f0600084c 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2APartExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/A2APartExtensionsTests.cs @@ -63,8 +63,8 @@ public sealed class A2APartExtensionsTests public void ToAIContent_WithFilePartWithFileWithUri_ReturnsHostedFileContent() { // Arrange - var uri = "https://example.com/file.txt"; - var filePart = new FilePart { File = new FileWithUri { Uri = uri } }; + const string Uri = "https://example.com/file.txt"; + var filePart = new FilePart { File = new FileWithUri { Uri = Uri } }; // Act var result = filePart.ToAIContent(); @@ -74,7 +74,7 @@ public sealed class A2APartExtensionsTests Assert.Equal(filePart, result.RawRepresentation); var hostedFileContent = Assert.IsType(result); - Assert.Equal(uri, hostedFileContent.FileId); + Assert.Equal(Uri, hostedFileContent.FileId); Assert.Null(hostedFileContent.AdditionalProperties); } @@ -85,12 +85,10 @@ public sealed class A2APartExtensionsTests var customPart = new MockPart(); // Act & Assert - var exception = Assert.Throws(() => customPart.ToAIContent()); + var exception = Assert.Throws(customPart.ToAIContent); Assert.Equal("Part type 'MockPart' is not supported.", exception.Message); } // Mock class for testing unsupported scenarios - private sealed class MockPart : Part - { - } + private sealed class MockPart : Part; } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/AIContentExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/AIContentExtensionsTests.cs index cf07dcffbf..9c057a8133 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/AIContentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.A2A.UnitTests/Extensions/AIContentExtensionsTests.cs @@ -31,8 +31,8 @@ public sealed class AIContentExtensionsTests public void ToA2APart_WithHostedFileContent_ReturnsFilePart() { // Arrange - var uri = "https://example.com/file.txt"; - var hostedFileContent = new HostedFileContent(uri); + const string Uri = "https://example.com/file.txt"; + var hostedFileContent = new HostedFileContent(Uri); // Act var result = hostedFileContent.ToA2APart(); @@ -44,7 +44,7 @@ public sealed class AIContentExtensionsTests Assert.NotNull(filePart.File); var fileWithUri = Assert.IsType(filePart.File); - Assert.Equal(uri, fileWithUri.Uri); + Assert.Equal(Uri, fileWithUri.Uri); } [Fact] @@ -54,7 +54,7 @@ public sealed class AIContentExtensionsTests var unsupportedContent = new MockAIContent(); // Act & Assert - var exception = Assert.Throws(() => unsupportedContent.ToA2APart()); + var exception = Assert.Throws(unsupportedContent.ToA2APart); Assert.Equal("Unsupported content type: MockAIContent.", exception.Message); } @@ -106,7 +106,5 @@ public sealed class AIContentExtensionsTests } // Mock class for testing unsupported scenarios - private sealed class MockAIContent : AIContent - { - } + private sealed class MockAIContent : AIContent; } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs index 9906b62a21..3fa0139f85 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs @@ -82,18 +82,18 @@ public class AIAgentTests public async Task InvokeWithStringMessageCallsMockedInvokeWithMessageInCollectionAsync() { // Arrange - var message = "Hello, Agent!"; + const string Message = "Hello, Agent!"; var options = new AgentRunOptions(); 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._agentThreadMock.Object, options, cancellationToken); Assert.Equal(this._invokeResponse, response); // Verify that the mocked method was called with the expected parameters this._agentMock.Verify( x => x.RunAsync( - It.Is>(messages => messages.Count == 1 && messages.First().Text == message), + It.Is>(messages => messages.Count == 1 && messages.First().Text == Message), this._agentThreadMock.Object, options, cancellationToken), @@ -162,12 +162,12 @@ public class AIAgentTests public async Task InvokeStreamingWithStringMessageCallsMockedInvokeWithMessageInCollectionAsync() { // Arrange - var message = "Hello, Agent!"; + const string Message = "Hello, Agent!"; var options = new AgentRunOptions(); 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._agentThreadMock.Object, options, cancellationToken)) { // Assert Assert.Contains(response, this._invokeStreamingResponses); @@ -176,7 +176,7 @@ public class AIAgentTests // Verify that the mocked method was called with the expected parameters this._agentMock.Verify( x => x.RunStreamingAsync( - It.Is>(messages => messages.Count == 1 && messages.First().Text == message), + It.Is>(messages => messages.Count == 1 && messages.First().Text == Message), this._agentThreadMock.Object, options, cancellationToken), @@ -361,28 +361,21 @@ public class AIAgentTests private sealed class MockAgent : AIAgent { - public static new Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IEnumerable messages, CancellationToken cancellationToken) - { - return AIAgent.NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken); - } + public static new Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IEnumerable messages, CancellationToken cancellationToken) => AIAgent.NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken); public override Task RunAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, - CancellationToken cancellationToken = default) - { + CancellationToken cancellationToken = default) => throw new NotImplementedException(); - } public override IAsyncEnumerable RunStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, - CancellationToken cancellationToken = default) - { + CancellationToken cancellationToken = default) => throw new NotImplementedException(); - } } private static async IAsyncEnumerable ToAsyncEnumerableAsync(IEnumerable values) diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextProviderTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextProviderTests.cs index f5eea0d5ed..ebeec30917 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextProviderTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextProviderTests.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text.Json; using System.Threading; @@ -14,7 +13,7 @@ public class AIContextProviderTests public async Task InvokedAsync_ReturnsCompletedTaskAsync() { var provider = new TestAIContextProvider(); - var messages = new ReadOnlyCollection(new List()); + var messages = new ReadOnlyCollection([]); var task = provider.InvokedAsync(new(messages)); Assert.Equal(default, task); } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextTests.cs index 2a85a525e5..23a96974af 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIContextTests.cs @@ -1,7 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System.Collections.Generic; - namespace Microsoft.Extensions.AI.Agents.Abstractions.UnitTests; /// @@ -25,11 +23,11 @@ public class AIContextTests { var context = new AIContext { - Messages = new List - { + Messages = + [ new(ChatRole.User, "Hello"), new(ChatRole.Assistant, "Hi there!") - } + ] }; Assert.NotNull(context.Messages); @@ -43,11 +41,11 @@ public class AIContextTests { var context = new AIContext { - Tools = new List - { + Tools = + [ AIFunctionFactory.Create(() => "Function1", "Function1", "Description1"), AIFunctionFactory.Create(() => "Function2", "Function2", "Description2"), - } + ] }; Assert.NotNull(context.Tools); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs index 7ea4b83ff0..8828f896e6 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs @@ -21,9 +21,7 @@ public class AgentRunOptionsTests } [Fact] - public void CloningConstructorThrowsIfNull() - { + public void CloningConstructorThrowsIfNull() => // Act & Assert Assert.Throws(() => new AgentRunOptions(null!)); - } } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs index b83050b620..31fae011e0 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs @@ -259,7 +259,7 @@ public class AgentRunResponseTests var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal))); // Act. - response.TryDeserialize(TestJsonSerializerContext.Default.Options, out Animal? animal); + response.TryDeserialize(TestJsonSerializerContext.Default.Options, out Animal? animal); // Assert. Assert.NotNull(animal); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs index a5e5cb097e..e7e38e4c38 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs @@ -31,10 +31,8 @@ public class AgentRunResponseUpdateExtensionsTests } [Fact] - public void ToAgentRunResponseWithInvalidArgsThrows() - { + public void ToAgentRunResponseWithInvalidArgsThrows() => Assert.Throws("updates", () => ((List)null!).ToAgentRunResponse()); - } [Theory] [InlineData(false)] @@ -129,7 +127,7 @@ public class AgentRunResponseUpdateExtensionsTests ChatMessage message = response.Messages.Single(); Assert.NotNull(message); - Assert.Equal(expected.Count + (gapLength * ((numSequences - 1) + (gapBeginningEnd ? 2 : 0))), message.Contents.Count); + Assert.Equal(expected.Count + (gapLength * (numSequences - 1 + (gapBeginningEnd ? 2 : 0))), message.Contents.Count); TextContent[] contents = message.Contents.OfType().ToArray(); Assert.Equal(expected.Count, contents.Length); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs index b22b7fc51a..7400fc1714 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs @@ -32,13 +32,13 @@ public class AgentThreadTests { // Arrange var thread = new AgentThread(); - var conversationid = "test-thread-id"; + const string Conversationid = "test-thread-id"; // Act - thread.ConversationId = conversationid; + thread.ConversationId = Conversationid; // Assert - Assert.Equal(conversationid, thread.ConversationId); + Assert.Equal(Conversationid, thread.ConversationId); Assert.Null(thread.MessageStore); } @@ -226,7 +226,7 @@ public class AgentThreadTests Assert.True(json.TryGetProperty("conversationId", out var idProperty)); Assert.Equal("TestConvId", idProperty.GetString()); - Assert.False(json.TryGetProperty("storeState", out var storeStateProperty)); + Assert.False(json.TryGetProperty("storeState", out _)); } /// @@ -236,8 +236,10 @@ public class AgentThreadTests public async Task VerifyThreadSerializationWithMessagesAsync() { // Arrange - var store = new InMemoryChatMessageStore(); - store.Add(new ChatMessage(ChatRole.User, "TestContent") { AuthorName = "TestAuthor" }); + var store = new InMemoryChatMessageStore + { + new ChatMessage(ChatRole.User, "TestContent") { AuthorName = "TestAuthor" } + }; var thread = new AgentThread { MessageStore = store }; // Act @@ -246,7 +248,7 @@ public class AgentThreadTests // Assert Assert.Equal(JsonValueKind.Object, json.ValueKind); - Assert.False(json.TryGetProperty("conversationId", out var idProperty)); + Assert.False(json.TryGetProperty("conversationId", out _)); Assert.True(json.TryGetProperty("storeState", out var storeStateProperty)); Assert.Equal(JsonValueKind.Object, storeStateProperty.ValueKind); @@ -329,15 +331,4 @@ public class AgentThreadTests } #endregion Serialize Tests - - private static async Task> ToListAsync(IAsyncEnumerable values) - { - var result = new List(); - await foreach (var v in values) - { - result.Add(v); - } - - return result; - } } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs index 22abf8e0ba..98e50a9802 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs @@ -60,11 +60,9 @@ public class DelegatingAIAgentTests /// Verify that constructor throws ArgumentNullException when innerAgent is null. /// [Fact] - public void RequiresInnerAgent() - { + public void RequiresInnerAgent() => // Act & Assert Assert.Throws("innerAgent", () => new TestDelegatingAIAgent(null!)); - } /// /// Verify that constructor sets the inner agent correctly. @@ -218,11 +216,9 @@ public class DelegatingAIAgentTests /// Verify that GetService throws ArgumentNullException when serviceType is null. /// [Fact] - public void GetServiceThrowsForNullType() - { + public void GetServiceThrowsForNullType() => // Act & Assert Assert.Throws("serviceType", () => this._delegatingAgent.GetService(null!)); - } /// /// Verify that GetService returns the delegating agent itself when requesting compatible type and key is null. diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/InMemoryChatMessageStoreTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/InMemoryChatMessageStoreTests.cs index 98bd4caa46..41c1982dd6 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/InMemoryChatMessageStoreTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/InMemoryChatMessageStoreTests.cs @@ -16,11 +16,9 @@ namespace Microsoft.Extensions.AI.Agents.Abstractions.UnitTests; public class InMemoryChatMessageStoreTests { [Fact] - public void Constructor_Throws_ForNullReducer() - { + public void Constructor_Throws_ForNullReducer() => // Arrange & Act & Assert Assert.Throws(() => new InMemoryChatMessageStore(null!)); - } [Fact] public void Constructor_DefaultsToBeforeMessageRetrieval_ForNotProvidedTriggerEvent() @@ -91,7 +89,7 @@ public class InMemoryChatMessageStoreTests [Fact] public async Task DeserializeConstructorWithEmptyElementAsync() { - var emptyObject = JsonSerializer.Deserialize("{}", TestJsonSerializerContext.Default.JsonElement); + var emptyObject = JsonSerializer.Deserialize("{}", TestJsonSerializerContext.Default.JsonElement); var newStore = new InMemoryChatMessageStore(emptyObject); @@ -304,9 +302,11 @@ public class InMemoryChatMessageStoreTests public void Clear_RemovesAllMessages() { // Arrange - var store = new InMemoryChatMessageStore(); - store.Add(new ChatMessage(ChatRole.User, "First")); - store.Add(new ChatMessage(ChatRole.Assistant, "Second")); + var store = new InMemoryChatMessageStore + { + new ChatMessage(ChatRole.User, "First"), + new ChatMessage(ChatRole.Assistant, "Second") + }; // Act store.Clear(); @@ -527,8 +527,10 @@ public class InMemoryChatMessageStoreTests var reducerMock = new Mock(); - var store = new InMemoryChatMessageStore(reducerMock.Object, InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded); - store.Add(originalMessages[0]); + var store = new InMemoryChatMessageStore(reducerMock.Object, InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded) + { + originalMessages[0] + }; // Act var result = (await store.GetMessagesAsync(CancellationToken.None)).ToList(); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.A2A.Tests/Converters/MessageConverterTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.A2A.Tests/Converters/MessageConverterTests.cs index 24ce1d3892..c14e3d823c 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.A2A.Tests/Converters/MessageConverterTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.A2A.Tests/Converters/MessageConverterTests.cs @@ -64,10 +64,10 @@ public class MessageConverterTests { MessageId = "test-id", Role = MessageRole.User, - Parts = new List - { + Parts = + [ new TextPart { Text = "Hello, world!" } - } + ] } }; @@ -116,13 +116,13 @@ public class MessageConverterTests { MessageId = "user-msg", Role = MessageRole.User, - Parts = new List { new TextPart { Text = "User message" } } + Parts = [new TextPart { Text = "User message" }] }, new() { MessageId = "agent-msg", Role = MessageRole.Agent, - Parts = new List { new TextPart { Text = "Agent response" } } + Parts = [new TextPart { Text = "Agent response" }] } }; @@ -151,7 +151,7 @@ public class MessageConverterTests { MessageId = "valid-msg", Role = MessageRole.User, - Parts = new List { new TextPart { Text = "Valid message" } } + Parts = [new TextPart { Text = "Valid message" }] }, new() { @@ -232,7 +232,7 @@ public class MessageConverterTests var unsupportedContent = new DataContent(new byte[] { 1, 2, 3 }, "image/png"); var chatMessage = new ChatMessage(ChatRole.User, [unsupportedContent]); - var exception = Assert.Throws(() => chatMessage.ToA2AMessage()); + var exception = Assert.Throws(chatMessage.ToA2AMessage); Assert.Contains("Content type 'DataContent' is not supported", exception.Message); } @@ -257,7 +257,7 @@ public class MessageConverterTests { MessageId = "test", Role = MessageRole.User, - Parts = new List { new TextPart { Text = "Test" } } + Parts = [new TextPart { Text = "Test" }] }; var result = new List { message }.ToChatMessages(); @@ -273,7 +273,7 @@ public class MessageConverterTests { MessageId = "test", Role = MessageRole.Agent, - Parts = new List { new TextPart { Text = "Test" } } + Parts = [new TextPart { Text = "Test" }] }; var result = new List { message }.ToChatMessages(); @@ -289,7 +289,7 @@ public class MessageConverterTests { MessageId = "test", Role = (MessageRole)999, // Unknown role - Parts = new List { new TextPart { Text = "Test" } } + Parts = [new TextPart { Text = "Test" }] }; var result = new List { message }.ToChatMessages(); @@ -346,7 +346,7 @@ public class MessageConverterTests { MessageId = "test", Role = MessageRole.User, - Parts = new List { textPart } + Parts = [textPart] }; var result = new List { message }.ToChatMessages(); @@ -374,7 +374,7 @@ public class MessageConverterTests { MessageId = "test", Role = MessageRole.User, - Parts = new List { textPart } + Parts = [textPart] }; var result = new List { message }.ToChatMessages(); @@ -395,7 +395,7 @@ public class MessageConverterTests { MessageId = "test", Role = MessageRole.User, - Parts = new List { filePart } + Parts = [filePart] }; var exception = Assert.Throws(() => new List { message }.ToChatMessages()); @@ -410,7 +410,7 @@ public class MessageConverterTests { MessageId = "test", Role = MessageRole.User, - Parts = new List { dataPart } + Parts = [dataPart] }; var exception = Assert.Throws(() => new List { message }.ToChatMessages()); @@ -429,7 +429,7 @@ public class MessageConverterTests { MessageId = "test-id", Role = MessageRole.User, - Parts = new List { new TextPart { Text = "Test" } }, + Parts = [new TextPart { Text = "Test" }], Metadata = metadata }; @@ -449,7 +449,7 @@ public class MessageConverterTests { MessageId = "test-id", Role = MessageRole.Agent, - Parts = new List { new TextPart { Text = "Test response" } } + Parts = [new TextPart { Text = "Test response" }] }; var result = new List { message }.ToChatMessages(); @@ -465,7 +465,7 @@ public class MessageConverterTests { MessageId = "test-id", Role = MessageRole.User, - Parts = new List() // Empty list + Parts = [] // Empty list }; var result = new List { message }.ToChatMessages(); @@ -502,7 +502,7 @@ public class MessageConverterTests { MessageId = "test-id", Role = MessageRole.User, - Parts = new List { new TextPart { Text = "Test" } }, + Parts = [new TextPart { Text = "Test" }], Metadata = null }; @@ -519,8 +519,8 @@ public class MessageConverterTests { MessageId = "test-id", Role = MessageRole.User, - Parts = new List { new TextPart { Text = "Test" } }, - Metadata = new Dictionary() + Parts = [new TextPart { Text = "Test" }], + Metadata = [] }; var result = new List { message }.ToChatMessages(); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyTests.cs index 995d964c2b..e7297f8a3a 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyTests.cs @@ -75,12 +75,10 @@ public class AgentProxyTests } private const string AgentName = "agentName"; private const string ThreadId = "thread1"; - private static readonly IReadOnlyCollection s_emptyMessages = new List(); + private static readonly IReadOnlyCollection s_emptyMessages = []; - private static bool IsValidGuid(string value) - { - return Guid.TryParse(value, out _); - } + private static bool IsValidGuid(string value) => + Guid.TryParse(value, out _); /// /// Verifies that RunAsync returns a deserialized AgentRunResponse when the actor response status is Completed. @@ -228,7 +226,7 @@ public class AgentProxyTests /// Verifies that passing an AgentThread that is not an AgentProxyThread to RunStreamingAsync throws an ArgumentException. /// [Fact] - public async System.Threading.Tasks.Task RunStreamingAsync_InvalidThread_ThrowsArgumentExceptionAsync() + public async Task RunStreamingAsync_InvalidThread_ThrowsArgumentExceptionAsync() { // Arrange var mockClient = new Mock(); @@ -249,7 +247,7 @@ public class AgentProxyTests /// TODO: Mock IActorClient.SendRequestAsync to return an ActorResponseHandle whose WatchUpdatesAsync yields no updates. /// [Fact(Skip = "Mocking of ActorResponseHandle.WatchUpdatesAsync with IActorClient is required")] - public async System.Threading.Tasks.Task RunStreamingAsync_ValidProxyThread_CompletesSuccessfullyAsync() + public async Task RunStreamingAsync_ValidProxyThread_CompletesSuccessfullyAsync() { // Arrange var mockClient = new Mock(); @@ -271,7 +269,7 @@ public class AgentProxyTests { // Arrange var messages = Array.Empty(); - var threadId = "thread1"; + const string ThreadId = "thread1"; var expectedUpdate = new AgentRunResponseUpdate(ChatRole.Assistant, "response"); var updateTypeInfo = AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(AgentRunResponseUpdate)); @@ -288,7 +286,7 @@ public class AgentProxyTests .ReturnsAsync(mockHandle.Object); var proxy = new AgentProxy("agentName", mockClient.Object); - var thread = proxy.GetThread(threadId); + var thread = proxy.GetThread(ThreadId); // Act var results = new List(); @@ -311,7 +309,7 @@ public class AgentProxyTests { // Arrange var messages = Array.Empty(); - var threadId = "thread1"; + const string ThreadId = "thread1"; var agentRunResponse = new AgentRunResponse { @@ -331,7 +329,7 @@ public class AgentProxyTests .ReturnsAsync(mockHandle.Object); var proxy = new AgentProxy("agentName", mockClient.Object); - var thread = proxy.GetThread(threadId); + var thread = proxy.GetThread(ThreadId); // Act var results = new List(); @@ -353,12 +351,12 @@ public class AgentProxyTests { // Arrange: Create a scenario with streaming updates followed by completion var messages = Array.Empty(); - var threadId = "thread1"; + const string ThreadId = "thread1"; var pendingUpdate = new AgentRunResponseUpdate(ChatRole.Assistant, "streaming response"); var completedResponse = new AgentRunResponse { - Messages = new List { new(ChatRole.Assistant, "streaming response") } + Messages = [new(ChatRole.Assistant, "streaming response")] }; var updates = new List @@ -379,7 +377,7 @@ public class AgentProxyTests .ReturnsAsync(mockHandle.Object); var proxy = new AgentProxy("agentName", mockClient.Object); - var thread = proxy.GetThread(threadId); + var thread = proxy.GetThread(ThreadId); // Act var results = new List(); @@ -418,7 +416,7 @@ public class AgentProxyTests { // Arrange var messages = Array.Empty(); - var threadId = "thread1"; + const string ThreadId = "thread1"; var expectedUpdate = new AgentRunResponseUpdate(ChatRole.Assistant, "response"); var updateTypeInfo = AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(AgentRunResponseUpdate)); var jsonElement = JsonSerializer.SerializeToElement(expectedUpdate, updateTypeInfo); @@ -434,7 +432,7 @@ public class AgentProxyTests .ReturnsAsync(mockHandle.Object); var proxy = new AgentProxy("agentName", mockClient.Object); - var thread = proxy.GetThread(threadId); + var thread = proxy.GetThread(ThreadId); // Act & Assert var exception = await Assert.ThrowsAsync(async () => @@ -451,11 +449,9 @@ public class AgentProxyTests /// Verifies that constructor throws ArgumentNullException when client is null. /// [Fact] - public void Constructor_NullClient_ThrowsArgumentNullException() - { + public void Constructor_NullClient_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => new AgentProxy("agentName", null!)); - } /// /// Verifies that constructor throws ArgumentNullException when name is null. @@ -675,14 +671,14 @@ public class AgentProxyTests // Arrange var mockClient = new Mock(); var mockHandle = new Mock(); - var expectedMessageId = "custom-message-id"; + const string ExpectedMessageId = "custom-message-id"; var response = new AgentRunResponse { Messages = [] }; var jsonElement = JsonSerializer.SerializeToElement(response, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(AgentRunResponse))); var actorResponse = new ActorResponse { ActorId = new ActorId(AgentName, ThreadId), - MessageId = expectedMessageId, + MessageId = ExpectedMessageId, Data = jsonElement, Status = RequestStatus.Completed }; @@ -698,7 +694,7 @@ public class AgentProxyTests var messages = new List { new(ChatRole.User, "first"), - new(ChatRole.User, "last") { MessageId = expectedMessageId } + new(ChatRole.User, "last") { MessageId = ExpectedMessageId } }; // Act @@ -706,7 +702,7 @@ public class AgentProxyTests // Assert mockClient.Verify(c => c.SendRequestAsync( - It.Is(r => r.MessageId == expectedMessageId), + It.Is(r => r.MessageId == ExpectedMessageId), It.IsAny()), Times.Once); } @@ -797,12 +793,12 @@ public class AgentProxyTests public override bool TryGetResponse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out ActorResponse? response) { response = this._response; - return this._response != null; + return this._response is not null; } public override ValueTask GetResponseAsync(CancellationToken cancellationToken) { - if (this._response == null) + if (this._response is null) { throw new InvalidOperationException("No response configured"); } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyThreadTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyThreadTests.cs index a765f45f83..4135c0bfb7 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyThreadTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentProxyThreadTests.cs @@ -12,36 +12,36 @@ public class AgentProxyThreadTests /// /// Provides valid identifier values that conform to RFC 3986 unreserved characters. /// - public static IEnumerable ValidIds => new List - { - new object[] { "normal" }, - new object[] { "test-id" }, - new object[] { "test_id" }, - new object[] { "test.id" }, - new object[] { "test~id" }, - new object[] { "ABC123" }, - new object[] { "a" }, - new object[] { "123" }, - new object[] { "test-id_with.various~chars" }, - new object[] { new string('a', 100) } // Long but valid ID - }; + public static IEnumerable ValidIds { get; } = + [ + ["normal"], + ["test-id"], + ["test_id"], + ["test.id"], + ["test~id"], + ["ABC123"], + ["a"], + ["123"], + ["test-id_with.various~chars"], + [new string('a', 100)] // Long but valid ID + ]; /// /// Provides invalid identifier values that violate the RFC 3986 unreserved character rules. /// - public static IEnumerable InvalidIds => new List - { - new object[] { " " }, // Space not allowed - new object[] { "!@#$%^&*()" }, // Special characters not allowed - new object[] { "test id" }, // Space not allowed - new object[] { "test/id" }, // Forward slash not allowed - new object[] { "test?id" }, // Question mark not allowed - new object[] { "test#id" }, // Hash not allowed - new object[] { "test@id" }, // At symbol not allowed - new object[] { "test id with spaces" }, // Multiple spaces not allowed - new object[] { "test\tid" }, // Tab not allowed - new object[] { "test\nid" }, // Newline not allowed - }; + public static IEnumerable InvalidIds { get; } = + [ + [" "], // Space not allowed + ["!@#$%^&*()"], // Special characters not allowed + ["test id"], // Space not allowed + ["test/id"], // Forward slash not allowed + ["test?id"], // Question mark not allowed + ["test#id"], // Hash not allowed + ["test@id"], // At symbol not allowed + ["test id with spaces"], // Multiple spaces not allowed + ["test\tid"], // Tab not allowed + ["test\nid"], // Newline not allowed + ]; /// /// Verifies that providing valid id to constructor sets the Id property correctly. @@ -76,21 +76,17 @@ public class AgentProxyThreadTests /// Verifies that providing a null id to constructor throws an . /// [Fact] - public void Constructor_NullId_ThrowsArgumentNullException() - { + public void Constructor_NullId_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => new AgentProxyThread(null!)); - } /// /// Verifies that providing an empty id to constructor throws an . /// [Fact] - public void Constructor_EmptyId_ThrowsArgumentException() - { + public void Constructor_EmptyId_ThrowsArgumentException() => // Act & Assert Assert.Throws(() => new AgentProxyThread("")); - } /// /// Verifies that the default constructor initializes the Id property with a valid non-empty GUID string in "N" format. @@ -160,10 +156,7 @@ public class AgentProxyThreadTests var ids = new string[NumberOfIds]; // Act - Create IDs in parallel to test thread safety - Parallel.For(0, NumberOfIds, i => - { - ids[i] = AgentProxyThread.CreateId(); - }); + Parallel.For(0, NumberOfIds, i => ids[i] = AgentProxyThread.CreateId()); // Assert var uniqueIds = ids.Distinct().Count(); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/HostApplicationBuilderAgentExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/HostApplicationBuilderAgentExtensionsTests.cs index fd4dde1d8a..73346f77af 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/HostApplicationBuilderAgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/HostApplicationBuilderAgentExtensionsTests.cs @@ -14,12 +14,10 @@ public class HostApplicationBuilderAgentExtensionsTests /// Verifies that providing a null builder to AddAIAgent throws an ArgumentNullException. /// [Fact] - public void AddAIAgent_NullBuilder_ThrowsArgumentNullException() - { + public void AddAIAgent_NullBuilder_ThrowsArgumentNullException() => // Act & Assert Assert.Throws( () => HostApplicationBuilderAgentExtensions.AddAIAgent(null!, "agent", "instructions")); - } /// /// Verifies that AddAIAgent with valid parameters returns the same builder instance. @@ -106,15 +104,13 @@ public class HostApplicationBuilderAgentExtensionsTests /// Verifies that AddAIAgent with factory delegate throws ArgumentNullException for null builder. /// [Fact] - public void AddAIAgentWithFactory_NullBuilder_ThrowsArgumentNullException() - { + public void AddAIAgentWithFactory_NullBuilder_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => HostApplicationBuilderAgentExtensions.AddAIAgent( null!, "agentName", (sp, key) => new Mock().Object)); - } /// /// Verifies that AddAIAgent with factory delegate throws ArgumentNullException for null name. @@ -179,7 +175,7 @@ public class HostApplicationBuilderAgentExtensionsTests // Assert var descriptor = builder.Services.FirstOrDefault( - d => d.ServiceKey as string == AgentName && + d => (d.ServiceKey as string) == AgentName && d.ServiceType == typeof(AIAgent)); Assert.NotNull(descriptor); @@ -275,7 +271,7 @@ public class HostApplicationBuilderAgentExtensionsTests Assert.Same(builder, result); // The agent should be registered (proving the method chain worked) var descriptor = builder.Services.FirstOrDefault( - d => d.ServiceKey as string == "agentName" && + d => d.ServiceKey is "agentName" && d.ServiceType == typeof(AIAgent)); Assert.NotNull(descriptor); } @@ -302,7 +298,7 @@ public class HostApplicationBuilderAgentExtensionsTests // Assert Assert.Same(builder, result); var descriptor = builder.Services.FirstOrDefault( - d => d.ServiceKey as string == name && + d => (d.ServiceKey as string) == name && d.ServiceType == typeof(AIAgent)); Assert.NotNull(descriptor); } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/ActorTypeTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/ActorTypeTests.cs index 7dd328c1ad..ecc6c613c8 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/ActorTypeTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/ActorTypeTests.cs @@ -10,69 +10,69 @@ public class ActorTypeTests /// /// Provides valid ActorType names that conform to the regex pattern ^[a-zA-Z_][a-zA-Z._:\-0-9]*$. /// - public static IEnumerable ValidActorTypeNames => new List - { - new object[] { "a" }, // Single letter - new object[] { "A" }, // Single uppercase letter - new object[] { "_" }, // Single underscore - new object[] { "agent" }, // Simple name - new object[] { "Agent" }, // Capitalized name - new object[] { "AGENT" }, // All caps name - new object[] { "my_agent" }, // With underscore - new object[] { "MyAgent" }, // Camel case - new object[] { "agent1" }, // With number - new object[] { "agent_1" }, // With underscore and number - new object[] { "agent:type" }, // With colon - new object[] { "agent-type" }, // With hyphen - new object[] { "my_agent:type-1" }, // Complex valid name - new object[] { "A1_test:complex-name" }, // Very complex valid name - new object[] { "_private_agent" }, // Starting with underscore - new object[] { "agent_with_many_underscores" }, // Multiple underscores - new object[] { "agent:with:colons" }, // Multiple colons - new object[] { "agent-with-hyphens" }, // Multiple hyphens - new object[] { "agent123456789" }, // With many numbers - new object[] { "agent.type" }, // With dot - new object[] { "agent.sub.type" }, // With multiple dots - new object[] { "my.agent_1:type-name" }, // Complex with dots - }; + public static IEnumerable ValidActorTypeNames { get; } = + [ + ["a"], // Single letter + ["A"], // Single uppercase letter + ["_"], // Single underscore + ["agent"], // Simple name + ["Agent"], // Capitalized name + ["AGENT"], // All caps name + ["my_agent"], // With underscore + ["MyAgent"], // Camel case + ["agent1"], // With number + ["agent_1"], // With underscore and number + ["agent:type"], // With colon + ["agent-type"], // With hyphen + ["my_agent:type-1"], // Complex valid name + ["A1_test:complex-name"], // Very complex valid name + ["_private_agent"], // Starting with underscore + ["agent_with_many_underscores"], // Multiple underscores + ["agent:with:colons"], // Multiple colons + ["agent-with-hyphens"], // Multiple hyphens + ["agent123456789"], // With many numbers + ["agent.type"], // With dot + ["agent.sub.type"], // With multiple dots + ["my.agent_1:type-name"], // Complex with dots + ]; /// /// Provides invalid ActorType names that violate the regex pattern ^[a-zA-Z_][a-zA-Z._:\-0-9]*$. /// - public static IEnumerable InvalidActorTypeNames => new List - { - new object[] { "1agent" }, // Starting with number - new object[] { "9test" }, // Starting with number - new object[] { "-agent" }, // Starting with hyphen - new object[] { ":agent" }, // Starting with colon - new object[] { " agent" }, // Starting with space - new object[] { "agent " }, // Trailing space - new object[] { "agent agent" }, // Space in middle - new object[] { "agent@type" }, // Invalid character @ - new object[] { "agent#type" }, // Invalid character # - new object[] { "agent$type" }, // Invalid character $ - new object[] { "agent%type" }, // Invalid character % - new object[] { "agent^type" }, // Invalid character ^ - new object[] { "agent&type" }, // Invalid character & - new object[] { "agent*type" }, // Invalid character * - new object[] { "agent(type)" }, // Invalid characters ( ) - new object[] { "agent[type]" }, // Invalid characters [ ] - new object[] { "agent{type}" }, // Invalid characters { } - new object[] { "agent+type" }, // Invalid character + - new object[] { "agent=type" }, // Invalid character = - new object[] { "agent\\type" }, // Invalid character \ - new object[] { "agent/type" }, // Invalid character / - new object[] { "agent?type" }, // Invalid character ? - new object[] { "agent,type" }, // Invalid character , - new object[] { "agent;type" }, // Invalid character ; - new object[] { "agent\"type" }, // Invalid character " - new object[] { "agent'type" }, // Invalid character ' - new object[] { "agent`type" }, // Invalid character ` - new object[] { "agent~type" }, // Invalid character ~ - new object[] { "agent!type" }, // Invalid character ! - new object[] { "agent\ttype" }, // Tab character - new object[] { "agent\ntype" }, // Newline character - }; + public static IEnumerable InvalidActorTypeNames { get; } = + [ + ["1agent"], // Starting with number + ["9test"], // Starting with number + ["-agent"], // Starting with hyphen + [":agent"], // Starting with colon + [" agent"], // Starting with space + ["agent "], // Trailing space + ["agent agent"], // Space in middle + ["agent@type"], // Invalid character @ + ["agent#type"], // Invalid character # + ["agent$type"], // Invalid character $ + ["agent%type"], // Invalid character % + ["agent^type"], // Invalid character ^ + ["agent&type"], // Invalid character & + ["agent*type"], // Invalid character * + ["agent(type)"], // Invalid characters ( ) + ["agent[type]"], // Invalid characters [ ] + ["agent{type}"], // Invalid characters { } + ["agent+type"], // Invalid character + + ["agent=type"], // Invalid character = + ["agent\\type"], // Invalid character \ + ["agent/type"], // Invalid character / + ["agent?type"], // Invalid character ? + ["agent,type"], // Invalid character , + ["agent;type"], // Invalid character ; + ["agent\"type"], // Invalid character " + ["agent'type"], // Invalid character ' + ["agent`type"], // Invalid character ` + ["agent~type"], // Invalid character ~ + ["agent!type"], // Invalid character ! + ["agent\ttype"], // Tab character + ["agent\ntype"], // Newline character + ]; /// /// Verifies that providing valid actor type name to constructor sets the Name property correctly. @@ -107,21 +107,17 @@ public class ActorTypeTests /// Verifies that providing a null type name to constructor throws an . /// [Fact] - public void Constructor_NullTypeName_ThrowsArgumentNullException() - { + public void Constructor_NullTypeName_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => new ActorType(null!)); - } /// /// Verifies that providing an empty type name to constructor throws an . /// [Fact] - public void Constructor_EmptyTypeName_ThrowsArgumentException() - { + public void Constructor_EmptyTypeName_ThrowsArgumentException() => // Act & Assert Assert.Throws(() => new ActorType("")); - } /// /// Verifies specific edge cases for valid type names. @@ -258,40 +254,32 @@ public class ActorTypeTests /// [Theory] [MemberData(nameof(ValidActorTypeNames))] - public void IsValidType_ValidTypeName_ReturnsTrue(string typeName) - { + public void IsValidType_ValidTypeName_ReturnsTrue(string typeName) => // Act & Assert Assert.True(ActorType.IsValidType(typeName)); - } /// /// Verifies that IsValidType static method works correctly for invalid names. /// [Theory] [MemberData(nameof(InvalidActorTypeNames))] - public void IsValidType_InvalidTypeName_ReturnsFalse(string typeName) - { + public void IsValidType_InvalidTypeName_ReturnsFalse(string typeName) => // Act & Assert Assert.False(ActorType.IsValidType(typeName)); - } /// /// Verifies that IsValidType throws for null. /// [Fact] - public void IsValidType_NullTypeName_ThrowsArgumentNullException() - { + public void IsValidType_NullTypeName_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => ActorType.IsValidType(null!)); - } /// /// Verifies that IsValidType throws for empty string. /// [Fact] - public void IsValidType_EmptyTypeName_ThrowsArgumentException() - { + public void IsValidType_EmptyTypeName_ThrowsArgumentException() => // Act & Assert Assert.Throws(() => ActorType.IsValidType("")); - } } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/InMemoryActorStateStorageTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/InMemoryActorStateStorageTests.cs index 923c1b71b0..83302c7038 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/InMemoryActorStateStorageTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/InMemoryActorStateStorageTests.cs @@ -19,14 +19,14 @@ public sealed class InMemoryActorStateStorageTests private readonly ActorId _anotherActorId = new("AnotherActor", "another-instance"); [Fact] - public async Task WriteStateAsync_WithSetValueOperation_ShouldStoreValue() + public async Task WriteStateAsync_WithSetValueOperation_ShouldStoreValueAsync() { // Arrange - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // Act @@ -39,23 +39,23 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task WriteStateAsync_WithRemoveKeyOperation_ShouldRemoveValue() + public async Task WriteStateAsync_WithRemoveKeyOperation_ShouldRemoveValueAsync() { // Arrange - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); // First set a value var setOperations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; var setResult = await this._storage.WriteStateAsync(this._testActorId, setOperations, "0", CancellationToken.None); // Now remove the value var removeOperations = new List { - new RemoveKeyOperation(key) + new RemoveKeyOperation(Key) }; // Act @@ -68,14 +68,14 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task WriteStateAsync_WithIncorrectETag_ShouldReturnFailure() + public async Task WriteStateAsync_WithIncorrectETag_ShouldReturnFailureAsync() { // Arrange - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; // Act @@ -88,20 +88,20 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ReadStateAsync_WithGetValueOperation_ShouldReturnValue() + public async Task ReadStateAsync_WithGetValueOperation_ShouldReturnValueAsync() { // Arrange - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var writeOperations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; await this._storage.WriteStateAsync(this._testActorId, writeOperations, "0", CancellationToken.None); var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; // Act @@ -116,7 +116,7 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ReadStateAsync_WithGetValueOperationForNonExistentKey_ShouldReturnNull() + public async Task ReadStateAsync_WithGetValueOperationForNonExistentKey_ShouldReturnNullAsync() { // Arrange var readOperations = new List @@ -135,18 +135,18 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ReadStateAsync_WithListKeysOperation_ShouldReturnAllKeys() + public async Task ReadStateAsync_WithListKeysOperation_ShouldReturnAllKeysAsync() { // Arrange - var key1 = "key1"; - var key2 = "key2"; + const string Key1 = "key1"; + const string Key2 = "key2"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var writeOperations = new List { - new SetValueOperation(key1, value1), - new SetValueOperation(key2, value2) + new SetValueOperation(Key1, value1), + new SetValueOperation(Key2, value2) }; await this._storage.WriteStateAsync(this._testActorId, writeOperations, "0", CancellationToken.None); @@ -163,13 +163,13 @@ public sealed class InMemoryActorStateStorageTests var listKeys = result.Results[0] as ListKeysResult; Assert.NotNull(listKeys); Assert.Equal(2, listKeys.Keys.Count); - Assert.Contains(key1, listKeys.Keys); - Assert.Contains(key2, listKeys.Keys); + Assert.Contains(Key1, listKeys.Keys); + Assert.Contains(Key2, listKeys.Keys); Assert.Null(listKeys.ContinuationToken); } [Fact] - public async Task ReadStateAsync_WithListKeysOperationForEmptyActor_ShouldReturnEmptyList() + public async Task ReadStateAsync_WithListKeysOperationForEmptyActor_ShouldReturnEmptyListAsync() { // Arrange var readOperations = new List @@ -189,21 +189,21 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ReadStateAsync_WithListKeysOperationAndKeyPrefix_ShouldReturnFilteredKeys() + public async Task ReadStateAsync_WithListKeysOperationAndKeyPrefix_ShouldReturnFilteredKeysAsync() { // Arrange - var prefixKey1 = "prefix_key1"; - var prefixKey2 = "prefix_key2"; - var otherKey = "other_key"; + const string PrefixKey1 = "prefix_key1"; + const string PrefixKey2 = "prefix_key2"; + const string OtherKey = "other_key"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var value3 = JsonSerializer.SerializeToElement("value3"); var writeOperations = new List { - new SetValueOperation(prefixKey1, value1), - new SetValueOperation(prefixKey2, value2), - new SetValueOperation(otherKey, value3) + new SetValueOperation(PrefixKey1, value1), + new SetValueOperation(PrefixKey2, value2), + new SetValueOperation(OtherKey, value3) }; await this._storage.WriteStateAsync(this._testActorId, writeOperations, "0", CancellationToken.None); @@ -220,25 +220,25 @@ public sealed class InMemoryActorStateStorageTests var listKeys = result.Results[0] as ListKeysResult; Assert.NotNull(listKeys); Assert.Equal(2, listKeys.Keys.Count); - Assert.Contains(prefixKey1, listKeys.Keys); - Assert.Contains(prefixKey2, listKeys.Keys); - Assert.DoesNotContain(otherKey, listKeys.Keys); + Assert.Contains(PrefixKey1, listKeys.Keys); + Assert.Contains(PrefixKey2, listKeys.Keys); + Assert.DoesNotContain(OtherKey, listKeys.Keys); Assert.Null(listKeys.ContinuationToken); } [Fact] - public async Task ReadStateAsync_WithListKeysOperationAndNonMatchingKeyPrefix_ShouldReturnEmptyList() + public async Task ReadStateAsync_WithListKeysOperationAndNonMatchingKeyPrefix_ShouldReturnEmptyListAsync() { // Arrange - var key1 = "key1"; - var key2 = "key2"; + const string Key1 = "key1"; + const string Key2 = "key2"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var writeOperations = new List { - new SetValueOperation(key1, value1), - new SetValueOperation(key2, value2) + new SetValueOperation(Key1, value1), + new SetValueOperation(Key2, value2) }; await this._storage.WriteStateAsync(this._testActorId, writeOperations, "0", CancellationToken.None); @@ -259,19 +259,19 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task MultipleOperations_ShouldBeProcessedInOrder() + public async Task MultipleOperations_ShouldBeProcessedInOrderAsync() { // Arrange - var key1 = "key1"; - var key2 = "key2"; + const string Key1 = "key1"; + const string Key2 = "key2"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var operations = new List { - new SetValueOperation(key1, value1), - new SetValueOperation(key2, value2), - new RemoveKeyOperation(key1) + new SetValueOperation(Key1, value1), + new SetValueOperation(Key2, value2), + new RemoveKeyOperation(Key1) }; // Act @@ -284,7 +284,7 @@ public sealed class InMemoryActorStateStorageTests // Verify remaining key var readOperations = new List { - new GetValueOperation(key2) + new GetValueOperation(Key2) }; var readResult = await this._storage.ReadStateAsync(this._testActorId, readOperations, CancellationToken.None); var getValue = readResult.Results[0] as GetValueResult; @@ -293,20 +293,20 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task DifferentActors_ShouldHaveIsolatedState() + public async Task DifferentActors_ShouldHaveIsolatedStateAsync() { // Arrange - var key = "sharedKey"; + const string Key = "sharedKey"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); var operations1 = new List { - new SetValueOperation(key, value1) + new SetValueOperation(Key, value1) }; var operations2 = new List { - new SetValueOperation(key, value2) + new SetValueOperation(Key, value2) }; // Act @@ -321,7 +321,7 @@ public sealed class InMemoryActorStateStorageTests // Verify values are different var readOperations = new List { - new GetValueOperation(key) + new GetValueOperation(Key) }; var result1 = await this._storage.ReadStateAsync(this._testActorId, readOperations, CancellationToken.None); @@ -337,14 +337,14 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ConcurrentOperations_ShouldBeThreadSafe() + public async Task ConcurrentOperations_ShouldBeThreadSafeAsync() { // Arrange - const int operationCount = 100; + const int OperationCount = 100; var tasks = new List(); // Act - for (int i = 0; i < operationCount; i++) + for (int i = 0; i < OperationCount; i++) { var key = $"key{i}"; var value = JsonSerializer.SerializeToElement($"value{i}"); @@ -359,9 +359,9 @@ public sealed class InMemoryActorStateStorageTests // Retry logic to handle concurrent updates var success = false; var retryCount = 0; - const int maxRetries = 10; + const int MaxRetries = 10; - while (!success && retryCount < maxRetries) + while (!success && retryCount < MaxRetries) { var currentETag = this._storage.GetETag(actorId); var result = await this._storage.WriteStateAsync(actorId, operations, currentETag, CancellationToken.None); @@ -385,14 +385,14 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task Clear_ShouldRemoveAllState() + public async Task Clear_ShouldRemoveAllStateAsync() { // Arrange - var key = "testKey"; + const string Key = "testKey"; var value = JsonSerializer.SerializeToElement("testValue"); var operations = new List { - new SetValueOperation(key, value) + new SetValueOperation(Key, value) }; await this._storage.WriteStateAsync(this._testActorId, operations, "0", CancellationToken.None); @@ -418,15 +418,13 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task WriteStateAsync_WithNullOperations_ShouldThrowArgumentNullException() - { + public async Task WriteStateAsync_WithNullOperations_ShouldThrowArgumentNullExceptionAsync() => // Act & Assert await Assert.ThrowsAsync(() => this._storage.WriteStateAsync(this._testActorId, null!, "0", CancellationToken.None).AsTask()); - } [Fact] - public async Task WriteStateAsync_WithNullETag_ShouldThrowArgumentNullException() + public async Task WriteStateAsync_WithNullETag_ShouldThrowArgumentNullExceptionAsync() { // Arrange var operations = new List(); @@ -437,15 +435,13 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ReadStateAsync_WithNullOperations_ShouldThrowArgumentNullException() - { + public async Task ReadStateAsync_WithNullOperations_ShouldThrowArgumentNullExceptionAsync() => // Act & Assert await Assert.ThrowsAsync(() => this._storage.ReadStateAsync(this._testActorId, null!, CancellationToken.None).AsTask()); - } [Fact] - public async Task WriteStateAsync_WithCancelledToken_ShouldThrowOperationCanceledException() + public async Task WriteStateAsync_WithCancelledToken_ShouldThrowOperationCanceledExceptionAsync() { // Arrange var operations = new List(); @@ -457,7 +453,7 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ReadStateAsync_WithCancelledToken_ShouldThrowOperationCanceledException() + public async Task ReadStateAsync_WithCancelledToken_ShouldThrowOperationCanceledExceptionAsync() { // Arrange var operations = new List(); @@ -469,18 +465,18 @@ public sealed class InMemoryActorStateStorageTests } [Fact] - public async Task ETagProgression_ShouldIncrementMonotonically() + public async Task ETagProgression_ShouldIncrementMonotonicallyAsync() { // Arrange - var key = "testKey"; + const string Key = "testKey"; var value1 = JsonSerializer.SerializeToElement("value1"); var value2 = JsonSerializer.SerializeToElement("value2"); // Act - var operations1 = new List { new SetValueOperation(key, value1) }; + var operations1 = new List { new SetValueOperation(Key, value1) }; var result1 = await this._storage.WriteStateAsync(this._testActorId, operations1, "0", CancellationToken.None); - var operations2 = new List { new SetValueOperation(key, value2) }; + var operations2 = new List { new SetValueOperation(Key, value2) }; var result2 = await this._storage.WriteStateAsync(this._testActorId, operations2, result1.ETag, CancellationToken.None); // Assert diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentExtensionsTests.cs index 5292277f02..c51d5f2af9 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentExtensionsTests.cs @@ -37,7 +37,6 @@ public class AgentExtensionsTests // Assert Assert.NotNull(result); - Assert.True(result is AIFunction); Assert.Equal("TestAgent", result.Name); Assert.Equal("Test agent description", result.Description); } @@ -301,7 +300,7 @@ public class AgentExtensionsTests public override string? Name { get; } public override string? Description { get; } - public List ReceivedMessages { get; } = new(); + public List ReceivedMessages { get; } = []; public CancellationToken LastCancellationToken { get; private set; } public int RunAsyncCallCount { get; private set; } @@ -315,7 +314,7 @@ public class AgentExtensionsTests this.LastCancellationToken = cancellationToken; this.ReceivedMessages.AddRange(messages); - if (this._exceptionToThrow != null) + if (this._exceptionToThrow is not null) { throw this._exceptionToThrow; } diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs index 420567acdc..589e7055e9 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs @@ -441,7 +441,7 @@ public class ChatClientAgentTests { capturedMessages.AddRange(msgs); capturedInstructions = opts.Instructions ?? string.Empty; - if (opts.Tools != null) + if (opts.Tools is not null) { capturedTools.AddRange(opts.Tools); } @@ -536,7 +536,7 @@ public class ChatClientAgentTests { capturedMessages.AddRange(msgs); capturedInstructions = opts.Instructions ?? string.Empty; - if (opts.Tools != null) + if (opts.Tools is not null) { capturedTools.AddRange(opts.Tools); } @@ -804,7 +804,7 @@ public class ChatClientAgentTests { // Arrange var chatClient = new Mock().Object; - ChatClientAgent agent = new(chatClient, options: null!); + ChatClientAgent agent = new(chatClient, options: null); // Act & Assert Assert.NotNull(agent.Id); @@ -1324,7 +1324,7 @@ public class ChatClientAgentTests // Assert Assert.NotNull(result); - Assert.IsAssignableFrom(result); + Assert.IsType(result, exactMatch: false); // Note: The result will be the AgentInvokedChatClient wrapper, not the original mock Assert.Equal("AgentInvokedChatClient", result.GetType().Name); @@ -1388,8 +1388,8 @@ public class ChatClientAgentTests // Arrange var mockChatClient = new Mock(); var customService = new object(); - var serviceKey = "test-key"; - mockChatClient.Setup(c => c.GetService(typeof(string), serviceKey)) + const string ServiceKey = "test-key"; + mockChatClient.Setup(c => c.GetService(typeof(string), ServiceKey)) .Returns(customService); var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions @@ -1398,11 +1398,11 @@ public class ChatClientAgentTests }); // Act - var result = agent.GetService(typeof(string), serviceKey); + var result = agent.GetService(typeof(string), ServiceKey); // Assert Assert.Same(customService, result); - mockChatClient.Verify(c => c.GetService(typeof(string), serviceKey), Times.Once); + mockChatClient.Verify(c => c.GetService(typeof(string), ServiceKey), Times.Once); } /// @@ -1417,7 +1417,7 @@ public class ChatClientAgentTests { // Arrange var mockChatClient = new Mock(); - var chatClientMetadata = providerName != null ? new ChatClientMetadata(providerName) : null; + var chatClientMetadata = providerName is not null ? new ChatClientMetadata(providerName) : null; mockChatClient.Setup(c => c.GetService(typeof(ChatClientMetadata), null)) .Returns(chatClientMetadata); @@ -1448,7 +1448,7 @@ public class ChatClientAgentTests { // Arrange var mockChatClient = new Mock(); - var chatClientMetadata = chatClientProviderName != null ? new ChatClientMetadata(chatClientProviderName) : null; + var chatClientMetadata = chatClientProviderName is not null ? new ChatClientMetadata(chatClientProviderName) : null; mockChatClient.Setup(c => c.GetService(typeof(ChatClientMetadata), null)) .Returns(chatClientMetadata); @@ -1615,7 +1615,7 @@ public class ChatClientAgentTests // Assert Assert.NotNull(result); - Assert.IsAssignableFrom(result); + Assert.IsType(result, exactMatch: false); // Verify that the ChatClient's GetService was NOT called because IChatClient is handled by the agent itself mockChatClient.Verify(c => c.GetService(typeof(IChatClient), "some-key"), Times.Never); diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/CopilotStudioAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/CopilotStudioAgentTests.cs index 9a8e056af6..e919e719a3 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/CopilotStudioAgentTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/CopilotStudioAgentTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.Extensions.AI.Agents.UnitTests; /// public class CopilotStudioAgentTests { - private CopilotClient CreateTestCopilotClient() + private static CopilotClient CreateTestCopilotClient() { // Create mock dependencies for CopilotClient var mockSettings = new Mock(); @@ -33,7 +33,7 @@ public class CopilotStudioAgentTests public void GetService_RequestingCopilotClient_ReturnsCopilotClient() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act @@ -51,7 +51,7 @@ public class CopilotStudioAgentTests public void GetService_RequestingAIAgentMetadata_ReturnsMetadata() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act @@ -71,7 +71,7 @@ public class CopilotStudioAgentTests public void GetService_RequestingUnknownServiceType_ReturnsNull() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act @@ -88,7 +88,7 @@ public class CopilotStudioAgentTests public void GetService_WithServiceKey_ReturnsNull() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act @@ -105,7 +105,7 @@ public class CopilotStudioAgentTests public void GetService_RequestingCopilotStudioAgentType_ReturnsBaseImplementation() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act @@ -123,7 +123,7 @@ public class CopilotStudioAgentTests public void GetService_RequestingAIAgentType_ReturnsBaseImplementation() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act @@ -141,7 +141,7 @@ public class CopilotStudioAgentTests public void GetService_RequestingCopilotClientWithServiceKey_CallsBaseFirstThenDerivedLogic() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act - Request CopilotClient with a service key (base.GetService will return null due to serviceKey) @@ -159,7 +159,7 @@ public class CopilotStudioAgentTests public void GetService_RequestingAIAgentMetadata_ReturnsConsistentMetadata() { // Arrange - var client = this.CreateTestCopilotClient(); + var client = CreateTestCopilotClient(); var agent = new CopilotStudioAgent(client, NullLoggerFactory.Instance); // Act diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/OpenTelemetryAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/OpenTelemetryAgentTests.cs index 626832a179..f80457ae1f 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/OpenTelemetryAgentTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/OpenTelemetryAgentTests.cs @@ -447,14 +447,14 @@ public class OpenTelemetryAgentTests var mockLogger = new Mock(); mockLoggerFactory.Setup(f => f.CreateLogger(It.IsAny())) .Returns(mockLogger.Object); - var sourceName = "custom-source"; - var enableSensitiveData = true; + const string SourceName = "custom-source"; + const bool EnableSensitiveData = true; // Act using var telemetryAgent = mockAgent.Object.WithOpenTelemetry( loggerFactory: mockLoggerFactory.Object, - sourceName: sourceName, - enableSensitiveData: enableSensitiveData); + sourceName: SourceName, + enableSensitiveData: EnableSensitiveData); // Assert Assert.IsType(telemetryAgent); @@ -553,10 +553,10 @@ public class OpenTelemetryAgentTests mockAgent.Setup(a => a.Id).Returns("test-id"); mockAgent.Setup(a => a.Name).Returns("TestAgent"); var mockLogger = new Mock(); - var sourceName = "test-source"; + const string SourceName = "test-source"; // Act - using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, mockLogger.Object, sourceName); + using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, mockLogger.Object, SourceName); // Assert Assert.Equal("test-id", telemetryAgent.Id); @@ -573,10 +573,10 @@ public class OpenTelemetryAgentTests var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-id"); mockAgent.Setup(a => a.Name).Returns("TestAgent"); - var sourceName = "test-source"; + const string SourceName = "test-source"; // Act - using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, logger: null, sourceName); + using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, logger: null, SourceName); // Assert Assert.Equal("test-id", telemetryAgent.Id); @@ -633,10 +633,10 @@ public class OpenTelemetryAgentTests var mockLogger = new Mock(); mockLoggerFactory.Setup(f => f.CreateLogger(It.IsAny())) .Returns(mockLogger.Object); - var sourceName = "test-source"; + const string SourceName = "test-source"; // Act - using var telemetryAgent = mockAgent.Object.WithOpenTelemetry(mockLoggerFactory.Object, sourceName); + using var telemetryAgent = mockAgent.Object.WithOpenTelemetry(mockLoggerFactory.Object, SourceName); // Assert Assert.IsType(telemetryAgent); @@ -835,8 +835,8 @@ public class OpenTelemetryAgentTests // Arrange var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-id"); - var sourceName = "test-source"; - using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, sourceName: sourceName); + const string SourceName = "test-source"; + using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, sourceName: SourceName); // Act var result = telemetryAgent.GetService(typeof(ActivitySource)); @@ -845,7 +845,7 @@ public class OpenTelemetryAgentTests Assert.NotNull(result); Assert.IsType(result); var activitySource = (ActivitySource)result; - Assert.Equal(sourceName, activitySource.Name); + Assert.Equal(SourceName, activitySource.Name); } /// @@ -900,18 +900,18 @@ public class OpenTelemetryAgentTests // Arrange var mockAgent = new Mock(); var customService = new object(); - var serviceKey = "test-key"; - mockAgent.Setup(a => a.GetService(typeof(string), serviceKey)) + const string ServiceKey = "test-key"; + mockAgent.Setup(a => a.GetService(typeof(string), ServiceKey)) .Returns(customService); using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object); // Act - var result = telemetryAgent.GetService(typeof(string), serviceKey); + var result = telemetryAgent.GetService(typeof(string), ServiceKey); // Assert Assert.Same(customService, result); - mockAgent.Verify(a => a.GetService(typeof(string), serviceKey), Times.Once); + mockAgent.Verify(a => a.GetService(typeof(string), ServiceKey), Times.Once); } /// @@ -926,8 +926,8 @@ public class OpenTelemetryAgentTests mockAgent.Setup(a => a.GetService(typeof(ActivitySource), null)) .Returns(innerActivitySource); - var sourceName = "telemetry-source"; - using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, sourceName: sourceName); + const string SourceName = "telemetry-source"; + using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, sourceName: SourceName); // Act var result = telemetryAgent.GetService(typeof(ActivitySource)); @@ -936,7 +936,7 @@ public class OpenTelemetryAgentTests Assert.NotNull(result); Assert.IsType(result); var activitySource = (ActivitySource)result; - Assert.Equal(sourceName, activitySource.Name); + Assert.Equal(SourceName, activitySource.Name); Assert.NotSame(innerActivitySource, result); // Should return OpenTelemetryAgent's ActivitySource, not inner agent's // Cleanup @@ -1017,8 +1017,8 @@ public class OpenTelemetryAgentTests // Arrange var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-id"); - var sourceName = "test-source"; - using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, sourceName: sourceName); + const string SourceName = "test-source"; + using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, sourceName: SourceName); // Act - Request ActivitySource with a service key (base.GetService will return null due to serviceKey) var result = telemetryAgent.GetService(typeof(ActivitySource), "some-key"); @@ -1027,7 +1027,7 @@ public class OpenTelemetryAgentTests Assert.NotNull(result); Assert.IsType(result); var activitySource = (ActivitySource)result; - Assert.Equal(sourceName, activitySource.Name); + Assert.Equal(SourceName, activitySource.Name); // Verify that the inner agent's GetService was NOT called because ActivitySource is handled by the telemetry agent itself mockAgent.Verify(a => a.GetService(typeof(ActivitySource), "some-key"), Times.Never); } @@ -1200,9 +1200,9 @@ public class OpenTelemetryAgentTests .AddInMemoryExporter(activities) .Build(); - var customProviderName = "custom-ai-provider"; + const string CustomProviderName = "custom-ai-provider"; var mockAgent = new Mock(); - var customMetadata = new AIAgentMetadata(customProviderName); + var customMetadata = new AIAgentMetadata(CustomProviderName); // Setup mock agent to return custom metadata mockAgent.Setup(a => a.GetService(typeof(AIAgentMetadata), null)) @@ -1224,12 +1224,12 @@ public class OpenTelemetryAgentTests var activity = Assert.Single(activities); // Verify that the custom provider name appears in telemetry - Assert.Equal(customProviderName, activity.GetTagItem(OpenTelemetryConsts.GenAI.SystemName)); + Assert.Equal(CustomProviderName, activity.GetTagItem(OpenTelemetryConsts.GenAI.SystemName)); // Verify that GetService returns the same custom provider name var agentMetadata = telemetryAgent.GetService(typeof(AIAgentMetadata)) as AIAgentMetadata; Assert.NotNull(agentMetadata); - Assert.Equal(customProviderName, agentMetadata.ProviderName); + Assert.Equal(CustomProviderName, agentMetadata.ProviderName); } /// @@ -1351,9 +1351,9 @@ public class OpenTelemetryAgentTests .AddInMemoryExporter(activities) .Build(); - var providerName = "consistent-provider"; + const string ProviderName = "consistent-provider"; var mockChatClient = new Mock(); - var chatClientMetadata = new ChatClientMetadata(providerName); + var chatClientMetadata = new ChatClientMetadata(ProviderName); mockChatClient.Setup(c => c.GetService(typeof(ChatClientMetadata), null)) .Returns(chatClientMetadata); mockChatClient.Setup(c => c.GetResponseAsync(It.IsAny>(), It.IsAny(), It.IsAny())) @@ -1382,7 +1382,7 @@ public class OpenTelemetryAgentTests // Verify that all activities have the same provider name foreach (var activity in activities) { - Assert.Equal(providerName, activity.GetTagItem(OpenTelemetryConsts.GenAI.SystemName)); + Assert.Equal(ProviderName, activity.GetTagItem(OpenTelemetryConsts.GenAI.SystemName)); } // Verify that GetService consistently returns the same provider name @@ -1390,8 +1390,8 @@ public class OpenTelemetryAgentTests var agentMetadata2 = telemetryAgent.GetService(typeof(AIAgentMetadata)) as AIAgentMetadata; Assert.NotNull(agentMetadata1); Assert.NotNull(agentMetadata2); - Assert.Equal(providerName, agentMetadata1.ProviderName); - Assert.Equal(providerName, agentMetadata2.ProviderName); + Assert.Equal(ProviderName, agentMetadata1.ProviderName); + Assert.Equal(ProviderName, agentMetadata2.ProviderName); Assert.Same(agentMetadata1, agentMetadata2); // Should be cached } @@ -1457,26 +1457,26 @@ public class OpenTelemetryAgentTests if (throwError) { mockAgent.Setup(a => a.RunStreamingAsync(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(ThrowingAsyncEnumerable()); + .Returns(ThrowingAsyncEnumerableAsync()); } else { mockAgent.Setup(a => a.RunStreamingAsync(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(CreateStreamingResponse()); + .Returns(CreateStreamingResponseAsync()); } return mockAgent; - static async IAsyncEnumerable ThrowingAsyncEnumerable([EnumeratorCancellation] CancellationToken cancellationToken = default) + static async IAsyncEnumerable ThrowingAsyncEnumerableAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { await Task.Yield(); throw new InvalidOperationException("Streaming error"); #pragma warning disable CS0162 // Unreachable code detected yield break; -#pragma warning restore CS0162 // Unreachable code detected +#pragma warning restore CS0162 } - static async IAsyncEnumerable CreateStreamingResponse([EnumeratorCancellation] CancellationToken cancellationToken = default) + static async IAsyncEnumerable CreateStreamingResponseAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { await Task.Yield(); @@ -1503,11 +1503,9 @@ public class OpenTelemetryAgentTests } [Fact] - public void Constructor_NullAgent_ThrowsArgumentNullException() - { + public void Constructor_NullAgent_ThrowsArgumentNullException() => // Act & Assert Assert.Throws(() => new OpenTelemetryAgent(null!)); - } [Fact] public void Constructor_WithParameters_SetsProperties() @@ -1520,10 +1518,10 @@ public class OpenTelemetryAgentTests var mockLogger = new Mock(); var logger = new Mock().Object; - var sourceName = "custom-source"; + const string SourceName = "custom-source"; // Act - using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, mockLogger.Object, sourceName); + using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, mockLogger.Object, SourceName); // Assert Assert.Equal("test-id", telemetryAgent.Id); @@ -1652,7 +1650,7 @@ public class OpenTelemetryAgentTests mockAgent.Setup(a => a.Name).Returns("TestAgent"); mockAgent.Setup(a => a.RunStreamingAsync(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(CreatePartialStreamingResponse()); + .Returns(CreatePartialStreamingResponseAsync()); using var telemetryAgent = new OpenTelemetryAgent(mockAgent.Object, sourceName: sourceName); @@ -1674,7 +1672,7 @@ public class OpenTelemetryAgentTests var activity = Assert.Single(activities); Assert.Equal("partial-response-id", activity.GetTagItem(OpenTelemetryConsts.GenAI.Response.Id)); - static async IAsyncEnumerable CreatePartialStreamingResponse([EnumeratorCancellation] CancellationToken cancellationToken = default) + static async IAsyncEnumerable CreatePartialStreamingResponseAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { await Task.Yield(); @@ -2051,10 +2049,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2104,10 +2099,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2157,10 +2149,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2223,10 +2212,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2314,10 +2300,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2382,10 +2365,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2466,10 +2446,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2520,10 +2497,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2587,10 +2561,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); @@ -2698,10 +2669,7 @@ public class OpenTelemetryAgentTests It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback((level, eventId, state, ex, formatter) => - { - loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? "")); - }); + .Callback((level, eventId, state, ex, formatter) => loggedEvents.Add((level, eventId, formatter.DynamicInvoke(state, ex)?.ToString() ?? ""))); var mockAgent = new Mock(); mockAgent.Setup(a => a.Id).Returns("test-agent"); diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs index b5a44a7e6c..4ed86195ed 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs @@ -66,10 +66,8 @@ public class OpenAIAssistantFixture : IChatClientAgentFixture }); } - public Task DeleteAgentAsync(ChatClientAgent agent) - { - return this._assistantClient!.DeleteAssistantAsync(agent.Id); - } + public Task DeleteAgentAsync(ChatClientAgent agent) => + this._assistantClient!.DeleteAssistantAsync(agent.Id); public Task DeleteThreadAsync(AgentThread thread) { diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs index feaacca6f8..349d1eddac 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs @@ -30,10 +30,8 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture public IChatClient ChatClient => this._agent.ChatClient; - public async Task> GetChatHistoryAsync(AgentThread thread) - { - return thread.MessageStore is null ? [] : (await thread.MessageStore.GetMessagesAsync()).ToList(); - } + public async Task> GetChatHistoryAsync(AgentThread thread) => + thread.MessageStore is null ? [] : (await thread.MessageStore.GetMessagesAsync()).ToList(); public Task CreateChatClientAgentAsync( string name = "HelpfulAssistant", @@ -52,25 +50,17 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture })); } - public Task DeleteAgentAsync(ChatClientAgent agent) - { + public Task DeleteAgentAsync(ChatClientAgent agent) => // Chat Completion does not require/support deleting agents, so this is a no-op. - return Task.CompletedTask; - } + Task.CompletedTask; - public Task DeleteThreadAsync(AgentThread thread) - { + public Task DeleteThreadAsync(AgentThread thread) => // Chat Completion does not require/support deleting threads, so this is a no-op. - return Task.CompletedTask; - } + Task.CompletedTask; - public async Task InitializeAsync() - { + public async Task InitializeAsync() => this._agent = await this.CreateChatClientAgentAsync(); - } - public Task DisposeAsync() - { - return Task.CompletedTask; - } + public Task DisposeAsync() => + Task.CompletedTask; } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs index 885f47a27a..669a4dd2a0 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs @@ -10,10 +10,8 @@ public class OpenAIResponseStoreTrueChatClientAgentRunStreamingTests() : ChatCli private const string SkipReason = "OpenAIResponse does not support empty messages"; [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - { - return Task.CompletedTask; - } + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => + Task.CompletedTask; } public class OpenAIResponseStoreFalseChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(store: false)) @@ -21,8 +19,6 @@ public class OpenAIResponseStoreFalseChatClientAgentRunStreamingTests() : ChatCl private const string SkipReason = "OpenAIResponse does not support empty messages"; [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - { - return Task.CompletedTask; - } + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => + Task.CompletedTask; } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs index 1eabc64612..af2f1c14ec 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs @@ -10,10 +10,8 @@ public class OpenAIResponseStoreTrueChatClientAgentRunTests() : ChatClientAgentR private const string SkipReason = "OpenAIResponse does not support empty messages"; [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - { - return Task.CompletedTask; - } + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => + Task.CompletedTask; } public class OpenAIResponseStoreFalseChatClientAgentRunTests() : ChatClientAgentRunTests(() => new(store: false)) @@ -21,8 +19,6 @@ public class OpenAIResponseStoreFalseChatClientAgentRunTests() : ChatClientAgent private const string SkipReason = "OpenAIResponse does not support empty messages"; [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - { - return Task.CompletedTask; - } + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => + Task.CompletedTask; } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index de094e46a2..71dd862973 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -64,36 +64,30 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture throw new NotSupportedException("This test currently only supports text messages"); } - public Task CreateChatClientAgentAsync( + public async Task CreateChatClientAgentAsync( string name = "HelpfulAssistant", string instructions = "You are a helpful assistant.", - IList? aiTools = null) - { - return Task.FromResult(new ChatClientAgent( - this._openAIResponseClient.AsIChatClient(), - options: new() - { - Name = name, - Instructions = instructions, - ChatOptions = new ChatOptions + IList? aiTools = null) => + new ChatClientAgent( + this._openAIResponseClient.AsIChatClient(), + options: new() { - Tools = aiTools, - RawRepresentationFactory = new Func((_) => new ResponseCreationOptions() { StoredOutputEnabled = store }) - }, - })); - } + Name = name, + Instructions = instructions, + ChatOptions = new ChatOptions + { + Tools = aiTools, + RawRepresentationFactory = new Func((_) => new ResponseCreationOptions() { StoredOutputEnabled = store }) + }, + }); - public Task DeleteAgentAsync(ChatClientAgent agent) - { + public Task DeleteAgentAsync(ChatClientAgent agent) => // Chat Completion does not require/support deleting agents, so this is a no-op. - return Task.CompletedTask; - } + Task.CompletedTask; - public Task DeleteThreadAsync(AgentThread thread) - { + public Task DeleteThreadAsync(AgentThread thread) => // Chat Completion does not require/support deleting threads, so this is a no-op. - return Task.CompletedTask; - } + Task.CompletedTask; public async Task InitializeAsync() { @@ -103,8 +97,5 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture this._agent = await this.CreateChatClientAgentAsync(); } - public Task DisposeAsync() - { - return Task.CompletedTask; - } + public Task DisposeAsync() => Task.CompletedTask; } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs index 7a384a14ee..e2e7e28bbd 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs @@ -9,10 +9,8 @@ public class OpenAIResponseStoreTrueRunStreamingTests() : RunStreamingTests + Task.CompletedTask; } public class OpenAIResponseStoreFalseRunStreamingTests() : RunStreamingTests(() => new(store: false)) @@ -20,8 +18,6 @@ public class OpenAIResponseStoreFalseRunStreamingTests() : RunStreamingTests + Task.CompletedTask; } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs index 1d4f3a4034..41c5254474 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs @@ -9,10 +9,8 @@ public class OpenAIResponseStoreTrueRunTests() : RunTests { private const string SkipReason = "OpenAIResponse does not support empty messages"; [Fact(Skip = SkipReason)] - public override Task RunWithNoMessageDoesNotFailAsync() - { - return Task.CompletedTask; - } + public override Task RunWithNoMessageDoesNotFailAsync() => + Task.CompletedTask; } public class OpenAIResponseStoreFalseRunTests() : RunTests(() => new(store: false)) @@ -20,8 +18,6 @@ public class OpenAIResponseStoreFalseRunTests() : RunTests + Task.CompletedTask; }