From 39332e6b707d084b531a0ec977ea5a2f77ab5c00 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Fri, 1 Aug 2025 10:53:43 +0100 Subject: [PATCH 1/5] prepare code to a2a sdk and asyncenumerable nuget update --- dotnet/Directory.Packages.props | 2 +- dotnet/samples/Concepts/Concepts.csproj | 5 +- .../MCPClient/MCPClient.csproj | 4 + .../ModelContextProtocolPlugin.csproj | 4 + .../ModelContextProtocolPluginAuth.csproj | 4 + .../GettingStarted/GettingStarted.csproj | 1 - .../GettingStartedWithAgents.csproj | 1 + .../GettingStartedWithProcesses.csproj | 1 + .../GettingStartedWithTextSearch.csproj | 5 +- .../GettingStartedWithVectorStores.csproj | 5 +- .../LearnResources/LearnResources.csproj | 1 - dotnet/src/.editorconfig | 1 + .../Abstractions/Agents.Abstractions.csproj | 1 - .../Agents/Abstractions/AggregatorChannel.cs | 17 +- .../Extensions/ChatHistoryExtensions.cs | 8 +- .../AzureAI/Internal/AgentThreadActions.cs | 22 ++- .../BedrockAgentInvokeExtensions.cs | 2 +- .../Functions/AgentKernelFunctionFactory.cs | 8 +- .../OpenAI/Internal/AssistantThreadActions.cs | 22 ++- .../OpenAI/Internal/ResponseThreadActions.cs | 4 +- dotnet/src/Agents/Orchestration/AgentActor.cs | 19 +- .../Abstractions/Runtime.Abstractions.csproj | 1 - .../Agents/UnitTests/Agents.UnitTests.csproj | 1 + dotnet/src/Agents/Yaml/Agents.Yaml.csproj | 1 - .../Process.IntegrationTestRunner.Dapr.csproj | 1 - .../AgentWithTextSearchProvider.cs | 1 - .../Memory/Milvus/MilvusMemoryStoreTests.cs | 16 +- .../IntegrationTests/IntegrationTests.csproj | 7 +- .../FunctionCalling/FunctionCallsProcessor.cs | 13 +- .../src/Linq/AsyncEnumerable.cs | 179 +++++++++++++++--- .../test/Linq/AsyncEnumerable.cs | 150 --------------- .../StructuredDataServiceExtensions.cs | 4 +- .../Memory/VolatileMemoryStoreTests.cs | 30 +-- .../Plugins.UnitTests.csproj | 1 + .../SemanticKernel.AotTests.csproj | 5 +- .../Data/TextSearchStore/TextSearchStore.cs | 2 +- .../AzureAISearch/AzureAISearchCollection.cs | 2 +- .../VectorData/Weaviate/WeaviateCollection.cs | 2 +- .../AzureAISearch.ConformanceTests.csproj | 4 + .../InMemory.ConformanceTests.csproj | 4 + .../Pinecone.ConformanceTests.csproj | 5 +- .../SqlServer.ConformanceTests.csproj | 1 - .../VectorData.ConformanceTests.csproj | 7 +- 43 files changed, 318 insertions(+), 256 deletions(-) delete mode 100644 dotnet/src/InternalUtilities/test/Linq/AsyncEnumerable.cs diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index f8b42e63f18b..4b21d6e07b80 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -85,7 +85,7 @@ - + diff --git a/dotnet/samples/Concepts/Concepts.csproj b/dotnet/samples/Concepts/Concepts.csproj index 97460d3e7c79..45b7f9b8372c 100644 --- a/dotnet/samples/Concepts/Concepts.csproj +++ b/dotnet/samples/Concepts/Concepts.csproj @@ -44,7 +44,6 @@ - @@ -97,6 +96,10 @@ + + + + diff --git a/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj b/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj index c523311cf38a..90e4d0213d2f 100644 --- a/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj +++ b/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj @@ -15,6 +15,10 @@ + + + + diff --git a/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj b/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj index bb6e26fa4df2..ee1e6f2a366b 100644 --- a/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj +++ b/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj @@ -15,6 +15,10 @@ + + + + diff --git a/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj b/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj index 35e7321db7d2..d3294c4b8e8a 100644 --- a/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj +++ b/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj @@ -16,6 +16,10 @@ + + + + diff --git a/dotnet/samples/GettingStarted/GettingStarted.csproj b/dotnet/samples/GettingStarted/GettingStarted.csproj index 0314496a6af6..451a78723ffa 100644 --- a/dotnet/samples/GettingStarted/GettingStarted.csproj +++ b/dotnet/samples/GettingStarted/GettingStarted.csproj @@ -47,7 +47,6 @@ - diff --git a/dotnet/samples/GettingStartedWithAgents/GettingStartedWithAgents.csproj b/dotnet/samples/GettingStartedWithAgents/GettingStartedWithAgents.csproj index 0abbddeba9de..ccb5299c45ef 100644 --- a/dotnet/samples/GettingStartedWithAgents/GettingStartedWithAgents.csproj +++ b/dotnet/samples/GettingStartedWithAgents/GettingStartedWithAgents.csproj @@ -30,6 +30,7 @@ + diff --git a/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj b/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj index 7244d2cb967b..913751f06cfd 100644 --- a/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj +++ b/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj @@ -41,6 +41,7 @@ + diff --git a/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj b/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj index 17f545356906..c8a97a999af0 100644 --- a/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj +++ b/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj @@ -34,12 +34,15 @@ - + + + + diff --git a/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj b/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj index 19c4e8d63cf9..d81bfe9433f0 100644 --- a/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj +++ b/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj @@ -32,12 +32,15 @@ - + + + + diff --git a/dotnet/samples/LearnResources/LearnResources.csproj b/dotnet/samples/LearnResources/LearnResources.csproj index 2ebd04438328..42bbf70dc69b 100644 --- a/dotnet/samples/LearnResources/LearnResources.csproj +++ b/dotnet/samples/LearnResources/LearnResources.csproj @@ -46,7 +46,6 @@ - diff --git a/dotnet/src/.editorconfig b/dotnet/src/.editorconfig index 7867acb76728..2fb82cdb11d5 100644 --- a/dotnet/src/.editorconfig +++ b/dotnet/src/.editorconfig @@ -1,5 +1,6 @@ # Setting errors for SDK projects under dotnet folder [*.cs] +dotnet_diagnostic.CS1998.severity = suggestion # Async method lacks 'await' operators and will run synchronously 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 diff --git a/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj b/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj index c9064186615c..a0fedaa605ec 100644 --- a/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj +++ b/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj @@ -26,7 +26,6 @@ - diff --git a/dotnet/src/Agents/Abstractions/AggregatorChannel.cs b/dotnet/src/Agents/Abstractions/AggregatorChannel.cs index a002e41351af..0ec6d339fda2 100644 --- a/dotnet/src/Agents/Abstractions/AggregatorChannel.cs +++ b/dotnet/src/Agents/Abstractions/AggregatorChannel.cs @@ -56,7 +56,11 @@ protected internal override IAsyncEnumerable GetHistoryAsync /// protected internal override async IAsyncEnumerable InvokeStreamingAsync(AggregatorAgent agent, IList messages, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - int initialCount = await this._chat.GetChatMessagesAsync(cancellationToken).CountAsync(cancellationToken).ConfigureAwait(false); + int initialCount = 0; + await foreach (var _ in this._chat.GetChatMessagesAsync(cancellationToken).ConfigureAwait(false)) + { + initialCount++; + } await foreach (StreamingChatMessageContent message in this._chat.InvokeStreamingAsync(cancellationToken).ConfigureAwait(false)) { @@ -66,12 +70,17 @@ protected internal override async IAsyncEnumerable } } - ChatMessageContent[] history = await this._chat.GetChatMessagesAsync(cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); - if (history.Length > initialCount) + List history = []; + await foreach (var item in this._chat.GetChatMessagesAsync(cancellationToken).ConfigureAwait(false)) + { + history.Add(item); + } + + if (history.Count > initialCount) { if (agent.Mode == AggregatorMode.Flat) { - for (int index = history.Length - 1; index >= initialCount; --index) + for (int index = history.Count - 1; index >= initialCount; --index) { messages.Add(history[index]); } diff --git a/dotnet/src/Agents/Abstractions/Extensions/ChatHistoryExtensions.cs b/dotnet/src/Agents/Abstractions/Extensions/ChatHistoryExtensions.cs index 0e93e3a3e2fd..672c297bf723 100644 --- a/dotnet/src/Agents/Abstractions/Extensions/ChatHistoryExtensions.cs +++ b/dotnet/src/Agents/Abstractions/Extensions/ChatHistoryExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. using System.Collections.Generic; -using System.Linq; using Microsoft.SemanticKernel.ChatCompletion; namespace Microsoft.SemanticKernel.Agents.Extensions; @@ -26,8 +25,11 @@ public static IEnumerable ToDescending(this ChatHistory hist /// Enumerates a history in descending order asynchronously. /// /// The chat history to sort. - public static IAsyncEnumerable ToDescendingAsync(this ChatHistory history) + public static async IAsyncEnumerable ToDescendingAsync(this ChatHistory history) { - return history.ToDescending().ToAsyncEnumerable(); + for (int index = history.Count; index > 0; --index) + { + yield return history[index - 1]; + } } } diff --git a/dotnet/src/Agents/AzureAI/Internal/AgentThreadActions.cs b/dotnet/src/Agents/AzureAI/Internal/AgentThreadActions.cs index 412642d88204..abf46e5785a0 100644 --- a/dotnet/src/Agents/AzureAI/Internal/AgentThreadActions.cs +++ b/dotnet/src/Agents/AzureAI/Internal/AgentThreadActions.cs @@ -181,7 +181,11 @@ public static async IAsyncEnumerable GetMessagesAsync(Persis throw new KernelException($"Agent Failure - Run terminated: {run.Status} [{run.Id}]: {run.LastError?.Message ?? "Unknown"}"); } - RunStep[] steps = await client.GetStepsAsync(run, cancellationToken: cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + List steps = []; + await foreach (var step in client.GetStepsAsync(run, cancellationToken: cancellationToken).ConfigureAwait(false)) + { + steps.Add(step); + } // Is tool action required? if (run.Status == RunStatus.RequiresAction) @@ -204,7 +208,7 @@ await functionProcessor.InvokeFunctionCallsAsync( functionOptions, kernel, isStreaming: false, - cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); // Capture function-call for message processing foreach (FunctionResultContent functionCall in functionResults) @@ -458,10 +462,14 @@ public static async IAsyncEnumerable InvokeStreamin if (run.Status == RunStatus.RequiresAction) { - RunStep[] activeSteps = - await client.GetStepsAsync(run, cancellationToken) - .Where(step => step.Status == RunStepStatus.InProgress) - .ToArrayAsync(cancellationToken).ConfigureAwait(false); + List activeSteps = []; + await foreach (var step in client.GetStepsAsync(run, cancellationToken).ConfigureAwait(false)) + { + if (step.Status == RunStepStatus.InProgress) + { + activeSteps.Add(step); + } + } // Capture map between the tool call and its associated step Dictionary toolMap = []; @@ -489,7 +497,7 @@ await functionProcessor.InvokeFunctionCallsAsync( functionOptions, kernel, isStreaming: true, - cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); // Process tool output ToolOutput[] toolOutputs = GenerateToolOutputs(functionResults); diff --git a/dotnet/src/Agents/Bedrock/Extensions/BedrockAgentInvokeExtensions.cs b/dotnet/src/Agents/Bedrock/Extensions/BedrockAgentInvokeExtensions.cs index 4c725ae52c16..5bdc214b4df7 100644 --- a/dotnet/src/Agents/Bedrock/Extensions/BedrockAgentInvokeExtensions.cs +++ b/dotnet/src/Agents/Bedrock/Extensions/BedrockAgentInvokeExtensions.cs @@ -44,7 +44,7 @@ public static async IAsyncEnumerable InternalInvokeAsync( } List functionCallContents = []; - await foreach (var responseEvent in invokeAgentResponse.Completion.ToAsyncEnumerable().ConfigureAwait(false)) + foreach (var responseEvent in invokeAgentResponse.Completion) { if (responseEvent is BedrockAgentRuntimeEventStreamException bedrockAgentRuntimeEventStreamException) { diff --git a/dotnet/src/Agents/Core/Functions/AgentKernelFunctionFactory.cs b/dotnet/src/Agents/Core/Functions/AgentKernelFunctionFactory.cs index 5c45d2a8d41f..42f9058ab516 100644 --- a/dotnet/src/Agents/Core/Functions/AgentKernelFunctionFactory.cs +++ b/dotnet/src/Agents/Core/Functions/AgentKernelFunctionFactory.cs @@ -53,8 +53,12 @@ async Task InvokeAgentAsync(Kernel kernel, KernelFunction functi } var response = agent.InvokeAsync(new ChatMessageContent(AuthorRole.User, queryString), null, options, cancellationToken); - var responseItems = await response.ToArrayAsync(cancellationToken: cancellationToken).ConfigureAwait(false); - var chatMessages = responseItems.Select(i => i.Message).ToArray(); + + List chatMessages = []; + await foreach (var item in response.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + chatMessages.Add(item.Message); + } return new FunctionResult(function, chatMessages, kernel.Culture); } diff --git a/dotnet/src/Agents/OpenAI/Internal/AssistantThreadActions.cs b/dotnet/src/Agents/OpenAI/Internal/AssistantThreadActions.cs index 6912d53d4c21..4cbe453baf65 100644 --- a/dotnet/src/Agents/OpenAI/Internal/AssistantThreadActions.cs +++ b/dotnet/src/Agents/OpenAI/Internal/AssistantThreadActions.cs @@ -164,7 +164,11 @@ public static async IAsyncEnumerable GetMessagesAsync(Assist throw new KernelException($"Agent Failure - Run terminated: {run.Status} [{run.Id}]: {run.LastError?.Message ?? "Unknown"}"); } - RunStep[] steps = await client.GetRunStepsAsync(run.ThreadId, run.Id, cancellationToken: cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + List steps = []; + await foreach (var step in client.GetRunStepsAsync(run.ThreadId, run.Id, cancellationToken: cancellationToken).ConfigureAwait(false)) + { + steps.Add(step); + } // Is tool action required? if (run.Status == RunStatus.RequiresAction) @@ -187,7 +191,7 @@ await functionProcessor.InvokeFunctionCallsAsync( functionOptions, kernel, isStreaming: false, - cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); // Capture function-call for message processing foreach (FunctionResultContent functionCall in functionResults) @@ -453,10 +457,14 @@ public static async IAsyncEnumerable InvokeStreamin if (run.Status == RunStatus.RequiresAction) { - RunStep[] activeSteps = - await client.GetRunStepsAsync(run.ThreadId, run.Id, cancellationToken: cancellationToken) - .Where(step => step.Status == RunStepStatus.InProgress) - .ToArrayAsync(cancellationToken).ConfigureAwait(false); + List activeSteps = []; + await foreach (var step in client.GetRunStepsAsync(run.ThreadId, run.Id, cancellationToken: cancellationToken).ConfigureAwait(false)) + { + if (step.Status == RunStepStatus.InProgress) + { + activeSteps.Add(step); + } + } // Capture map between the tool call and its associated step Dictionary toolMap = []; @@ -483,7 +491,7 @@ await functionProcessor.InvokeFunctionCallsAsync( functionOptions, kernel, isStreaming: true, - cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); // Process tool output ToolOutput[] toolOutputs = GenerateToolOutputs(functionResults); diff --git a/dotnet/src/Agents/OpenAI/Internal/ResponseThreadActions.cs b/dotnet/src/Agents/OpenAI/Internal/ResponseThreadActions.cs index 2214796055f4..a7c7cc0b8cc4 100644 --- a/dotnet/src/Agents/OpenAI/Internal/ResponseThreadActions.cs +++ b/dotnet/src/Agents/OpenAI/Internal/ResponseThreadActions.cs @@ -88,7 +88,7 @@ await functionProcessor.InvokeFunctionCallsAsync( functionOptions, agent.GetKernel(options), isStreaming: false, - cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); var functionOutputItems = functionResults.Select(fr => ResponseItem.CreateFunctionCallOutputItem(fr.CallId, fr.Result?.ToString() ?? string.Empty)).ToList(); // If store is enabled we only need to send the function output items @@ -259,7 +259,7 @@ await functionProcessor.InvokeFunctionCallsAsync( functionOptions, agent.GetKernel(options), isStreaming: true, - cancellationToken).ToArrayAsync(cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); var functionOutputItems = functionResults.Select(fr => ResponseItem.CreateFunctionCallOutputItem(fr.CallId, fr.Result?.ToString() ?? string.Empty)).ToList(); // If store is enabled we only need to send the function output items diff --git a/dotnet/src/Agents/Orchestration/AgentActor.cs b/dotnet/src/Agents/Orchestration/AgentActor.cs index 5aac15c1d17c..63ef2fdff48c 100644 --- a/dotnet/src/Agents/Orchestration/AgentActor.cs +++ b/dotnet/src/Agents/Orchestration/AgentActor.cs @@ -122,14 +122,19 @@ async Task HandleMessage(ChatMessageContent message) private async Task InvokeAsync(IList input, AgentInvokeOptions options, CancellationToken cancellationToken) { - AgentResponseItem? lastResponse = - await this.Agent.InvokeAsync( - input, - this.Thread, - options, - cancellationToken).LastOrDefaultAsync(cancellationToken).ConfigureAwait(false); + var last = default(AgentResponseItem)!; + var hasLast = false; + + await foreach (var item in this.Agent.InvokeAsync(input, this.Thread, options, cancellationToken).ConfigureAwait(false)) + { + hasLast = true; + last = item; + } - this.Thread ??= lastResponse?.Thread; + if (this.Thread is null && hasLast) + { + this.Thread = last.Thread; + } } private async Task InvokeStreamingAsync(IList input, AgentInvokeOptions options, CancellationToken cancellationToken) diff --git a/dotnet/src/Agents/Runtime/Abstractions/Runtime.Abstractions.csproj b/dotnet/src/Agents/Runtime/Abstractions/Runtime.Abstractions.csproj index 2e32ccad3c7a..1c734a781259 100644 --- a/dotnet/src/Agents/Runtime/Abstractions/Runtime.Abstractions.csproj +++ b/dotnet/src/Agents/Runtime/Abstractions/Runtime.Abstractions.csproj @@ -32,7 +32,6 @@ - diff --git a/dotnet/src/Agents/UnitTests/Agents.UnitTests.csproj b/dotnet/src/Agents/UnitTests/Agents.UnitTests.csproj index e0a5d2938e1e..0d1df7473dc4 100644 --- a/dotnet/src/Agents/UnitTests/Agents.UnitTests.csproj +++ b/dotnet/src/Agents/UnitTests/Agents.UnitTests.csproj @@ -24,6 +24,7 @@ + diff --git a/dotnet/src/Agents/Yaml/Agents.Yaml.csproj b/dotnet/src/Agents/Yaml/Agents.Yaml.csproj index 7ef8ad4c8aaa..e43efde96a49 100644 --- a/dotnet/src/Agents/Yaml/Agents.Yaml.csproj +++ b/dotnet/src/Agents/Yaml/Agents.Yaml.csproj @@ -33,7 +33,6 @@ - diff --git a/dotnet/src/Experimental/Process.IntegrationTestRunner.Dapr/Process.IntegrationTestRunner.Dapr.csproj b/dotnet/src/Experimental/Process.IntegrationTestRunner.Dapr/Process.IntegrationTestRunner.Dapr.csproj index 8029ae2ff601..8e72413d778f 100644 --- a/dotnet/src/Experimental/Process.IntegrationTestRunner.Dapr/Process.IntegrationTestRunner.Dapr.csproj +++ b/dotnet/src/Experimental/Process.IntegrationTestRunner.Dapr/Process.IntegrationTestRunner.Dapr.csproj @@ -31,7 +31,6 @@ - diff --git a/dotnet/src/IntegrationTests/Agents/CommonInterfaceConformance/AgentWithTextSearchProviderConformance/AgentWithTextSearchProvider.cs b/dotnet/src/IntegrationTests/Agents/CommonInterfaceConformance/AgentWithTextSearchProviderConformance/AgentWithTextSearchProvider.cs index 38db0fa6e40a..4d350564b7de 100644 --- a/dotnet/src/IntegrationTests/Agents/CommonInterfaceConformance/AgentWithTextSearchProviderConformance/AgentWithTextSearchProvider.cs +++ b/dotnet/src/IntegrationTests/Agents/CommonInterfaceConformance/AgentWithTextSearchProviderConformance/AgentWithTextSearchProvider.cs @@ -41,7 +41,6 @@ public abstract class AgentWithTextSearchProvider(Func creat public async Task TextSearchBehaviorStateIsUsedByAgentInternalAsync(string question, string expectedResult, params string[] ragResults) { // Arrange - ragResults.Select(x => new TextSearchResult(x)).ToAsyncEnumerable(); var mockTextSearch = new Mock(); mockTextSearch.Setup(x => x.GetTextSearchResultsAsync( It.IsAny(), diff --git a/dotnet/src/IntegrationTests/Connectors/Memory/Milvus/MilvusMemoryStoreTests.cs b/dotnet/src/IntegrationTests/Connectors/Memory/Milvus/MilvusMemoryStoreTests.cs index 4ee21728816a..c5ec1ccbb72b 100644 --- a/dotnet/src/IntegrationTests/Connectors/Memory/Milvus/MilvusMemoryStoreTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/Memory/Milvus/MilvusMemoryStoreTests.cs @@ -43,7 +43,7 @@ public async Task GetCollectionsAsync() await this.Store.CreateCollectionAsync("collection1"); await this.Store.CreateCollectionAsync("collection2"); - List collections = this.Store.GetCollectionsAsync().ToEnumerable().ToList(); + List collections = await this.Store.GetCollectionsAsync().ToListAsync(); Assert.Contains("collection1", collections); Assert.Contains("collection2", collections); } @@ -112,7 +112,7 @@ public async Task GetBatchAsync(bool withEmbeddings) await this.Store.CreateCollectionAsync(CollectionName); await this.InsertSampleDataAsync(); - List records = this.Store.GetBatchAsync(CollectionName, ["Some id", "Some other id"], withEmbeddings: withEmbeddings).ToEnumerable().ToList(); + List records = await this.Store.GetBatchAsync(CollectionName, ["Some id", "Some other id"], withEmbeddings: withEmbeddings).ToListAsync(); Assert.Collection(records.OrderBy(r => r.Metadata.Id), r => @@ -185,7 +185,7 @@ public async Task GetNearestMatchesAsync(bool withEmbeddings) await Task.Delay(1000); List<(MemoryRecord Record, double SimilarityScore)> results = - this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToEnumerable().ToList(); + await this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToListAsync(); Assert.All(results, t => Assert.True(t.SimilarityScore > 0)); @@ -234,7 +234,7 @@ public async Task GetNearestMatchesWithMetricTypeAsync(bool withEmbeddings) //Search with Ip metric, run correctly List<(MemoryRecord Record, double SimilarityScore)> ipResults = - this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToEnumerable().ToList(); + await this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToListAsync(); Assert.All(ipResults, t => Assert.True(t.SimilarityScore > 0)); @@ -242,7 +242,7 @@ public async Task GetNearestMatchesWithMetricTypeAsync(bool withEmbeddings) this.Store = new(this._milvusFixture.Host, vectorSize: 5, port: this._milvusFixture.Port, metricType: SimilarityMetricType.Cosine, consistencyLevel: ConsistencyLevel.Strong); //An exception will be thrown here, the exception message includes "metric type not match" - MilvusException milvusException = Assert.Throws(() => this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToEnumerable().ToList()); + MilvusException milvusException = await Assert.ThrowsAsync(async () => await this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToListAsync()); Assert.NotNull(milvusException); @@ -256,7 +256,7 @@ public async Task GetNearestMatchesWithMetricTypeAsync(bool withEmbeddings) //Search with Ip metric, run correctly List<(MemoryRecord Record, double SimilarityScore)> cosineResults = - this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToEnumerable().ToList(); + await this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, withEmbeddings: withEmbeddings).ToListAsync(); Assert.All(cosineResults, t => Assert.True(t.SimilarityScore > 0)); } @@ -268,12 +268,12 @@ public async Task GetNearestMatchesWithMinRelevanceScoreAsync() await this.InsertSampleDataAsync(); List<(MemoryRecord Record, double SimilarityScore)> results = - this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2).ToEnumerable().ToList(); + await this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2).ToListAsync(); string firstId = results[0].Record.Metadata.Id; double firstSimilarityScore = results[0].SimilarityScore; - results = this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, minRelevanceScore: firstSimilarityScore + 0.0001).ToEnumerable().ToList(); + results = await this.Store.GetNearestMatchesAsync(CollectionName, new[] { 5f, 6f, 7f, 8f, 9f }, limit: 2, minRelevanceScore: firstSimilarityScore + 0.0001).ToListAsync(); Assert.DoesNotContain(firstId, results.Select(r => r.Record.Metadata.Id)); } diff --git a/dotnet/src/IntegrationTests/IntegrationTests.csproj b/dotnet/src/IntegrationTests/IntegrationTests.csproj index 0c223043dcbb..973ab1c49caa 100644 --- a/dotnet/src/IntegrationTests/IntegrationTests.csproj +++ b/dotnet/src/IntegrationTests/IntegrationTests.csproj @@ -42,7 +42,6 @@ - @@ -97,6 +96,10 @@ + + + + Always @@ -199,7 +202,7 @@ - + Always diff --git a/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs b/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs index 55a300769812..e82800d295b9 100644 --- a/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs +++ b/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs @@ -246,16 +246,17 @@ public FunctionCallsProcessor(ILogger? logger = null) /// Boolean flag which indicates whether an operation is invoked within streaming or non-streaming mode. /// The to monitor for cancellation requests. /// Last chat history message if function invocation filter requested processing termination, otherwise null. - public async IAsyncEnumerable InvokeFunctionCallsAsync( + public async ValueTask InvokeFunctionCallsAsync( ChatMessageContent chatMessageContent, Func checkIfFunctionAdvertised, FunctionChoiceBehaviorOptions options, Kernel kernel, bool isStreaming, - [EnumeratorCancellation] CancellationToken cancellationToken) + CancellationToken cancellationToken) { FunctionCallContent[] functionCalls = FunctionCallContent.GetFunctionCalls(chatMessageContent).ToArray(); ChatHistory history = [chatMessageContent]; + List results = []; this._logger.LogFunctionCalls(functionCalls); @@ -270,7 +271,7 @@ public async IAsyncEnumerable InvokeFunctionCallsAsync( // Check if the function call is valid to execute. if (!TryValidateFunctionCall(functionCall, checkIfFunctionAdvertised, kernel, out KernelFunction? function, out string? errorMessage)) { - yield return this.GenerateResultContent(functionCall, result: null, errorMessage); + results.Add(this.GenerateResultContent(functionCall, result: null, errorMessage)); continue; } @@ -298,10 +299,12 @@ public async IAsyncEnumerable InvokeFunctionCallsAsync( // Wait for all of the function invocations to complete, then add the results to the chat, but stop when we hit a // function for which termination was requested. FunctionResultContext[] resultContexts = await Task.WhenAll(functionTasks).ConfigureAwait(false); - foreach (FunctionResultContext resultContext in resultContexts) + foreach (var context in resultContexts) { - yield return this.GenerateResultContent(resultContext); + results.Add(this.GenerateResultContent(context)); } + + return [.. results]; } private static bool TryValidateFunctionCall( diff --git a/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs b/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs index a85a509d1980..67649bc3b550 100644 --- a/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs +++ b/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs @@ -5,9 +5,10 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel; -// Used for compatibility with System.Linq.Async Nuget pkg +#pragma warning disable IDE1006 // Naming Styles + +// Used for compatibility with System.Linq.AsyncEnumerable Nuget pkg namespace System.Linq; [ExcludeFromCodeCoverage] @@ -33,7 +34,6 @@ public static IEnumerable ToEnumerable(this IAsyncEnumerable source, Ca } #pragma warning restore VSTHRD002 // Avoid problematic synchronous waits -#pragma warning disable IDE1006 // Naming rule violation: Missing suffix: 'Async' #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public static async IAsyncEnumerable ToAsyncEnumerable(this IEnumerable source) { @@ -43,7 +43,6 @@ public static async IAsyncEnumerable ToAsyncEnumerable(this IEnumerable } } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously -#pragma warning restore IDE1006 // Naming rule violation: Missing suffix: 'Async' public static async ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) { @@ -105,6 +104,81 @@ public static async ValueTask CountAsync(this IAsyncEnumerable source return count; } + public static async IAsyncEnumerable Cast(this IAsyncEnumerable source) + { + await foreach (var item in source.ConfigureAwait(false)) + { + yield return (T)item; + } + } + + public static async IAsyncEnumerable Reverse(this IAsyncEnumerable source, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + T[] results = await source.ToArrayAsync(cancellationToken).ConfigureAwait(false); + for (int i = results.Length - 1; i >= 0; i--) + { + yield return results[i]; + } + } + + public static async ValueTask FirstAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + return item; + } + + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static async ValueTask LastAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + var enumerator = source.GetAsyncEnumerator(cancellationToken); + try + { + if (await enumerator.MoveNextAsync().ConfigureAwait(false)) + { + var last = enumerator.Current; + while (await enumerator.MoveNextAsync().ConfigureAwait(false)) + { + last = enumerator.Current; + } + + return last; + } + + throw new InvalidOperationException("Sequence contains no elements"); + } + finally + { + await enumerator.DisposeAsync().ConfigureAwait(false); + } + } + + public static async ValueTask SingleAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + var enumerator = source.GetAsyncEnumerator(cancellationToken); + try + { + if (await enumerator.MoveNextAsync().ConfigureAwait(false)) + { + var result = enumerator.Current; + if (await enumerator.MoveNextAsync().ConfigureAwait(false)) + { + throw new InvalidOperationException("Sequence contains more than one element"); + } + + return result; + } + + throw new InvalidOperationException("Sequence contains no elements"); + } + finally + { + await enumerator.DisposeAsync().ConfigureAwait(false); + } + } + /// /// Determines whether any element of an async-enumerable sequence satisfies a condition. /// @@ -113,30 +187,37 @@ public static async ValueTask CountAsync(this IAsyncEnumerable source /// A function to test each element for a condition. /// The optional cancellation token to be used for cancelling the sequence at any time. /// An async-enumerable sequence containing a single element determining whether any elements in the source sequence pass the test in the specified predicate. - /// or is null. /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. - public static ValueTask AnyAsync(this IAsyncEnumerable source, Func predicate, CancellationToken cancellationToken = default) + public static async ValueTask AnyAsync(this IAsyncEnumerable source, Func predicate, CancellationToken cancellationToken = default) { - Verify.NotNull(source); - Verify.NotNull(predicate); - - return Core(source, predicate, cancellationToken); - - static async ValueTask Core(IAsyncEnumerable source, Func predicate, CancellationToken cancellationToken) + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + if (predicate(item)) { - if (predicate(item)) - { - return true; - } + return true; } - - return false; } + + return false; } -#pragma warning disable IDE1006 // Naming rule violation: Missing suffix: 'Async' + /// + /// Determines whether any element of an async-enumerable sequence satisfies a condition. + /// + /// The type of the elements in the source sequence. + /// An async-enumerable sequence whose elements to apply the predicate to. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element determining whether any elements in the source sequence pass the test in the specified predicate. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static async ValueTask AnyAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + return true; + } + + return false; + } /// /// Projects each element of an into a new form by incorporating @@ -157,7 +238,7 @@ static async ValueTask Core(IAsyncEnumerable source, Func /// Thrown when the source or selector is null. - public static async IAsyncEnumerable SelectAsync( + public static async IAsyncEnumerable Select( this IAsyncEnumerable source, Func selector, [EnumeratorCancellation] CancellationToken cancellationToken = default) @@ -168,7 +249,61 @@ public static async IAsyncEnumerable SelectAsync( } } -#pragma warning restore IDE1006 // Naming rule violation: Missing suffix: 'Async' + /// Sorts the elements of a sequence in ascending order. + /// The type of the elements of . + /// The type of the key returned by . + /// A sequence of values to order. + /// A function to extract a key from an element. + /// An whose elements are sorted according to a key. + public static async IAsyncEnumerable OrderBy(this IAsyncEnumerable source, Func keySelector) + { + var results = await source.ToArrayAsync().ConfigureAwait(false); + var keys = results.Select(keySelector).ToArray(); + Array.Sort(keys, results); + foreach (var result in results) + { + yield return result; + } + } + + /// + /// Creates an array from an async-enumerable sequence. + /// + /// The type of the elements in the source sequence. + /// The source async-enumerable sequence to get an array of elements for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with an array containing all the elements of the source sequence. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static async ValueTask ToArrayAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + List results = []; + await foreach (var result in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + results.Add(result); + } + + return results.ToArray(); + } + + /// + /// Filters the elements of an async-enumerable sequence based on a predicate. + /// + /// The type of the elements in the source sequence. + /// An async-enumerable sequence whose elements to filter. + /// A function to test each source element for a condition. + /// An async-enumerable sequence that contains elements from the input sequence that satisfy the condition. + /// or is null. + public static async IAsyncEnumerable Where(this IAsyncEnumerable source, Func predicate) + { + await foreach (var result in source.ConfigureAwait(false)) + { + if (predicate(result)) + { + yield return result; + } + } + } private sealed class EmptyAsyncEnumerable : IAsyncEnumerable, IAsyncEnumerator { diff --git a/dotnet/src/InternalUtilities/test/Linq/AsyncEnumerable.cs b/dotnet/src/InternalUtilities/test/Linq/AsyncEnumerable.cs deleted file mode 100644 index ff4b967343a8..000000000000 --- a/dotnet/src/InternalUtilities/test/Linq/AsyncEnumerable.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -// Used for compatibility with System.Linq.Async Nuget pkg -namespace System.Linq; - -internal static class AsyncEnumerable -{ - public static IAsyncEnumerable Empty() => EmptyAsyncEnumerable.Instance; - -#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits - public static IEnumerable ToEnumerable(this IAsyncEnumerable source, CancellationToken cancellationToken = default) - { - var enumerator = source.GetAsyncEnumerator(cancellationToken); - try - { - while (enumerator.MoveNextAsync().AsTask().GetAwaiter().GetResult()) - { - yield return enumerator.Current; - } - } - finally - { - enumerator.DisposeAsync().AsTask().GetAwaiter().GetResult(); - } - } -#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits - -#pragma warning disable IDE1006 // Naming rule violation: Missing suffix: 'Async' -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public static async IAsyncEnumerable ToAsyncEnumerable(this IEnumerable source) - { - foreach (var item in source) - { - yield return item; - } - } -#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously -#pragma warning restore IDE1006 // Naming rule violation: Missing suffix: 'Async' - - public static async ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) - { - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - return item; - } - - return default; - } - - public static async ValueTask LastOrDefaultAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) - { - var last = default(T)!; // NB: Only matters when hasLast is set to true. - var hasLast = false; - - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - hasLast = true; - last = item; - } - - return hasLast ? last! : default; - } - - public static async ValueTask> ToListAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) - { - var result = new List(); - - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - result.Add(item); - } - - return result; - } - - public static async ValueTask ContainsAsync(this IAsyncEnumerable source, T value) - { - await foreach (var item in source.ConfigureAwait(false)) - { - if (EqualityComparer.Default.Equals(item, value)) - { - return true; - } - } - - return false; - } - - public static async ValueTask CountAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) - { - int count = 0; - await foreach (var _ in source.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - checked { count++; } - } - - return count; - } - - /// - /// Determines whether any element of an async-enumerable sequence satisfies a condition. - /// - /// The type of the elements in the source sequence. - /// An async-enumerable sequence whose elements to apply the predicate to. - /// A function to test each element for a condition. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// An async-enumerable sequence containing a single element determining whether any elements in the source sequence pass the test in the specified predicate. - /// or is null. - /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. - public static ValueTask AnyAsync(this IAsyncEnumerable source, Func predicate, CancellationToken cancellationToken = default) - { - if (source is null) - { - throw new ArgumentNullException(nameof(source)); - } - - if (predicate is null) - { - throw new ArgumentNullException(nameof(predicate)); - } - - return Core(source, predicate, cancellationToken); - - static async ValueTask Core(IAsyncEnumerable source, Func predicate, CancellationToken cancellationToken) - { - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - if (predicate(item)) - { - return true; - } - } - - return false; - } - } - - private sealed class EmptyAsyncEnumerable : IAsyncEnumerable, IAsyncEnumerator - { - public static readonly EmptyAsyncEnumerable Instance = new(); - public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => this; - public ValueTask MoveNextAsync() => new(false); - public T Current => default!; - public ValueTask DisposeAsync() => default; - } -} diff --git a/dotnet/src/Plugins/Plugins.StructuredData.EntityFramework/StructuredDataServiceExtensions.cs b/dotnet/src/Plugins/Plugins.StructuredData.EntityFramework/StructuredDataServiceExtensions.cs index f88b2ab2f607..f96f306c9a90 100644 --- a/dotnet/src/Plugins/Plugins.StructuredData.EntityFramework/StructuredDataServiceExtensions.cs +++ b/dotnet/src/Plugins/Plugins.StructuredData.EntityFramework/StructuredDataServiceExtensions.cs @@ -91,10 +91,10 @@ public static KernelFunction CreateSelectFunction( ReturnParameter = new() { ParameterType = typeof(IList) }, }; - Task> SelectAsync(string? filter = null, CancellationToken cancellationToken = default) + Task> Select(string? filter = null, CancellationToken cancellationToken = default) => Task.FromResult>(service.Select(filter).ToList()); - return KernelFunctionFactory.CreateFromMethod(SelectAsync, options); + return KernelFunctionFactory.CreateFromMethod(Select, options); } /// diff --git a/dotnet/src/Plugins/Plugins.UnitTests/Memory/VolatileMemoryStoreTests.cs b/dotnet/src/Plugins/Plugins.UnitTests/Memory/VolatileMemoryStoreTests.cs index d087cc49774e..90d01f8547d2 100644 --- a/dotnet/src/Plugins/Plugins.UnitTests/Memory/VolatileMemoryStoreTests.cs +++ b/dotnet/src/Plugins/Plugins.UnitTests/Memory/VolatileMemoryStoreTests.cs @@ -70,7 +70,7 @@ public async Task ItCanCreateAndGetCollectionAsync() var collections = this._db.GetCollectionsAsync(); // Assert - Assert.NotEmpty(collections.ToEnumerable()); + Assert.NotEmpty(await collections.ToArrayAsync()); Assert.True(await collections.ContainsAsync(collection)); } @@ -258,13 +258,13 @@ public async Task ItCanListAllDatabaseCollectionsAsync() await this._db.CreateCollectionAsync(testCollections[2]); // Act - var collections = this._db.GetCollectionsAsync().ToEnumerable(); + var collections = await this._db.GetCollectionsAsync().ToArrayAsync(); #pragma warning disable CA1851 // Possible multiple enumerations of 'IEnumerable' collection // Assert Assert.NotNull(collections); - Assert.True(collections.Any(), "Collections is empty"); - Assert.Equal(3, collections.Count()); + Assert.True(collections.Length != 0, "Collections is empty"); + Assert.Equal(3, collections.Length); Assert.True(collections.Contains(testCollections[0]), $"Collections does not contain the newly-created collection {testCollections[0]}"); Assert.True(collections.Contains(testCollections[1]), @@ -325,7 +325,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() // Act double threshold = -1; - var topNResults = this._db.GetNearestMatchesAsync(collection, compareEmbedding, limit: topN, minRelevanceScore: threshold).ToEnumerable().ToArray(); + var topNResults = await this._db.GetNearestMatchesAsync(collection, compareEmbedding, limit: topN, minRelevanceScore: threshold).ToArrayAsync(); // Assert Assert.Equal(topN, topNResults.Length); @@ -475,7 +475,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() } // Act - var topNResults = this._db.GetNearestMatchesAsync(collection, compareEmbedding, limit: topN, minRelevanceScore: 0.75).ToEnumerable().ToArray(); + var topNResults = await this._db.GetNearestMatchesAsync(collection, compareEmbedding, limit: topN, minRelevanceScore: 0.75).ToArrayAsync(); IEnumerable topNKeys = topNResults.Select(x => x.Item1.Key).ToImmutableSortedSet(); // Assert @@ -501,12 +501,12 @@ public async Task ItCanBatchUpsertRecordsAsync() // Act var keys = this._db.UpsertBatchAsync(collection, records); - var resultRecords = this._db.GetBatchAsync(collection, keys.ToEnumerable()); + var resultRecords = this._db.GetBatchAsync(collection, await keys.ToArrayAsync()); // Assert Assert.NotNull(keys); - Assert.Equal(numRecords, keys.ToEnumerable().Count()); - Assert.Equal(numRecords, resultRecords.ToEnumerable().Count()); + Assert.Equal(numRecords, (await keys.ToArrayAsync()).Length); + Assert.Equal(numRecords, (await resultRecords.ToArrayAsync()).Length); } [Fact] @@ -521,12 +521,12 @@ public async Task ItCanBatchGetRecordsAsync() var keys = this._db.UpsertBatchAsync(collection, records); // Act - var results = this._db.GetBatchAsync(collection, keys.ToEnumerable()); + var results = this._db.GetBatchAsync(collection, await keys.ToArrayAsync()); // Assert Assert.NotNull(keys); Assert.NotNull(results); - Assert.Equal(numRecords, results.ToEnumerable().Count()); + Assert.Equal(numRecords, (await results.ToArrayAsync()).Length); } [Fact] @@ -559,9 +559,9 @@ public async Task ItCanBatchRemoveRecordsAsync() public async Task CollectionsCanBeDeletedAsync() { // Arrange - var collections = this._db.GetCollectionsAsync().ToEnumerable(); + var collections = await this._db.GetCollectionsAsync().ToArrayAsync(); #pragma warning disable CA1851 // Possible multiple enumerations of 'IEnumerable' collection - int numCollections = collections.Count(); + int numCollections = collections.Length; Assert.True(numCollections == this._collectionNum); // Act @@ -571,8 +571,8 @@ public async Task CollectionsCanBeDeletedAsync() } // Assert - collections = this._db.GetCollectionsAsync().ToEnumerable(); - numCollections = collections.Count(); + collections = await this._db.GetCollectionsAsync().ToArrayAsync(); + numCollections = collections.Length; Assert.Equal(0, numCollections); this._collectionNum = 0; } diff --git a/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj b/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj index c1661fedddaf..2f394d320a3d 100644 --- a/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj +++ b/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj @@ -40,6 +40,7 @@ + diff --git a/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj b/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj index 9da3b544ac88..58a7066fcab6 100644 --- a/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj +++ b/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj @@ -18,9 +18,12 @@ - + + + + diff --git a/dotnet/src/SemanticKernel.Core/Data/TextSearchStore/TextSearchStore.cs b/dotnet/src/SemanticKernel.Core/Data/TextSearchStore/TextSearchStore.cs index 9af4c7c37c39..ed2314eb8b1e 100644 --- a/dotnet/src/SemanticKernel.Core/Data/TextSearchStore/TextSearchStore.cs +++ b/dotnet/src/SemanticKernel.Core/Data/TextSearchStore/TextSearchStore.cs @@ -262,7 +262,7 @@ private async Task>> SearchInternalAsyn // Retrieve the documents from the search results. var searchResponseDocs = await searchResult - .SelectAsync(x => x.Record, cancellationToken) + .Select(x => x.Record, cancellationToken) .ToListAsync(cancellationToken) .ConfigureAwait(false); diff --git a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollection.cs b/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollection.cs index f83ab94f4beb..9792adfdfffc 100644 --- a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollection.cs +++ b/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollection.cs @@ -376,7 +376,7 @@ public override IAsyncEnumerable GetAsync(Expression result.Record, cancellationToken); + .Select(result => result.Record, cancellationToken); } #region Search diff --git a/dotnet/src/VectorData/Weaviate/WeaviateCollection.cs b/dotnet/src/VectorData/Weaviate/WeaviateCollection.cs index f902665d3af7..8eafc3548a07 100644 --- a/dotnet/src/VectorData/Weaviate/WeaviateCollection.cs +++ b/dotnet/src/VectorData/Weaviate/WeaviateCollection.cs @@ -388,7 +388,7 @@ public override IAsyncEnumerable GetAsync(Expression result.Record, cancellationToken: cancellationToken); + .Select(result => result.Record, cancellationToken: cancellationToken); } /// diff --git a/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj b/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj index ea2f4b1e31c9..0bb373f4a0b5 100644 --- a/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj +++ b/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj @@ -29,6 +29,10 @@ + + + + Always diff --git a/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj b/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj index d6444400097f..f5abae8b0ece 100644 --- a/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj +++ b/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj @@ -18,6 +18,10 @@ + + + + diff --git a/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj b/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj index 49a049f8bfc8..f9d6eeac9fac 100644 --- a/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj +++ b/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj @@ -25,7 +25,6 @@ - @@ -35,6 +34,10 @@ + + + + diff --git a/dotnet/test/VectorData/SqlServer.ConformanceTests/SqlServer.ConformanceTests.csproj b/dotnet/test/VectorData/SqlServer.ConformanceTests/SqlServer.ConformanceTests.csproj index dd13f2a57b9c..d9d72973ee15 100644 --- a/dotnet/test/VectorData/SqlServer.ConformanceTests/SqlServer.ConformanceTests.csproj +++ b/dotnet/test/VectorData/SqlServer.ConformanceTests/SqlServer.ConformanceTests.csproj @@ -25,7 +25,6 @@ - diff --git a/dotnet/test/VectorData/VectorData.ConformanceTests/VectorData.ConformanceTests.csproj b/dotnet/test/VectorData/VectorData.ConformanceTests/VectorData.ConformanceTests.csproj index dd005b212c09..88b921ee5992 100644 --- a/dotnet/test/VectorData/VectorData.ConformanceTests/VectorData.ConformanceTests.csproj +++ b/dotnet/test/VectorData/VectorData.ConformanceTests/VectorData.ConformanceTests.csproj @@ -12,12 +12,15 @@ - - + + + + + From fefe09b0014b376005d561ccbde419d5fafbf706 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Fri, 1 Aug 2025 12:31:23 +0100 Subject: [PATCH 2/5] suppress false positive warning --- dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs b/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs index 67649bc3b550..813fd917e220 100644 --- a/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs +++ b/dotnet/src/InternalUtilities/src/Linq/AsyncEnumerable.cs @@ -1,10 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. +#pragma warning disable IDE0005 // Using directive is unnecessary using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +#pragma warning restore IDE0005 // Using directive is unnecessary #pragma warning disable IDE1006 // Naming Styles From dc4ccd0f3c93007892f7926838b63533e0a2b15e Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Fri, 1 Aug 2025 12:52:50 +0100 Subject: [PATCH 3/5] remove unnecessury namespace --- dotnet/src/Agents/Abstractions/AggregatorChannel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/dotnet/src/Agents/Abstractions/AggregatorChannel.cs b/dotnet/src/Agents/Abstractions/AggregatorChannel.cs index 0ec6d339fda2..9362846d64cd 100644 --- a/dotnet/src/Agents/Abstractions/AggregatorChannel.cs +++ b/dotnet/src/Agents/Abstractions/AggregatorChannel.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading; From 75927145393ce0afeb0068a52b66454c94feb860 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Fri, 1 Aug 2025 14:21:16 +0100 Subject: [PATCH 4/5] remove unused namespaces --- dotnet/src/Agents/Orchestration/AgentActor.cs | 1 - .../connectors/AI/FunctionCalling/FunctionCallsProcessor.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/dotnet/src/Agents/Orchestration/AgentActor.cs b/dotnet/src/Agents/Orchestration/AgentActor.cs index 54adc0daea43..962dc4327241 100644 --- a/dotnet/src/Agents/Orchestration/AgentActor.cs +++ b/dotnet/src/Agents/Orchestration/AgentActor.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs b/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs index e82800d295b9..ddca98c7c053 100644 --- a/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs +++ b/dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime.CompilerServices; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; From ac49817ee200079c2ab14ab67a4e6bcc3276be87 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Tue, 5 Aug 2025 09:03:56 +0100 Subject: [PATCH 5/5] address pr review feedback --- .../MCPClient/MCPClient.csproj | 5 +---- .../ModelContextProtocolPlugin.csproj | 5 +---- .../ModelContextProtocolPluginAuth.csproj | 5 +---- .../GettingStartedWithProcesses.csproj | 2 +- .../GettingStartedWithTextSearch.csproj | 5 +---- .../GettingStartedWithVectorStores.csproj | 5 +---- dotnet/src/IntegrationTests/IntegrationTests.csproj | 6 +----- .../src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj | 2 +- .../SemanticKernel.AotTests/SemanticKernel.AotTests.csproj | 5 +---- .../AzureAISearch.ConformanceTests.csproj | 5 +---- .../InMemory.ConformanceTests.csproj | 5 +---- .../Pinecone.ConformanceTests.csproj | 7 ++----- 12 files changed, 13 insertions(+), 44 deletions(-) diff --git a/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj b/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj index 90e4d0213d2f..01c7df63ab62 100644 --- a/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj +++ b/dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient/MCPClient.csproj @@ -13,10 +13,7 @@ - - - - + diff --git a/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj b/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj index ee1e6f2a366b..2a29456e277d 100644 --- a/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj +++ b/dotnet/samples/Demos/ModelContextProtocolPlugin/ModelContextProtocolPlugin.csproj @@ -14,10 +14,7 @@ - - - - + diff --git a/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj b/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj index d3294c4b8e8a..83c1269cc65b 100644 --- a/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj +++ b/dotnet/samples/Demos/ModelContextProtocolPluginAuth/ModelContextProtocolPluginAuth.csproj @@ -15,12 +15,9 @@ + - - - - diff --git a/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj b/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj index 913751f06cfd..0d59932a831e 100644 --- a/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj +++ b/dotnet/samples/GettingStartedWithProcesses/GettingStartedWithProcesses.csproj @@ -35,13 +35,13 @@ + - diff --git a/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj b/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj index c8a97a999af0..22d387acf592 100644 --- a/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj +++ b/dotnet/samples/GettingStartedWithTextSearch/GettingStartedWithTextSearch.csproj @@ -35,14 +35,11 @@ + - - - - diff --git a/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj b/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj index d81bfe9433f0..5c1fc5edb9cc 100644 --- a/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj +++ b/dotnet/samples/GettingStartedWithVectorStores/GettingStartedWithVectorStores.csproj @@ -33,14 +33,11 @@ + - - - - diff --git a/dotnet/src/IntegrationTests/IntegrationTests.csproj b/dotnet/src/IntegrationTests/IntegrationTests.csproj index 973ab1c49caa..25f2fe364b3b 100644 --- a/dotnet/src/IntegrationTests/IntegrationTests.csproj +++ b/dotnet/src/IntegrationTests/IntegrationTests.csproj @@ -54,6 +54,7 @@ + @@ -87,7 +88,6 @@ - @@ -96,10 +96,6 @@ - - - - Always diff --git a/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj b/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj index 2f394d320a3d..190a5f6eba33 100644 --- a/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj +++ b/dotnet/src/Plugins/Plugins.UnitTests/Plugins.UnitTests.csproj @@ -26,6 +26,7 @@ all + @@ -40,7 +41,6 @@ - diff --git a/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj b/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj index 58a7066fcab6..19454a297881 100644 --- a/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj +++ b/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj @@ -18,12 +18,9 @@ + - - - - diff --git a/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj b/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj index 0bb373f4a0b5..cf64171e6123 100644 --- a/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj +++ b/dotnet/test/VectorData/AzureAISearch.ConformanceTests/AzureAISearch.ConformanceTests.csproj @@ -22,6 +22,7 @@ + @@ -29,10 +30,6 @@ - - - - Always diff --git a/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj b/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj index f5abae8b0ece..cf188714fbb7 100644 --- a/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj +++ b/dotnet/test/VectorData/InMemory.ConformanceTests/InMemory.ConformanceTests.csproj @@ -16,10 +16,7 @@ all - - - - + diff --git a/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj b/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj index f9d6eeac9fac..eadf74cc2d71 100644 --- a/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj +++ b/dotnet/test/VectorData/Pinecone.ConformanceTests/Pinecone.ConformanceTests.csproj @@ -1,4 +1,4 @@ - + @@ -32,10 +32,7 @@ - - - - +