diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props index 54a125a13b..2482c43013 100644 --- a/dotnet/Directory.Build.props +++ b/dotnet/Directory.Build.props @@ -3,8 +3,7 @@ true true - AllEnabledByDefault - latest + 10.0-all true latest enable diff --git a/dotnet/samples/.editorconfig b/dotnet/samples/.editorconfig index d260a0e568..6da078d7c5 100644 --- a/dotnet/samples/.editorconfig +++ b/dotnet/samples/.editorconfig @@ -1,6 +1,7 @@ # Suppressing errors for Sample projects under dotnet/samples folder [*.cs] dotnet_diagnostic.CA1716.severity = none # Add summary to documentation comment. +dotnet_diagnostic.CA1873.severity = none # Evaluation of logging arguments may be expensive dotnet_diagnostic.CA2000.severity = none # Call System.IDisposable.Dispose on object before all references to it are out of scope dotnet_diagnostic.CA2007.severity = none # Do not directly await a Task diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/GeminiChatClient.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/GeminiChatClient.cs index 2a1d47a456..28f6f26013 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/GeminiChatClient.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/GeminiChatClient.cs @@ -49,7 +49,7 @@ public async Task GetResponseAsync(IEnumerable messag GenerateContentResponse generateResult = await this._models.GenerateContentAsync(modelId!, contents, config).ConfigureAwait(false); // Create the response. - ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, new List())) + ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, [])) { CreatedAt = generateResult.CreateTime is { } dt ? new DateTimeOffset(dt) : null, ModelId = !string.IsNullOrWhiteSpace(generateResult.ModelVersion) ? generateResult.ModelVersion : modelId, @@ -82,7 +82,7 @@ public async IAsyncEnumerable GetStreamingResponseAsync(IEnu await foreach (GenerateContentResponse generateResult in this._models.GenerateContentStreamAsync(modelId!, contents, config).WithCancellation(cancellationToken).ConfigureAwait(false)) { // Create a response update for each result in the stream. - ChatResponseUpdate responseUpdate = new(ChatRole.Assistant, new List()) + ChatResponseUpdate responseUpdate = new(ChatRole.Assistant, []) { CreatedAt = generateResult.CreateTime is { } dt ? new DateTimeOffset(dt) : null, ModelId = !string.IsNullOrWhiteSpace(generateResult.ModelVersion) ? generateResult.ModelVersion : modelId, @@ -148,7 +148,7 @@ void IDisposable.Dispose() { /* nop */ } // create the request instance, allowing the caller to populate it with GenAI-specific options. Otherwise, create // a new instance directly. string? model = this._defaultModelId; - List contents = new(); + List contents = []; GenerateContentConfig config = options?.RawRepresentationFactory?.Invoke(this) as GenerateContentConfig ?? new(); if (options is not null) @@ -160,7 +160,7 @@ void IDisposable.Dispose() { /* nop */ } if (options.Instructions is { } instructions) { - ((config.SystemInstruction ??= new()).Parts ??= new()).Add(new() { Text = instructions }); + ((config.SystemInstruction ??= new()).Parts ??= []).Add(new() { Text = instructions }); } if (options.MaxOutputTokens is { } maxOutputTokens) @@ -185,7 +185,7 @@ void IDisposable.Dispose() { /* nop */ } if (options.StopSequences is { } stopSequences) { - (config.StopSequences ??= new()).AddRange(stopSequences); + (config.StopSequences ??= []).AddRange(stopSequences); } if (options.Temperature is { } temperature) @@ -213,7 +213,7 @@ void IDisposable.Dispose() { /* nop */ } switch (tool) { case AIFunctionDeclaration af: - functionDeclarations ??= new(); + functionDeclarations ??= []; functionDeclarations.Add(new() { Name = af.Name, @@ -223,15 +223,15 @@ void IDisposable.Dispose() { /* nop */ } break; case HostedCodeInterpreterTool: - (config.Tools ??= new()).Add(new() { CodeExecution = new() }); + (config.Tools ??= []).Add(new() { CodeExecution = new() }); break; case HostedFileSearchTool: - (config.Tools ??= new()).Add(new() { Retrieval = new() }); + (config.Tools ??= []).Add(new() { Retrieval = new() }); break; case HostedWebSearchTool: - (config.Tools ??= new()).Add(new() { GoogleSearch = new() }); + (config.Tools ??= []).Add(new() { GoogleSearch = new() }); break; } } @@ -240,8 +240,8 @@ void IDisposable.Dispose() { /* nop */ } if (functionDeclarations is { Count: > 0 }) { Tool functionTools = new(); - (functionTools.FunctionDeclarations ??= new()).AddRange(functionDeclarations); - (config.Tools ??= new()).Add(functionTools); + (functionTools.FunctionDeclarations ??= []).AddRange(functionDeclarations); + (config.Tools ??= []).Add(functionTools); } // Transfer over the tool mode if there are any tools. @@ -261,7 +261,7 @@ void IDisposable.Dispose() { /* nop */ } config.ToolConfig = new() { FunctionCallingConfig = new() { Mode = FunctionCallingConfigMode.ANY } }; if (required.RequiredFunctionName is not null) { - ((config.ToolConfig.FunctionCallingConfig ??= new()).AllowedFunctionNames ??= new()).Add(required.RequiredFunctionName); + ((config.ToolConfig.FunctionCallingConfig ??= new()).AllowedFunctionNames ??= []).Add(required.RequiredFunctionName); } break; } @@ -287,14 +287,14 @@ void IDisposable.Dispose() { /* nop */ } string instruction = message.Text; if (!string.IsNullOrWhiteSpace(instruction)) { - ((config.SystemInstruction ??= new()).Parts ??= new()).Add(new() { Text = instruction }); + ((config.SystemInstruction ??= new()).Parts ??= []).Add(new() { Text = instruction }); } continue; } Content content = new() { Role = message.Role == ChatRole.Assistant ? "model" : "user" }; - content.Parts ??= new(); + content.Parts ??= []; AddPartsForAIContents(ref callIdToFunctionNames, message.Contents, content.Parts); contents.Add(content); @@ -367,7 +367,7 @@ private static void AddPartsForAIContents(ref Dictionary? callId break; case FunctionCallContent functionCallContent: - (callIdToFunctionNames ??= new())[functionCallContent.CallId] = functionCallContent.Name; + (callIdToFunctionNames ??= [])[functionCallContent.CallId] = functionCallContent.Name; callIdToFunctionNames[""] = functionCallContent.Name; // track last function name in case calls don't have IDs part = new() @@ -480,22 +480,22 @@ private static void AddAIContentsForParts(List parts, IList con { foreach (var citation in citations) { - textContent.Annotations = new List() - { - new CitationAnnotation() - { - Title = citation.Title, - Url = Uri.TryCreate(citation.Uri, UriKind.Absolute, out Uri? uri) ? uri : null, - AnnotatedRegions = new List() - { - new TextSpanAnnotatedRegion() - { - StartIndex = citation.StartIndex, - EndIndex = citation.EndIndex, - } - }, - } - }; + textContent.Annotations = + [ + new CitationAnnotation() + { + Title = citation.Title, + Url = Uri.TryCreate(citation.Uri, UriKind.Absolute, out Uri? uri) ? uri : null, + AnnotatedRegions = + [ + new TextSpanAnnotatedRegion() + { + StartIndex = citation.StartIndex, + EndIndex = citation.EndIndex, + } + ], + } + ]; } } } @@ -551,7 +551,7 @@ void AddIfPresent(string key, int? value) { if (value is int i) { - (details.AdditionalCounts ??= new())[key] = i; + (details.AdditionalCounts ??= [])[key] = i; } } } diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs index 887e780104..bdb1f72928 100644 --- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs @@ -281,7 +281,7 @@ private static AgentMessage CreateA2AMessage(A2AAgentThread typedThread, IEnumer private static A2AContinuationToken? CreateContinuationToken(string taskId, TaskState state) { - if (state == TaskState.Submitted || state == TaskState.Working) + if (state is TaskState.Submitted or TaskState.Working) { return new A2AContinuationToken(taskId); } diff --git a/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs index 3571d97085..37c9c60413 100644 --- a/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs +++ b/dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs @@ -220,7 +220,11 @@ public async IAsyncEnumerable GetStreamingResponseAsync( if (options?.Tools is { Count: > 0 }) { input.Tools = options.Tools.AsAGUITools(); - this._logger.LogDebug("[AGUIChatClient] Tool count: {ToolCount}", options.Tools.Count); + + if (this._logger.IsEnabled(LogLevel.Debug)) + { + this._logger.LogDebug("[AGUIChatClient] Tool count: {ToolCount}", options.Tools.Count); + } } var clientToolSet = new HashSet(); diff --git a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/ActivityProcessor.cs b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/ActivityProcessor.cs index b4dfb59a28..178c8bc904 100644 --- a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/ActivityProcessor.cs +++ b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/ActivityProcessor.cs @@ -27,7 +27,7 @@ public static async IAsyncEnumerable ProcessActivityAsync(IAsyncEnu { yield return CreateChatMessageFromActivity(activity, [new TextContent(activity.Text)]); } - else + else if (logger.IsEnabled(LogLevel.Warning)) { logger.LogWarning("Unknown activity type '{ActivityType}' received.", activity.Type); } diff --git a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatMessageStore.cs b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatMessageStore.cs index fff7f56fa5..03334d90f9 100644 --- a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatMessageStore.cs +++ b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatMessageStore.cs @@ -274,7 +274,7 @@ public static CosmosChatMessageStore CreateFromSerializedState(CosmosClient cosm throw new ArgumentException("Invalid serialized state", nameof(serializedStoreState)); } - var state = JsonSerializer.Deserialize(serializedStoreState, jsonSerializerOptions); + var state = serializedStoreState.Deserialize(jsonSerializerOptions); if (state?.ConversationIdentifier is not { } conversationId) { throw new ArgumentException("Invalid serialized state", nameof(serializedStoreState)); diff --git a/dotnet/src/Microsoft.Agents.AI.DevUI/DevUIMiddleware.cs b/dotnet/src/Microsoft.Agents.AI.DevUI/DevUIMiddleware.cs index a2b210ca4d..2d6fcbd7e4 100644 --- a/dotnet/src/Microsoft.Agents.AI.DevUI/DevUIMiddleware.cs +++ b/dotnet/src/Microsoft.Agents.AI.DevUI/DevUIMiddleware.cs @@ -83,7 +83,11 @@ public async Task HandleRequestAsync(HttpContext context) context.Response.StatusCode = StatusCodes.Status301MovedPermanently; context.Response.Headers.Location = redirectUrl; - this._logger.LogDebug("Redirecting {OriginalPath} to {RedirectUrl}", NewlineRegex().Replace(path, ""), NewlineRegex().Replace(redirectUrl, "")); + if (this._logger.IsEnabled(LogLevel.Debug)) + { + this._logger.LogDebug("Redirecting {OriginalPath} to {RedirectUrl}", NewlineRegex().Replace(path, ""), NewlineRegex().Replace(redirectUrl, "")); + } + return; } @@ -123,7 +127,11 @@ private async Task TryServeResourceAsync(HttpContext context, string resou { if (!this._resourceCache.TryGetValue(resourcePath.Replace('.', '/'), out var cacheEntry)) { - this._logger.LogDebug("Embedded resource not found: {ResourcePath}", resourcePath); + if (this._logger.IsEnabled(LogLevel.Debug)) + { + this._logger.LogDebug("Embedded resource not found: {ResourcePath}", resourcePath); + } + return false; } @@ -133,7 +141,12 @@ private async Task TryServeResourceAsync(HttpContext context, string resou if (context.Request.Headers.IfNoneMatch == cacheEntry.ETag) { response.StatusCode = StatusCodes.Status304NotModified; - this._logger.LogDebug("Resource not modified (304): {ResourcePath}", resourcePath); + + if (this._logger.IsEnabled(LogLevel.Debug)) + { + this._logger.LogDebug("Resource not modified (304): {ResourcePath}", resourcePath); + } + return true; } @@ -161,12 +174,20 @@ private async Task TryServeResourceAsync(HttpContext context, string resou await response.Body.WriteAsync(content, context.RequestAborted).ConfigureAwait(false); - this._logger.LogDebug("Served embedded resource: {ResourcePath} (compressed: {Compressed})", resourcePath, serveCompressed); + if (this._logger.IsEnabled(LogLevel.Debug)) + { + this._logger.LogDebug("Served embedded resource: {ResourcePath} (compressed: {Compressed})", resourcePath, serveCompressed); + } + return true; } catch (Exception ex) { - this._logger.LogError(ex, "Error serving embedded resource: {ResourcePath}", resourcePath); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError(ex, "Error serving embedded resource: {ResourcePath}", resourcePath); + } + return false; } } diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs index 49c55be5da..021c8f22c7 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs @@ -98,8 +98,10 @@ public override async Task RunAsync( responseFormat = chatClientOptions.ChatOptions?.ResponseFormat; } - RunRequest request = new([.. messages], responseFormat, enableToolCalls, enableToolNames); - request.OrchestrationId = this._context.InstanceId; + RunRequest request = new([.. messages], responseFormat, enableToolCalls, enableToolNames) + { + OrchestrationId = this._context.InstanceId + }; try { diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/State/DurableAgentStateFunctionCallContent.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/State/DurableAgentStateFunctionCallContent.cs index babea0f4ff..1deccc8a77 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/State/DurableAgentStateFunctionCallContent.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/State/DurableAgentStateFunctionCallContent.cs @@ -46,7 +46,7 @@ public static DurableAgentStateFunctionCallContent FromFunctionCallContent(Funct { return new DurableAgentStateFunctionCallContent() { - Arguments = content.Arguments?.ToImmutableDictionary() ?? ImmutableDictionary.Empty, + Arguments = content.Arguments?.ToDictionary() ?? [], CallId = content.CallId, Name = content.Name }; diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIServerSentEventsResult.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIServerSentEventsResult.cs index a0111605eb..95642771ff 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIServerSentEventsResult.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIServerSentEventsResult.cs @@ -20,8 +20,6 @@ internal sealed partial class AGUIServerSentEventsResult : IResult, IDisposable private readonly ILogger _logger; private Utf8JsonWriter? _jsonWriter; - public int? StatusCode => StatusCodes.Status200OK; - internal AGUIServerSentEventsResult(IAsyncEnumerable events, ILogger logger) { this._events = events; diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/HostedAgentResponseExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/HostedAgentResponseExecutor.cs index dd9a048ea8..78cf89b970 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/HostedAgentResponseExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/HostedAgentResponseExecutor.cs @@ -59,7 +59,11 @@ public HostedAgentResponseExecutor( AIAgent? agent = this._serviceProvider.GetKeyedService(agentName); if (agent is null) { - this._logger.LogWarning("Failed to resolve agent with name '{AgentName}'", agentName); + if (this._logger.IsEnabled(LogLevel.Warning)) + { + this._logger.LogWarning("Failed to resolve agent with name '{AgentName}'", agentName); + } + return ValueTask.FromResult(new ResponseError { Code = "agent_not_found", diff --git a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Client.cs b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Client.cs index c3be7c6262..39c4db8c96 100644 --- a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Client.cs +++ b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Client.cs @@ -133,7 +133,7 @@ internal sealed class CreateMemoryRequest [JsonPropertyName("agent_id")] public string? AgentId { get; set; } [JsonPropertyName("run_id")] public string? RunId { get; set; } [JsonPropertyName("user_id")] public string? UserId { get; set; } - [JsonPropertyName("messages")] public CreateMemoryMessage[] Messages { get; set; } = Array.Empty(); + [JsonPropertyName("messages")] public CreateMemoryMessage[] Messages { get; set; } = []; } internal sealed class CreateMemoryMessage diff --git a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs index 98bed507d5..0e9b4288b1 100644 --- a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs @@ -153,7 +153,7 @@ public override async ValueTask InvokingAsync(InvokingContext context ? null : $"{this._contextPrompt}\n{string.Join(Environment.NewLine, memories)}"; - if (this._logger is not null) + if (this._logger?.IsEnabled(LogLevel.Information) is true) { this._logger.LogInformation( "Mem0AIContextProvider: Retrieved {Count} memories. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", @@ -162,7 +162,8 @@ public override async ValueTask InvokingAsync(InvokingContext context this._searchScope.AgentId, this._searchScope.ThreadId, this.SanitizeLogData(this._searchScope.UserId)); - if (outputMessageText is not null) + + if (outputMessageText is not null && this._logger.IsEnabled(LogLevel.Trace)) { this._logger.LogTrace( "Mem0AIContextProvider: Search Results\nInput:{Input}\nOutput:{MessageText}\nApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", @@ -186,13 +187,16 @@ public override async ValueTask InvokingAsync(InvokingContext context } catch (Exception ex) { - this._logger?.LogError( - ex, - "Mem0AIContextProvider: Failed to search Mem0 for memories due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", - this._searchScope.ApplicationId, - this._searchScope.AgentId, - this._searchScope.ThreadId, - this.SanitizeLogData(this._searchScope.UserId)); + if (this._logger?.IsEnabled(LogLevel.Error) is true) + { + this._logger.LogError( + ex, + "Mem0AIContextProvider: Failed to search Mem0 for memories due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + this._searchScope.ApplicationId, + this._searchScope.AgentId, + this._searchScope.ThreadId, + this.SanitizeLogData(this._searchScope.UserId)); + } return new AIContext(); } } @@ -212,13 +216,16 @@ public override async ValueTask InvokedAsync(InvokedContext context, Cancellatio } catch (Exception ex) { - this._logger?.LogError( - ex, - "Mem0AIContextProvider: Failed to send messages to Mem0 due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", - this._storageScope.ApplicationId, - this._storageScope.AgentId, - this._storageScope.ThreadId, - this.SanitizeLogData(this._storageScope.UserId)); + if (this._logger?.IsEnabled(LogLevel.Error) is true) + { + this._logger.LogError( + ex, + "Mem0AIContextProvider: Failed to send messages to Mem0 due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + this._storageScope.ApplicationId, + this._storageScope.AgentId, + this._storageScope.ThreadId, + this.SanitizeLogData(this._storageScope.UserId)); + } } } diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs b/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs index 5469079015..efe376718b 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs @@ -43,7 +43,10 @@ public BackgroundJobRunner(IChannelHandler channelHandler, IPurviewClient purvie } catch (Exception e) when (e is not OperationCanceledException and not SystemException) { - this._logger.LogError(e, "Error running background job {BackgroundJobError}.", e.Message); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError(e, "Error running background job {BackgroundJobError}.", e.Message); + } } } }); diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/ChannelHandler.cs b/dotnet/src/Microsoft.Agents.AI.Purview/ChannelHandler.cs index 746014b700..89b5c864fa 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/ChannelHandler.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/ChannelHandler.cs @@ -73,7 +73,10 @@ public void QueueJob(BackgroundJobBase job) } catch (Exception e) when (this._purviewSettings.IgnoreExceptions) { - this._logger.LogError(e, "Error queuing job: {ExceptionMessage}", e.Message); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError(e, "Error queuing job: {ExceptionMessage}", e.Message); + } } } diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAppLocation.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAppLocation.cs index 5e5d7af96f..0c10db6237 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAppLocation.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAppLocation.cs @@ -38,16 +38,12 @@ public PurviewAppLocation(PurviewLocationType locationType, string locationValue /// Thrown when an invalid location type is provided. internal PolicyLocation GetPolicyLocation() { - switch (this.LocationType) + return this.LocationType switch { - case PurviewLocationType.Application: - return new PolicyLocation($"{Constants.ODataGraphNamespace}.policyLocationApplication", this.LocationValue); - case PurviewLocationType.Uri: - return new PolicyLocation($"{Constants.ODataGraphNamespace}.policyLocationUrl", this.LocationValue); - case PurviewLocationType.Domain: - return new PolicyLocation($"{Constants.ODataGraphNamespace}.policyLocationDomain", this.LocationValue); - default: - throw new InvalidOperationException("Invalid location type."); - } + PurviewLocationType.Application => new($"{Constants.ODataGraphNamespace}.policyLocationApplication", this.LocationValue), + PurviewLocationType.Uri => new($"{Constants.ODataGraphNamespace}.policyLocationUrl", this.LocationValue), + PurviewLocationType.Domain => new($"{Constants.ODataGraphNamespace}.policyLocationDomain", this.LocationValue), + _ => throw new InvalidOperationException("Invalid location type."), + }; } } diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewClient.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewClient.cs index 7fade4eabb..28013f524e 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewClient.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewClient.cs @@ -58,7 +58,7 @@ public PurviewClient(TokenCredential tokenCredential, PurviewSettings purviewSet this._tokenCredential = tokenCredential; this._httpClient = httpClient; - this._scopes = new string[] { $"https://{purviewSettings.GraphBaseUri.Host}/.default" }; + this._scopes = [$"https://{purviewSettings.GraphBaseUri.Host}/.default"]; this._graphUri = purviewSettings.GraphBaseUri.ToString().TrimEnd('/'); this._logger = logger ?? NullLogger.Instance; } @@ -176,7 +176,11 @@ public async Task ProcessContentAsync(ProcessContentRequ throw new PurviewRequestException(DeserializeError); } - this._logger.LogError("Failed to process content. Status code: {StatusCode}", response.StatusCode); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError("Failed to process content. Status code: {StatusCode}", response.StatusCode); + } + throw CreateExceptionForStatusCode(response.StatusCode, "processContent"); } } @@ -241,7 +245,11 @@ public async Task GetProtectionScopesAsync(ProtectionS throw new PurviewRequestException(DeserializeError); } - this._logger.LogError("Failed to retrieve protection scopes. Status code: {StatusCode}", response.StatusCode); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError("Failed to retrieve protection scopes. Status code: {StatusCode}", response.StatusCode); + } + throw CreateExceptionForStatusCode(response.StatusCode, "protectionScopes/compute"); } } @@ -304,7 +312,11 @@ public async Task SendContentActivitiesAsync(ContentA throw new PurviewRequestException(DeserializeError); } - this._logger.LogError("Failed to create content activities. Status code: {StatusCode}", response.StatusCode); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError("Failed to create content activities. Status code: {StatusCode}", response.StatusCode); + } + throw CreateExceptionForStatusCode(response.StatusCode, "contentActivities"); } } diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs index c8316a4e21..a818fb264f 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs @@ -73,13 +73,20 @@ public async Task ProcessChatContentAsync(IEnumerable (bool shouldBlockPrompt, resolvedUserId) = await this._scopedProcessor.ProcessMessagesAsync(messages, options?.ConversationId, Activity.UploadText, this._purviewSettings, null, cancellationToken).ConfigureAwait(false); if (shouldBlockPrompt) { - this._logger.LogInformation("Prompt blocked by policy. Sending message: {Message}", this._purviewSettings.BlockedPromptMessage); + if (this._logger.IsEnabled(LogLevel.Information)) + { + this._logger.LogInformation("Prompt blocked by policy. Sending message: {Message}", this._purviewSettings.BlockedPromptMessage); + } + return new ChatResponse(new ChatMessage(ChatRole.System, this._purviewSettings.BlockedPromptMessage)); } } catch (Exception ex) { - this._logger.LogError(ex, "Error processing prompt: {ExceptionMessage}", ex.Message); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError(ex, "Error processing prompt: {ExceptionMessage}", ex.Message); + } if (!this._purviewSettings.IgnoreExceptions) { @@ -94,13 +101,20 @@ public async Task ProcessChatContentAsync(IEnumerable (bool shouldBlockResponse, _) = await this._scopedProcessor.ProcessMessagesAsync(response.Messages, options?.ConversationId, Activity.UploadText, this._purviewSettings, resolvedUserId, cancellationToken).ConfigureAwait(false); if (shouldBlockResponse) { - this._logger.LogInformation("Response blocked by policy. Sending message: {Message}", this._purviewSettings.BlockedResponseMessage); + if (this._logger.IsEnabled(LogLevel.Information)) + { + this._logger.LogInformation("Response blocked by policy. Sending message: {Message}", this._purviewSettings.BlockedResponseMessage); + } + return new ChatResponse(new ChatMessage(ChatRole.System, this._purviewSettings.BlockedResponseMessage)); } } catch (Exception ex) { - this._logger.LogError(ex, "Error processing response: {ExceptionMessage}", ex.Message); + if (this._logger.IsEnabled(LogLevel.Error)) + { + this._logger.LogError(ex, "Error processing response: {ExceptionMessage}", ex.Message); + } if (!this._purviewSettings.IgnoreExceptions) { @@ -132,13 +146,20 @@ public async Task ProcessAgentContentAsync(IEnumerable ProcessAgentContentAsync(IEnumerable ProcessContentWithProtectionScopesAsy /// /// The process content response which may contain DLP actions. /// DLP actions returned from protection scopes. - /// The process content response with the protection scopes DLP actions added. Actions are deduplicated. + /// The process content response with the protection scopes DLP actions added. private static ProcessContentResponse CombinePolicyActions(ProcessContentResponse pcResponse, List? actionInfos) { - if (actionInfos == null || actionInfos.Count == 0) + if (actionInfos?.Count > 0) { - return pcResponse; + pcResponse.PolicyActions = pcResponse.PolicyActions is null ? + actionInfos : + [.. pcResponse.PolicyActions, .. actionInfos]; } - if (pcResponse.PolicyActions == null) - { - pcResponse.PolicyActions = actionInfos; - return pcResponse; - } - - List pcActionInfos = new(pcResponse.PolicyActions); - pcActionInfos.AddRange(actionInfos); - pcResponse.PolicyActions = pcActionInfos; return pcResponse; } @@ -339,20 +332,14 @@ private static ProtectionScopesRequest CreateProtectionScopesRequest(ProcessCont /// The protection scopes activity. private static ProtectionScopeActivities TranslateActivity(Activity activity) { - switch (activity) + return activity switch { - case Activity.Unknown: - return ProtectionScopeActivities.None; - case Activity.UploadText: - return ProtectionScopeActivities.UploadText; - case Activity.UploadFile: - return ProtectionScopeActivities.UploadFile; - case Activity.DownloadText: - return ProtectionScopeActivities.DownloadText; - case Activity.DownloadFile: - return ProtectionScopeActivities.DownloadFile; - default: - return ProtectionScopeActivities.UnknownFutureValue; - } + Activity.Unknown => ProtectionScopeActivities.None, + Activity.UploadText => ProtectionScopeActivities.UploadText, + Activity.UploadFile => ProtectionScopeActivities.UploadFile, + Activity.DownloadText => ProtectionScopeActivities.DownloadText, + Activity.DownloadFile => ProtectionScopeActivities.DownloadFile, + _ => ProtectionScopeActivities.UnknownFutureValue, + }; } } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/RepresentationExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/RepresentationExtensions.cs index 3d76c965bd..7c5b8183a6 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/RepresentationExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/RepresentationExtensions.cs @@ -46,7 +46,7 @@ public static WorkflowInfo ToWorkflowInfo(this Workflow workflow) keySelector: sourceId => sourceId, elementSelector: sourceId => workflow.Edges[sourceId].Select(ToEdgeInfo).ToList()); - HashSet inputPorts = new(workflow.Ports.Values.Select(ToPortInfo)); + HashSet inputPorts = [.. workflow.Ports.Values.Select(ToPortInfo)]; return new WorkflowInfo(executors, edges, inputPorts, workflow.StartExecutorId, workflow.OutputExecutors); } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeConnection.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeConnection.cs index 8833907489..6780b26fe7 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeConnection.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeConnection.cs @@ -41,8 +41,8 @@ public EdgeConnection(List sourceIds, List sinkIds) /// contains duplicate values. public static EdgeConnection CreateChecked(List sourceIds, List sinkIds) { - HashSet sourceSet = new(Throw.IfNull(sourceIds)); - HashSet sinkSet = new(Throw.IfNull(sinkIds)); + HashSet sourceSet = [.. Throw.IfNull(sourceIds)]; + HashSet sinkSet = [.. Throw.IfNull(sinkIds)]; if (sourceSet.Count != sourceIds.Count) { diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeState.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeState.cs index fe564f1c38..9c6a941a11 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeState.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeState.cs @@ -14,7 +14,7 @@ internal sealed class FanInEdgeState public FanInEdgeState(FanInEdgeData fanInEdge) { this.SourceIds = fanInEdge.SourceIds.ToArray(); - this.Unseen = new(this.SourceIds); + this.Unseen = [.. this.SourceIds]; this._pendingMessages = []; } @@ -40,7 +40,7 @@ public FanInEdgeState(string[] sourceIds, HashSet unseen, List takenMessages = Interlocked.Exchange(ref this._pendingMessages, []); - this.Unseen = new(this.SourceIds); + this.Unseen = [.. this.SourceIds]; if (takenMessages.Count == 0) { diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowBuilder.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowBuilder.cs index 674a65f7f6..93f7850135 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowBuilder.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowBuilder.cs @@ -380,7 +380,7 @@ private void Validate(bool validateOrphans) } // Make sure that all nodes are connected to the start executor (transitively) - HashSet remainingExecutors = new(this._executorBindings.Keys); + HashSet remainingExecutors = [.. this._executorBindings.Keys]; Queue toVisit = new([this._startExecutorId]); if (!validateOrphans) diff --git a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs index c232b2d554..6384d36d9f 100644 --- a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs @@ -212,13 +212,17 @@ public override async ValueTask InvokingAsync(InvokingContext context } catch (Exception ex) { - this._logger?.LogError( - ex, - "ChatHistoryMemoryProvider: Failed to search for chat history due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", - this._searchScope.ApplicationId, - this._searchScope.AgentId, - this._searchScope.ThreadId, - this.SanitizeLogData(this._searchScope.UserId)); + if (this._logger?.IsEnabled(LogLevel.Error) is true) + { + this._logger.LogError( + ex, + "ChatHistoryMemoryProvider: Failed to search for chat history due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + this._searchScope.ApplicationId, + this._searchScope.AgentId, + this._searchScope.ThreadId, + this.SanitizeLogData(this._searchScope.UserId)); + } + return new AIContext(); } } @@ -264,13 +268,16 @@ public override async ValueTask InvokedAsync(InvokedContext context, Cancellatio } catch (Exception ex) { - this._logger?.LogError( - ex, - "ChatHistoryMemoryProvider: Failed to add messages to chat history vector store due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", - this._searchScope.ApplicationId, - this._searchScope.AgentId, - this._searchScope.ThreadId, - this.SanitizeLogData(this._searchScope.UserId)); + if (this._logger?.IsEnabled(LogLevel.Error) is true) + { + this._logger.LogError( + ex, + "ChatHistoryMemoryProvider: Failed to add messages to chat history vector store due to error. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + this._searchScope.ApplicationId, + this._searchScope.AgentId, + this._searchScope.ThreadId, + this.SanitizeLogData(this._searchScope.UserId)); + } } } @@ -302,14 +309,18 @@ internal async Task SearchTextAsync(string userQuestion, CancellationTok var formatted = $"{this._contextPrompt}\n{outputResultsText}"; - this._logger?.LogTrace( - "ChatHistoryMemoryProvider: Search Results\nInput:{Input}\nOutput:{MessageText}\n ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", - this.SanitizeLogData(userQuestion), - this.SanitizeLogData(formatted), - this._searchScope.ApplicationId, - this._searchScope.AgentId, - this._searchScope.ThreadId, - this.SanitizeLogData(this._searchScope.UserId)); + if (this._logger?.IsEnabled(LogLevel.Trace) is true) + { + this._logger.LogTrace( + "ChatHistoryMemoryProvider: Search Results\nInput:{Input}\nOutput:{MessageText}\n ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + this.SanitizeLogData(userQuestion), + this.SanitizeLogData(formatted), + this._searchScope.ApplicationId, + this._searchScope.AgentId, + this._searchScope.ThreadId, + this.SanitizeLogData(this._searchScope.UserId)); + } + return formatted; } @@ -383,13 +394,16 @@ internal async Task SearchTextAsync(string userQuestion, CancellationTok results.Add(result.Record); } - this._logger?.LogInformation( - "ChatHistoryMemoryProvider: Retrieved {Count} search results. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", - results.Count, - this._searchScope.ApplicationId, - this._searchScope.AgentId, - this._searchScope.ThreadId, - this.SanitizeLogData(this._searchScope.UserId)); + if (this._logger?.IsEnabled(LogLevel.Information) is true) + { + this._logger.LogInformation( + "ChatHistoryMemoryProvider: Retrieved {Count} search results. ApplicationId: '{ApplicationId}', AgentId: '{AgentId}', ThreadId: '{ThreadId}', UserId: '{UserId}'.", + results.Count, + this._searchScope.ApplicationId, + this._searchScope.AgentId, + this._searchScope.ThreadId, + this.SanitizeLogData(this._searchScope.UserId)); + } return results; } diff --git a/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs b/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs index 9659fdb7b8..be9eba1365 100644 --- a/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs @@ -134,7 +134,11 @@ public override async ValueTask InvokingAsync(InvokingContext context // Search var results = await this._searchAsync(input, cancellationToken).ConfigureAwait(false); IList materialized = results as IList ?? results.ToList(); - this._logger?.LogInformation("TextSearchProvider: Retrieved {Count} search results.", materialized.Count); + + if (this._logger?.IsEnabled(LogLevel.Information) is true) + { + this._logger?.LogInformation("TextSearchProvider: Retrieved {Count} search results.", materialized.Count); + } if (materialized.Count == 0) { @@ -144,7 +148,10 @@ public override async ValueTask InvokingAsync(InvokingContext context // Format search results string formatted = this.FormatResults(materialized); - this._logger?.LogTrace("TextSearchProvider: Search Results\nInput:{Input}\nOutput:{MessageText}", input, formatted); + if (this._logger?.IsEnabled(LogLevel.Trace) is true) + { + this._logger.LogTrace("TextSearchProvider: Search Results\nInput:{Input}\nOutput:{MessageText}", input, formatted); + } return new AIContext { @@ -230,8 +237,15 @@ internal async Task SearchAsync(string userQuestion, CancellationToken c IList materialized = results as IList ?? results.ToList(); string outputText = this.FormatResults(materialized); - this._logger?.LogInformation("TextSearchProvider: Retrieved {Count} search results.", materialized.Count); - this._logger?.LogTrace("TextSearchProvider Input:{UserQuestion}\nOutput:{MessageText}", userQuestion, outputText); + if (this._logger?.IsEnabled(LogLevel.Information) is true) + { + this._logger.LogInformation("TextSearchProvider: Retrieved {Count} search results.", materialized.Count); + + if (this._logger.IsEnabled(LogLevel.Trace)) + { + this._logger.LogTrace("TextSearchProvider Input:{UserQuestion}\nOutput:{MessageText}", userQuestion, outputText); + } + } return outputText; } diff --git a/dotnet/tests/.editorconfig b/dotnet/tests/.editorconfig index e3ee57d9ba..a200bbb9ed 100644 --- a/dotnet/tests/.editorconfig +++ b/dotnet/tests/.editorconfig @@ -1,6 +1,10 @@ # Suppressing errors for Test projects under dotnet/tests folder [*.cs] +dotnet_diagnostic.CA1822.severity = none # Member does not access instance data and can be marked as static +dotnet_diagnostic.CA1873.severity = none # Evaluation of logging arguments may be expensive +dotnet_diagnostic.CA1875.severity = none # Regex.IsMatch/Count instead of Regex.Match(...).Success/Regex.Matches(...).Count dotnet_diagnostic.CA2007.severity = none # Do not directly await a Task +dotnet_diagnostic.CA2249.severity = none # Use `string.Contains` instead of `string.IndexOf` to improve readability dotnet_diagnostic.CS1591.severity = none # Missing XML comment for publicly visible type or member diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs index f079fc5ed8..9869d47f6b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs @@ -547,7 +547,7 @@ public async Task RunAsync_WithVariousTaskStates_ReturnsCorrectTokenAsync(TaskSt var result = await this._agent.RunAsync("Test message"); // Assert - if (taskState == TaskState.Submitted || taskState == TaskState.Working) + if (taskState is TaskState.Submitted or TaskState.Working) { Assert.NotNull(result.ContinuationToken); } diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs index 659c678034..b18abd4485 100644 --- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs @@ -64,12 +64,12 @@ public void ToAIContents_WithMultipleParts_ReturnsCorrectList() { ArtifactId = "artifact-ai-multi", Name = "test", - Parts = new List - { + Parts = + [ new TextPart { Text = "Part 1" }, new TextPart { Text = "Part 2" }, new TextPart { Text = "Part 3" } - }, + ], Metadata = null }; @@ -93,7 +93,7 @@ public void ToAIContents_WithEmptyParts_ReturnsEmptyList() { ArtifactId = "artifact-empty", Name = "test", - Parts = new List(), + Parts = [], Metadata = null }; diff --git a/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs b/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs index 0eeacaf161..c61a7e289d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/AGUIChatClientTests.cs @@ -919,7 +919,7 @@ public async Task GetStreamingResponseAsync_ExtractsThreadIdFromFunctionCall_OnS List messages = [new ChatMessage(ChatRole.User, "Test")]; // Act - First turn - List conversation = new(messages); + List conversation = [.. messages]; string? conversationId = null; await foreach (var update in chatClient.GetStreamingResponseAsync(conversation, options)) { diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs index 1136d2b1f4..4ca2b7f461 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs @@ -2684,13 +2684,12 @@ public TestChatClient(IChatClient innerClient) : base(innerClient) private sealed class MockPipelineResponse : PipelineResponse { private readonly int _status; - private readonly BinaryData _content; private readonly MockPipelineResponseHeaders _headers; public MockPipelineResponse(int status, BinaryData? content = null) { this._status = status; - this._content = content ?? BinaryData.Empty; + this.Content = content ?? BinaryData.Empty; this._headers = new MockPipelineResponseHeaders(); } @@ -2704,7 +2703,7 @@ public override Stream? ContentStream set { } } - public override BinaryData Content => this._content; + public override BinaryData Content { get; } protected override PipelineResponseHeaders HeadersCore => this._headers; diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatMessageStoreTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatMessageStoreTests.cs index 6f2a256206..7ce0611c99 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatMessageStoreTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatMessageStoreTests.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Identity; -using Microsoft.Agents.AI; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.AI; using Xunit; @@ -81,7 +80,7 @@ await databaseResponse.Database.CreateContainerIfNotExistsAsync( throughput: 400); // Create container for hierarchical partitioning tests with hierarchical partition key - var hierarchicalContainerProperties = new ContainerProperties(HierarchicalTestContainerId, new List { "/tenantId", "/userId", "/sessionId" }); + var hierarchicalContainerProperties = new ContainerProperties(HierarchicalTestContainerId, ["/tenantId", "/userId", "/sessionId"]); await databaseResponse.Database.CreateContainerIfNotExistsAsync( hierarchicalContainerProperties, throughput: 400); @@ -247,7 +246,7 @@ public async Task AddMessagesAsync_WithSingleMessage_ShouldAddMessageAsync() PartitionKey = new PartitionKey(conversationId) }); - List rawResults = new(); + List rawResults = []; while (rawIterator.HasMoreResults) { var rawResponse = await rawIterator.ReadNextAsync(); diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs index dfa1f14221..8f5749b187 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs @@ -77,7 +77,7 @@ await this._database.CreateContainerIfNotExistsAsync( this._emulatorAvailable = true; } - catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException || ex is AccessViolationException)) + catch (Exception ex) when (ex is not (OutOfMemoryException or StackOverflowException or AccessViolationException)) { // Emulator not available, tests will be skipped this._emulatorAvailable = false; diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIServerSentEventsResultTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIServerSentEventsResultTests.cs index c2a8fa9998..f049218473 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIServerSentEventsResultTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIServerSentEventsResultTests.cs @@ -100,9 +100,6 @@ public async Task ExecuteAsync_WithEmptyEventStream_CompletesSuccessfullyAsync() // Act await result.ExecuteAsync(httpContext); - - // Assert - Assert.Equal(StatusCodes.Status200OK, result.StatusCode); } [Fact] diff --git a/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs index 5240eccd05..bacc59833a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs @@ -49,14 +49,14 @@ public async Task CanAddAndRetrieveUserMemoriesAsync() var sut = new Mem0Provider(this._httpClient, storageScope); await sut.ClearStoredMemoriesAsync(); - var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext(new[] { question })); + var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question])); Assert.DoesNotContain("Caoimhe", ctxBefore.Messages?[0].Text ?? string.Empty); // Act - await sut.InvokedAsync(new AIContextProvider.InvokedContext(new[] { input }, aiContextProviderMessages: null)); + await sut.InvokedAsync(new AIContextProvider.InvokedContext([input], aiContextProviderMessages: null)); var ctxAfterAdding = await GetContextWithRetryAsync(sut, question); await sut.ClearStoredMemoriesAsync(); - var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext(new[] { question })); + var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question])); // Assert Assert.Contains("Caoimhe", ctxAfterAdding.Messages?[0].Text ?? string.Empty); @@ -73,14 +73,14 @@ public async Task CanAddAndRetrieveAgentMemoriesAsync() var sut = new Mem0Provider(this._httpClient, storageScope); await sut.ClearStoredMemoriesAsync(); - var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext(new[] { question })); + var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question])); Assert.DoesNotContain("Caoimhe", ctxBefore.Messages?[0].Text ?? string.Empty); // Act - await sut.InvokedAsync(new AIContextProvider.InvokedContext(new[] { assistantIntro }, aiContextProviderMessages: null)); + await sut.InvokedAsync(new AIContextProvider.InvokedContext([assistantIntro], aiContextProviderMessages: null)); var ctxAfterAdding = await GetContextWithRetryAsync(sut, question); await sut.ClearStoredMemoriesAsync(); - var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext(new[] { question })); + var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question])); // Assert Assert.Contains("Caoimhe", ctxAfterAdding.Messages?[0].Text ?? string.Empty); @@ -99,13 +99,13 @@ public async Task DoesNotLeakMemoriesAcrossAgentScopesAsync() await sut1.ClearStoredMemoriesAsync(); await sut2.ClearStoredMemoriesAsync(); - var ctxBefore1 = await sut1.InvokingAsync(new AIContextProvider.InvokingContext(new[] { question })); - var ctxBefore2 = await sut2.InvokingAsync(new AIContextProvider.InvokingContext(new[] { question })); + var ctxBefore1 = await sut1.InvokingAsync(new AIContextProvider.InvokingContext([question])); + var ctxBefore2 = await sut2.InvokingAsync(new AIContextProvider.InvokingContext([question])); Assert.DoesNotContain("Caoimhe", ctxBefore1.Messages?[0].Text ?? string.Empty); Assert.DoesNotContain("Caoimhe", ctxBefore2.Messages?[0].Text ?? string.Empty); // Act - await sut1.InvokedAsync(new AIContextProvider.InvokedContext(new[] { assistantIntro }, aiContextProviderMessages: null)); + await sut1.InvokedAsync(new AIContextProvider.InvokedContext([assistantIntro], aiContextProviderMessages: null)); var ctxAfterAdding1 = await GetContextWithRetryAsync(sut1, question); var ctxAfterAdding2 = await GetContextWithRetryAsync(sut2, question); @@ -123,7 +123,7 @@ private static async Task GetContextWithRetryAsync(Mem0Provider provi AIContext? ctx = null; for (int i = 0; i < attempts; i++) { - ctx = await provider.InvokingAsync(new AIContextProvider.InvokingContext(new[] { question }), CancellationToken.None); + ctx = await provider.InvokingAsync(new AIContextProvider.InvokingContext([question]), CancellationToken.None); var text = ctx.Messages?[0].Text; if (!string.IsNullOrEmpty(text) && text.IndexOf("Caoimhe", StringComparison.OrdinalIgnoreCase) >= 0) { diff --git a/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs index 0515c8e7ac..d1d48b33aa 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs @@ -35,6 +35,10 @@ public Mem0ProviderTests() .Setup(f => f.CreateLogger(typeof(Mem0Provider).FullName!)) .Returns(this._loggerMock.Object); + this._loggerMock + .Setup(f => f.IsEnabled(It.IsAny())) + .Returns(true); + this._httpClient = new HttpClient(this._handler) { BaseAddress = new Uri("https://localhost/") @@ -131,10 +135,10 @@ public async Task InvokingAsync_PerformsSearch_AndReturnsContextMessageAsync() } [Theory] - [InlineData(false, false, 2)] - [InlineData(true, false, 2)] - [InlineData(false, true, 1)] - [InlineData(true, true, 1)] + [InlineData(false, false, 4)] + [InlineData(true, false, 4)] + [InlineData(false, true, 2)] + [InlineData(true, true, 2)] public async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsync(bool enableSensitiveTelemetryData, bool requestThrows, int expectedLogInvocations) { // Arrange @@ -157,7 +161,7 @@ public async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsy var options = new Mem0ProviderOptions { EnableSensitiveTelemetryData = enableSensitiveTelemetryData }; var sut = new Mem0Provider(this._httpClient, storageScope, options: options, loggerFactory: this._loggerFactoryMock.Object); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "Who am I?") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Who am I?")]); // Act await sut.InvokingAsync(invokingContext, CancellationToken.None); @@ -166,7 +170,12 @@ public async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsy Assert.Equal(expectedLogInvocations, this._loggerMock.Invocations.Count); foreach (var logInvocation in this._loggerMock.Invocations) { - var state = Assert.IsAssignableFrom>>(logInvocation.Arguments[2]); + if (logInvocation.Method.Name == nameof(ILogger.IsEnabled)) + { + continue; + } + + var state = Assert.IsType>>(logInvocation.Arguments[2], exactMatch: false); var userIdValue = state.First(kvp => kvp.Key == "UserId").Value; Assert.Equal(enableSensitiveTelemetryData ? "user" : "", userIdValue); @@ -275,8 +284,8 @@ public async Task InvokedAsync_ShouldNotThrow_WhenStorageFailsAsync() [Theory] [InlineData(false, false, 0)] [InlineData(true, false, 0)] - [InlineData(false, true, 1)] - [InlineData(true, true, 1)] + [InlineData(false, true, 2)] + [InlineData(true, true, 2)] public async Task InvokedAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsync(bool enableSensitiveTelemetryData, bool requestThrows, int expectedLogCount) { // Arrange @@ -315,7 +324,12 @@ public async Task InvokedAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsyn Assert.Equal(expectedLogCount, this._loggerMock.Invocations.Count); foreach (var logInvocation in this._loggerMock.Invocations) { - var state = Assert.IsAssignableFrom>>(logInvocation.Arguments[2]); + if (logInvocation.Method.Name == nameof(ILogger.IsEnabled)) + { + continue; + } + + var state = Assert.IsType>>(logInvocation.Arguments[2], exactMatch: false); var userIdValue = state.First(kvp => kvp.Key == "UserId").Value; Assert.Equal(enableSensitiveTelemetryData ? "user" : "", userIdValue); } @@ -386,7 +400,7 @@ public async Task InvokingAsync_ShouldNotThrow_WhenSearchFailsAsync() // Arrange var storageScope = new Mem0ProviderScope { ApplicationId = "app" }; var provider = new Mem0Provider(this._httpClient, storageScope, loggerFactory: this._loggerFactoryMock.Object); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "Q?") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]); // Act var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None); diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewClientTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewClientTests.cs index 3e45d8d4bd..0846decc2f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewClientTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewClientTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Text; @@ -50,10 +49,10 @@ public async Task ProcessContentAsync_WithValidRequest_ReturnsSuccessResponseAsy { Id = "test-id-123", ProtectionScopeState = ProtectionScopeState.NotModified, - PolicyActions = new List - { + PolicyActions = + [ new() { Action = DlpAction.NotifyUser } - } + ] }; this._handler.StatusCodeToReturn = HttpStatusCode.OK; @@ -228,8 +227,8 @@ public async Task GetProtectionScopesAsync_WithValidRequest_ReturnsSuccessRespon var expectedResponse = new ProtectionScopesResponse { - Scopes = new List - { + Scopes = + [ new() { Activities = ProtectionScopeActivities.UploadText, @@ -238,7 +237,7 @@ public async Task GetProtectionScopesAsync_WithValidRequest_ReturnsSuccessRespon new ("microsoft.graph.policyLocationApplication", "app-123") ] } - } + ] }; this._handler.StatusCodeToReturn = HttpStatusCode.OK; @@ -264,7 +263,7 @@ public async Task GetProtectionScopesAsync_SetsETagFromResponse_Async() { // Arrange var request = new ProtectionScopesRequest("test-user-id", "test-tenant-id"); - var expectedResponse = new ProtectionScopesResponse { Scopes = new List() }; + var expectedResponse = new ProtectionScopesResponse { Scopes = [] }; this._handler.StatusCodeToReturn = HttpStatusCode.OK; this._handler.ResponseToReturn = JsonSerializer.Serialize(expectedResponse, PurviewSerializationUtils.SerializationSettings.GetTypeInfo(typeof(ProtectionScopesResponse))); diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs index 9d56e0bc50..3dfd5c9b88 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/ScopedContentProcessorTests.cs @@ -56,8 +56,8 @@ public async Task ProcessMessagesAsync_WithBlockAccessAction_ReturnsShouldBlockT var psResponse = new ProtectionScopesResponse { - Scopes = new List - { + Scopes = + [ new() { Activities = ProtectionScopeActivities.UploadText, @@ -67,7 +67,7 @@ public async Task ProcessMessagesAsync_WithBlockAccessAction_ReturnsShouldBlockT ], ExecutionMode = ExecutionMode.EvaluateInline } - } + ] }; this._mockPurviewClient.Setup(x => x.GetProtectionScopesAsync( @@ -76,10 +76,10 @@ public async Task ProcessMessagesAsync_WithBlockAccessAction_ReturnsShouldBlockT var pcResponse = new ProcessContentResponse { - PolicyActions = new List - { + PolicyActions = + [ new() { Action = DlpAction.BlockAccess } - } + ] }; this._mockPurviewClient.Setup(x => x.ProcessContentAsync( @@ -115,8 +115,8 @@ public async Task ProcessMessagesAsync_WithRestrictionActionBlock_ReturnsShouldB var psResponse = new ProtectionScopesResponse { - Scopes = new List - { + Scopes = + [ new() { Activities = ProtectionScopeActivities.UploadText, @@ -126,7 +126,7 @@ public async Task ProcessMessagesAsync_WithRestrictionActionBlock_ReturnsShouldB ], ExecutionMode = ExecutionMode.EvaluateInline } - } + ] }; this._mockPurviewClient.Setup(x => x.GetProtectionScopesAsync( @@ -135,10 +135,10 @@ public async Task ProcessMessagesAsync_WithRestrictionActionBlock_ReturnsShouldB var pcResponse = new ProcessContentResponse { - PolicyActions = new List - { + PolicyActions = + [ new() { RestrictionAction = RestrictionAction.Block } - } + ] }; this._mockPurviewClient.Setup(x => x.ProcessContentAsync( @@ -174,8 +174,8 @@ public async Task ProcessMessagesAsync_WithNoBlockingActions_ReturnsShouldBlockF var psResponse = new ProtectionScopesResponse { - Scopes = new List - { + Scopes = + [ new() { Activities = ProtectionScopeActivities.UploadText, @@ -185,7 +185,7 @@ public async Task ProcessMessagesAsync_WithNoBlockingActions_ReturnsShouldBlockF ], ExecutionMode = ExecutionMode.EvaluateInline } - } + ] }; this._mockPurviewClient.Setup(x => x.GetProtectionScopesAsync( @@ -194,10 +194,10 @@ public async Task ProcessMessagesAsync_WithNoBlockingActions_ReturnsShouldBlockF var pcResponse = new ProcessContentResponse { - PolicyActions = new List - { + PolicyActions = + [ new() { Action = DlpAction.NotifyUser } - } + ] }; this._mockPurviewClient.Setup(x => x.ProcessContentAsync( @@ -229,8 +229,8 @@ public async Task ProcessMessagesAsync_UsesCachedProtectionScopes_WhenAvailableA var cachedPsResponse = new ProtectionScopesResponse { - Scopes = new List - { + Scopes = + [ new() { Activities = ProtectionScopeActivities.UploadText, @@ -240,7 +240,7 @@ public async Task ProcessMessagesAsync_UsesCachedProtectionScopes_WhenAvailableA ], ExecutionMode = ExecutionMode.EvaluateInline } - } + ] }; this._mockCacheProvider.Setup(x => x.GetAsync( @@ -249,7 +249,7 @@ public async Task ProcessMessagesAsync_UsesCachedProtectionScopes_WhenAvailableA var pcResponse = new ProcessContentResponse { - PolicyActions = new List() + PolicyActions = [] }; this._mockPurviewClient.Setup(x => x.ProcessContentAsync( @@ -285,8 +285,8 @@ public async Task ProcessMessagesAsync_InvalidatesCache_WhenProtectionScopeModif var psResponse = new ProtectionScopesResponse { - Scopes = new List - { + Scopes = + [ new() { Activities = ProtectionScopeActivities.UploadText, @@ -296,7 +296,7 @@ public async Task ProcessMessagesAsync_InvalidatesCache_WhenProtectionScopeModif ], ExecutionMode = ExecutionMode.EvaluateInline } - } + ] }; this._mockPurviewClient.Setup(x => x.GetProtectionScopesAsync( @@ -306,7 +306,7 @@ public async Task ProcessMessagesAsync_InvalidatesCache_WhenProtectionScopeModif var pcResponse = new ProcessContentResponse { ProtectionScopeState = ProtectionScopeState.Modified, - PolicyActions = new List() + PolicyActions = [] }; this._mockPurviewClient.Setup(x => x.ProcessContentAsync( @@ -342,8 +342,8 @@ public async Task ProcessMessagesAsync_SendsContentActivities_WhenNoApplicableSc var psResponse = new ProtectionScopesResponse { - Scopes = new List - { + Scopes = + [ new() { Activities = ProtectionScopeActivities.UploadText, @@ -352,7 +352,7 @@ public async Task ProcessMessagesAsync_SendsContentActivities_WhenNoApplicableSc new ("microsoft.graph.policyLocationApplication", "app-456") ] } - } + ] }; this._mockPurviewClient.Setup(x => x.GetProtectionScopesAsync( @@ -436,7 +436,7 @@ public async Task ProcessMessagesAsync_ExtractsUserIdFromMessageAdditionalProper It.IsAny(), It.IsAny())) .ReturnsAsync((ProtectionScopesResponse?)null); - var psResponse = new ProtectionScopesResponse { Scopes = new List() }; + var psResponse = new ProtectionScopesResponse { Scopes = [] }; this._mockPurviewClient.Setup(x => x.GetProtectionScopesAsync( It.IsAny(), It.IsAny())) .ReturnsAsync(psResponse); @@ -471,7 +471,7 @@ public async Task ProcessMessagesAsync_ExtractsUserIdFromMessageAuthorName_WhenV It.IsAny(), It.IsAny())) .ReturnsAsync((ProtectionScopesResponse?)null); - var psResponse = new ProtectionScopesResponse { Scopes = new List() }; + var psResponse = new ProtectionScopesResponse { Scopes = [] }; this._mockPurviewClient.Setup(x => x.GetProtectionScopesAsync( It.IsAny(), It.IsAny())) .ReturnsAsync(psResponse); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs index 0e44eb8576..3698ee7065 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs @@ -30,6 +30,10 @@ public TextSearchProviderTests() this._loggerFactoryMock .Setup(f => f.CreateLogger(typeof(TextSearchProvider).FullName!)) .Returns(this._loggerMock.Object); + + this._loggerMock + .Setup(f => f.IsEnabled(It.IsAny())) + .Returns(true); } [Theory] @@ -135,7 +139,7 @@ public async Task InvokingAsync_OnDemand_ShouldExposeSearchToolAsync(string? ove FunctionToolDescription = overrideDescription }; var provider = new TextSearchProvider(this.NoResultSearchAsync, default, null, options); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "Q?") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]); // Act var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -154,7 +158,7 @@ public async Task InvokingAsync_ShouldNotThrow_WhenSearchFailsAsync() { // Arrange var provider = new TextSearchProvider(this.FailingSearchAsync, default, null, loggerFactory: this._loggerFactoryMock.Object); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "Q?") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]); // Act var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -247,7 +251,7 @@ public async Task InvokingAsync_ShouldUseContextFormatterWhenProvidedAsync() ContextFormatter = r => $"Custom formatted context with {r.Count} results." }; var provider = new TextSearchProvider(SearchDelegateAsync, default, null, options); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "Q?") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]); // Act var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -281,7 +285,7 @@ public async Task InvokingAsync_WithRawRepresentations_ContextFormatterCanAccess ContextFormatter = r => string.Join(",", r.Select(x => ((RawPayload)x.RawRepresentation!).Id)) }; var provider = new TextSearchProvider(SearchDelegateAsync, default, null, options); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "Q?") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]); // Act var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -298,7 +302,7 @@ public async Task InvokingAsync_WithNoResults_ShouldReturnEmptyContextAsync() // Arrange var options = new TextSearchProviderOptions { SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke }; var provider = new TextSearchProvider(this.NoResultSearchAsync, default, null, options); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "Q?") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]); // Act var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -338,10 +342,10 @@ public async Task InvokingAsync_WithPreviousFailedRequest_ShouldNotIncludeFailed }; await provider.InvokedAsync(new(initialMessages, aiContextProviderMessages: null) { InvokeException = new InvalidOperationException("Request Failed") }); - var invokingContext = new AIContextProvider.InvokingContext(new[] - { + var invokingContext = new AIContextProvider.InvokingContext( + [ new ChatMessage(ChatRole.User, "E") - }); + ]); // Act await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -378,10 +382,10 @@ public async Task InvokingAsync_WithRecentMessageMemory_ShouldIncludeStoredMessa }; await provider.InvokedAsync(new(initialMessages, aiContextProviderMessages: null)); - var invokingContext = new AIContextProvider.InvokingContext(new[] - { + var invokingContext = new AIContextProvider.InvokingContext( + [ new ChatMessage(ChatRole.User, "E") - }); + ]); // Act await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -409,21 +413,21 @@ public async Task InvokingAsync_WithAccumulatedMemoryAcrossInvocations_ShouldInc var provider = new TextSearchProvider(SearchDelegateAsync, default, null, options); // First memory update (A,B) - await provider.InvokedAsync(new(new[] - { + await provider.InvokedAsync(new( + [ new ChatMessage(ChatRole.User, "A"), new ChatMessage(ChatRole.Assistant, "B"), - }, aiContextProviderMessages: null)); + ], aiContextProviderMessages: null)); // Second memory update (C,D,E) - await provider.InvokedAsync(new(new[] - { + await provider.InvokedAsync(new( + [ new ChatMessage(ChatRole.User, "C"), new ChatMessage(ChatRole.Assistant, "D"), new ChatMessage(ChatRole.User, "E"), - }, aiContextProviderMessages: null)); + ], aiContextProviderMessages: null)); - var invokingContext = new AIContextProvider.InvokingContext(new[] { new ChatMessage(ChatRole.User, "F") }); + var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "F")]); // Act await provider.InvokingAsync(invokingContext, CancellationToken.None); @@ -460,10 +464,10 @@ public async Task InvokingAsync_WithRecentMessageRolesIncluded_ShouldFilterRoles }; await provider.InvokedAsync(new(initialMessages, null)); - var invokingContext = new AIContextProvider.InvokingContext(new[] - { + var invokingContext = new AIContextProvider.InvokingContext( + [ new ChatMessage(ChatRole.User, "Question?") // Current request message always appended. - }); + ]); // Act await provider.InvokingAsync(invokingContext, CancellationToken.None); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs index 860867f8a2..f898147ad0 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs @@ -36,6 +36,10 @@ public ChatHistoryMemoryProviderTests() .Setup(f => f.CreateLogger(typeof(ChatHistoryMemoryProvider).FullName!)) .Returns(this._loggerMock.Object); + this._loggerMock + .Setup(f => f.IsEnabled(It.IsAny())) + .Returns(true); + this._vectorStoreCollectionMock = new(MockBehavior.Strict); this._vectorStoreMock = new(MockBehavior.Strict); @@ -218,8 +222,8 @@ public async Task InvokedAsync_DoesNotThrow_WhenUpsertThrowsAsync() [Theory] [InlineData(false, false, 0)] [InlineData(true, false, 0)] - [InlineData(false, true, 1)] - [InlineData(true, true, 1)] + [InlineData(false, true, 2)] + [InlineData(true, true, 2)] public async Task InvokedAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsync(bool enableSensitiveTelemetryData, bool requestThrows, int expectedLogInvocations) { // Arrange @@ -259,6 +263,11 @@ public async Task InvokedAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsyn Assert.Equal(expectedLogInvocations, this._loggerMock.Invocations.Count); foreach (var logInvocation in this._loggerMock.Invocations) { + if (logInvocation.Method.Name == nameof(ILogger.IsEnabled)) + { + continue; + } + var state = Assert.IsType>>(logInvocation.Arguments[2], exactMatch: false); var userIdValue = state.First(kvp => kvp.Key == "UserId").Value; Assert.Equal(enableSensitiveTelemetryData ? "user1" : "", userIdValue); @@ -385,10 +394,10 @@ public async Task InvokedAsync_CreatesFilter_WhenSearchScopeProvidedAsync() } [Theory] - [InlineData(false, false, 1)] - [InlineData(true, false, 1)] - [InlineData(false, true, 1)] - [InlineData(true, true, 1)] + [InlineData(false, false, 2)] + [InlineData(true, false, 2)] + [InlineData(false, true, 2)] + [InlineData(true, true, 2)] public async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsync(bool enableSensitiveTelemetryData, bool requestThrows, int expectedLogInvocations) { // Arrange @@ -442,7 +451,12 @@ public async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsy Assert.Equal(expectedLogInvocations, this._loggerMock.Invocations.Count); foreach (var logInvocation in this._loggerMock.Invocations) { - var state = Assert.IsAssignableFrom>>(logInvocation.Arguments[2]); + if (logInvocation.Method.Name == nameof(ILogger.IsEnabled)) + { + continue; + } + + var state = Assert.IsType>>(logInvocation.Arguments[2], exactMatch: false); var userIdValue = state.First(kvp => kvp.Key == "UserId").Value; Assert.Equal(enableSensitiveTelemetryData ? "user1" : "", userIdValue); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs index a77fc8a495..4966585211 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs @@ -57,7 +57,7 @@ IEnumerable echoMessages protected virtual IEnumerable GetEpilogueMessages(AgentRunOptions? options = null) { - return Enumerable.Empty(); + return []; } public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)